From 7414f1034d32c249294a081f1e176a9266fc92ac Mon Sep 17 00:00:00 2001
From: Vitaly Takmazov
Date: Thu, 8 Feb 2018 22:06:08 +0300
Subject: reorganize project structure
---
.../src/main/java/com/juick/api/ApiServer.java | 251 ----------------
.../main/java/com/juick/api/CrosspostManager.java | 165 -----------
.../src/main/java/com/juick/api/EmailManager.java | 110 -------
.../java/com/juick/api/FacebookPageManager.java | 32 ---
.../main/java/com/juick/api/MessengerManager.java | 143 ---------
.../java/com/juick/api/TelegramBotManager.java | 298 -------------------
.../src/main/java/com/juick/api/TopManager.java | 57 ----
.../main/java/com/juick/api/WebsocketManager.java | 163 -----------
.../api/configuration/ApiAppConfiguration.java | 135 ---------
.../juick/api/configuration/ApiInitializer.java | 65 -----
.../juick/api/configuration/ApiSecurityConfig.java | 128 ---------
.../api/configuration/ApiSecurityInitializer.java | 37 ---
.../com/juick/api/controllers/ApiController.java | 54 ----
.../java/com/juick/api/controllers/Messages.java | 181 ------------
.../juick/api/controllers/MessengerWebhook.java | 62 ----
.../com/juick/api/controllers/Notifications.java | 190 ------------
.../java/com/juick/api/controllers/Others.java | 66 -----
.../main/java/com/juick/api/controllers/PM.java | 127 --------
.../main/java/com/juick/api/controllers/Post.java | 281 ------------------
.../java/com/juick/api/controllers/Service.java | 74 -----
.../com/juick/api/controllers/SkypeEndpoint.java | 47 ---
.../main/java/com/juick/api/controllers/Tags.java | 54 ----
.../com/juick/api/controllers/TelegramWebhook.java | 48 ----
.../main/java/com/juick/api/controllers/Users.java | 125 --------
.../juick/api/controllers/util/JsonpAdvice.java | 31 --
.../java/com/juick/server/CrosspostManager.java | 165 +++++++++++
.../main/java/com/juick/server/EmailManager.java | 110 +++++++
.../java/com/juick/server/FacebookPageManager.java | 32 +++
.../java/com/juick/server/MessengerManager.java | 143 +++++++++
.../main/java/com/juick/server/ServerManager.java | 252 ++++++++++++++++
.../java/com/juick/server/TelegramBotManager.java | 298 +++++++++++++++++++
.../src/main/java/com/juick/server/TopManager.java | 57 ++++
.../java/com/juick/server/WebsocketManager.java | 163 +++++++++++
.../src/main/java/com/juick/server/api/Index.java | 54 ++++
.../main/java/com/juick/server/api/Messages.java | 180 ++++++++++++
.../java/com/juick/server/api/Notifications.java | 190 ++++++++++++
.../src/main/java/com/juick/server/api/PM.java | 148 ++++++++++
.../src/main/java/com/juick/server/api/Post.java | 281 ++++++++++++++++++
.../main/java/com/juick/server/api/Service.java | 74 +++++
.../src/main/java/com/juick/server/api/Tags.java | 54 ++++
.../src/main/java/com/juick/server/api/Users.java | 125 ++++++++
.../server/api/webhooks/MessengerWebhook.java | 62 ++++
.../juick/server/api/webhooks/SkypeWebhook.java | 47 +++
.../juick/server/api/webhooks/TelegramWebhook.java | 48 ++++
.../server/configuration/ApiAppConfiguration.java | 128 +++++++++
.../juick/server/configuration/ApiInitializer.java | 65 +++++
.../server/configuration/ApiSecurityConfig.java | 128 +++++++++
.../java/com/juick/server/util/JsonpAdvice.java | 31 ++
.../java/com/juick/api/tests/MessagesTests.java | 319 ---------------------
.../java/com/juick/server/tests/MessagesTests.java | 319 +++++++++++++++++++++
50 files changed, 3154 insertions(+), 3243 deletions(-)
delete mode 100644 juick-server/src/main/java/com/juick/api/ApiServer.java
delete mode 100644 juick-server/src/main/java/com/juick/api/CrosspostManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/EmailManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/FacebookPageManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/MessengerManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/TelegramBotManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/TopManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/WebsocketManager.java
delete mode 100644 juick-server/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java
delete mode 100644 juick-server/src/main/java/com/juick/api/configuration/ApiInitializer.java
delete mode 100644 juick-server/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java
delete mode 100644 juick-server/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/ApiController.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Messages.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/MessengerWebhook.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Notifications.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Others.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/PM.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Post.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Service.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/SkypeEndpoint.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Tags.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/TelegramWebhook.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/Users.java
delete mode 100644 juick-server/src/main/java/com/juick/api/controllers/util/JsonpAdvice.java
create mode 100644 juick-server/src/main/java/com/juick/server/CrosspostManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/EmailManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/FacebookPageManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/MessengerManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/ServerManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/TelegramBotManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/TopManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/WebsocketManager.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Index.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Messages.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Notifications.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/PM.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Post.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Service.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Tags.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/Users.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/webhooks/MessengerWebhook.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/webhooks/SkypeWebhook.java
create mode 100644 juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java
create mode 100644 juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
create mode 100644 juick-server/src/main/java/com/juick/server/configuration/ApiInitializer.java
create mode 100644 juick-server/src/main/java/com/juick/server/configuration/ApiSecurityConfig.java
create mode 100644 juick-server/src/main/java/com/juick/server/util/JsonpAdvice.java
delete mode 100644 juick-server/src/test/java/com/juick/api/tests/MessagesTests.java
create mode 100644 juick-server/src/test/java/com/juick/server/tests/MessagesTests.java
(limited to 'juick-server')
diff --git a/juick-server/src/main/java/com/juick/api/ApiServer.java b/juick-server/src/main/java/com/juick/api/ApiServer.java
deleted file mode 100644
index 51097440..00000000
--- a/juick-server/src/main/java/com/juick/api/ApiServer.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package com.juick.api;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.User;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.socket.TextMessage;
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.XmppException;
-import rocks.xmpp.core.session.Extension;
-import rocks.xmpp.core.session.XmppSessionConfiguration;
-import rocks.xmpp.core.session.debug.LogbackDebugger;
-import rocks.xmpp.core.stanza.model.Message;
-import rocks.xmpp.extensions.component.accept.ExternalComponent;
-import rocks.xmpp.extensions.oob.model.x.OobX;
-import rocks.xmpp.util.XmppUtils;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import javax.xml.bind.JAXBException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @author Ugnich Anton
- */
-public class ApiServer implements AutoCloseable {
- private static Logger logger = LoggerFactory.getLogger(ApiServer.class);
-
- private ExternalComponent xmpp;
-
- @Value("${xmpp_host:localhost}")
- private String xmppHost;
- @Value("${xmpp_password:secret}")
- private String xmppPassword;
- @Value("${ws_jid:ws.localhost}")
- private String xmppJid;
- @Value("${xmpp_port:5347}")
- private int xmppPort;
- @Value("${xmpp_disabled:false}")
- private boolean isXmppDisabled;
- @Inject
- private ObjectMapper jsonMapper;
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private WebsocketManager wsHandler;
- @Value("${service_user:juick}")
- private String serviceUser;
-
- @PostConstruct
- public void init() {
- if (!isXmppDisabled) {
- setupXmppComponent(xmppHost, xmppPort, xmppJid, xmppPassword);
- } else {
- logger.warn("XMPP is not enabled");
- }
- }
-
- @Override
- public void close() {
- try {
- if (xmpp != null)
- xmpp.close();
-
- logger.info("ExternalComponent on juick-api destroyed");
- } catch (Exception e) {
- logger.warn("Exception occurs on juick-api destroy", e);
- }
- }
-
- public void setupXmppComponent(final String host, final int port, final String jid, final String password) {
- XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
- .extensions(Extension.of(com.juick.Message.class))
- .build();
- xmpp = ExternalComponent.create(jid, password, configuration, host, port);
- xmpp.addInboundMessageListener(e -> {
- try {
- Message msg = e.getMessage();
- com.juick.Message jmsg = msg.getExtension(com.juick.Message.class);
- if (jmsg != null) {
- if (logger.isInfoEnabled()) { // prevent writeValueAsString execution if log is disabled
- try {
- StringWriter stanzaWriter = new StringWriter();
- XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter(
- xmpp.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter));
- xmpp.createMarshaller().marshal(msg, xmppStreamWriter);
- xmppStreamWriter.flush();
- xmppStreamWriter.close();
- logger.info("got msg: {}", stanzaWriter.toString());
- } catch (XMLStreamException e1) {
- logger.info("jaxb exception", e1);
- }
-
- }
- if (jmsg.getMid() == 0) {
- int uid_to = NumberUtils.toInt(msg.getTo().getLocal(), 0);
- if (uid_to > 0) {
- onJuickPM(uid_to, jmsg);
- }
- } else if (jmsg.getRid() == 0) {
- // to get full message with attachment, etc.
- onJuickMessagePost(messagesService.getMessage(jmsg.getMid()));
- } else {
- // to get quote and attachment
- com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
- onJuickMessageReply(reply);
- }
- }
- } catch (JsonProcessingException ex) {
- logger.error("mapper exception", ex);
- } catch (JAXBException exc) {
- logger.error("jaxb exception", exc);
- }
- });
- try {
- xmpp.connect();
- } catch (XmppException e) {
- logger.warn("xmpp extension", e);
- }
- }
-
- public void sendMessage(Message message) {
- if (!isXmppDisabled) {
- xmpp.sendMessage(message);
- }
- }
-
- public void processMessage(User visitor, String body, String attachmentName) {
- Message xmsg = new Message();
- xmsg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "perl"));
- xmsg.setTo(Jid.of("juick@juick.com/Juick"));
- xmsg.setBody(body);
- try {
- if (StringUtils.isNotEmpty(attachmentName)) {
- String attachmentUrl = String.format("juick://%s", attachmentName);
- xmsg.addExtension(new OobX(new URI(attachmentUrl), "!!!!Juick!!"));
- }
- sendMessage(xmsg);
- } catch (URISyntaxException e1) {
- logger.warn("attachment error", e1);
- }
- }
-
- private void onJuickPM(final int uid_to, final com.juick.Message jmsg) throws JsonProcessingException {
- String json = jsonMapper.writeValueAsString(jmsg);
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.getUid() == uid_to) || c.visitor.getName().equals(serviceUser))
- .forEach(c -> {
- try {
- logger.info("sending pm to {}", c.visitor.getUid());
- c.session.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- }
-
- private void onJuickMessagePost(final com.juick.Message jmsg) throws JsonProcessingException {
- String json = jsonMapper.writeValueAsString(jmsg);
- List uids = subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid())
- .stream().map(User::getUid).collect(Collectors.toList());
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.getUid() == 0) // anonymous users
- || c.visitor.getName().equals(serviceUser) // services
- || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions
- .forEach(c -> {
- try {
- logger.info("sending message to {}", c.visitor.getUid());
- c.session.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- wsHandler.getClients().stream().filter(c ->
- c.legacy && c.allMessages) // legacy all posts
- .forEach(c -> {
- try {
- logger.info("sending message to legacy client {}", c.visitor.getUid());
- c.session.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- }
-
- private void onJuickMessageReply(final com.juick.Message jmsg) throws JsonProcessingException {
- String json = jsonMapper.writeValueAsString(jmsg);
- List threadUsers =
- subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid())
- .stream().map(User::getUid).collect(Collectors.toList());
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.getUid() == 0) // anonymous users
- || c.visitor.getName().equals(serviceUser) // services
- || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions
- .forEach(c -> {
- try {
- logger.info("sending reply to {}", c.visitor.getUid());
- c.session.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- wsHandler.getClients().stream().filter(c ->
- (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies
- .forEach(c -> {
- try {
- logger.info("sending reply to legacy client {}", c.visitor.getUid());
- c.session.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/CrosspostManager.java b/juick-server/src/main/java/com/juick/api/CrosspostManager.java
deleted file mode 100644
index a601d7b7..00000000
--- a/juick-server/src/main/java/com/juick/api/CrosspostManager.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package com.juick.api;
-
-import com.juick.Message;
-import com.juick.server.component.MessageEvent;
-import com.juick.service.CrosspostService;
-import com.juick.util.MessageUtils;
-import org.apache.commons.codec.CharEncoding;
-import org.apache.commons.io.IOUtils;
-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.ApplicationListener;
-import org.springframework.social.twitter.api.Twitter;
-import org.springframework.social.twitter.api.impl.TwitterTemplate;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import javax.net.ssl.HttpsURLConnection;
-import java.io.OutputStreamWriter;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-
-/**
- * @author Ugnich Anton
- */
-@Component
-public class CrosspostManager implements ApplicationListener {
- private final static String FBURL = "https://graph.facebook.com/me/feed";
- private final static String VKURL = "https://api.vk.com/method/wall.post";
-
- private static Logger logger = LoggerFactory.getLogger(CrosspostManager.class);
-
- @Inject
- private CrosspostService crosspostService;
-
- @Value("${twitter_consumer_key:}")
- private String twitter_consumer_key;
- @Value("${twitter_consumer_secret:}")
- private String twitter_consumer_secret;
-
- @Override
- public void onApplicationEvent(@Nonnull MessageEvent event) {
- Message msg = event.getMessage();
- if (msg.getMid() > 0 && msg.getRid() == 0) {
- if (StringUtils.isNotEmpty(crosspostService.getTwitterName(msg.getUser().getUid()))) {
- if (msg.getTags().stream().noneMatch(t -> t.getName().equals("notwitter"))) {
- twitterPost(msg);
- }
- }
- // TODO: approve application for facebook crosspost
- }
- }
-
- private boolean facebookPost(final com.juick.Message jmsg) {
- String token = crosspostService.getFacebookTokens(jmsg.getUser().getUid())
- .orElse(Pair.of(StringUtils.EMPTY, StringUtils.EMPTY)).getRight();
- if (token.isEmpty()) {
- return false;
- }
-
- logger.info("FB: #{}", jmsg.getMid());
-
- String status = MessageUtils.getMessageHashTags(jmsg) + "\n" + jmsg.getText();
-
- boolean ret = false;
- try {
- String body = "access_token="
- + URLEncoder.encode(token, CharEncoding.UTF_8)
- + "&message="
- + URLEncoder.encode(status, CharEncoding.UTF_8)
- + "&link=http%3A%2F%2Fjuick.com%2F"
- + jmsg.getMid();
-
- HttpsURLConnection conn = (HttpsURLConnection) new URL(FBURL).openConnection();
- conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- conn.setRequestProperty("User-Agent", "Juick");
- conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
- conn.setUseCaches(false);
- conn.setDoInput(true);
- conn.setDoOutput(true);
- conn.setRequestMethod("POST");
- conn.connect();
-
- OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
- wr.write(body);
- wr.close();
-
- ret = StringUtils.isNotEmpty(IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8));
-
- conn.disconnect();
- } catch (Exception e) {
- logger.error("fbPost exception", e);
- }
- return ret;
- }
-
- private boolean vkontaktePost(final com.juick.Message jmsg) {
- Pair tokens = crosspostService.getVkTokens(jmsg.getUser().getUid()).orElse(Pair.of(StringUtils.EMPTY, StringUtils.EMPTY));
- if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) {
- return false;
- }
-
- logger.info("VK: #", jmsg.getMid());
-
- String status = MessageUtils.getMessageHashTags(jmsg) + "\n" + jmsg.getText() + "\nhttp://juick.com/" + jmsg.getMid();
-
- boolean ret = false;
- try {
- String body = "owner_id=" + tokens.getLeft() + "&access_token=" + URLEncoder.encode(tokens.getRight(), CharEncoding.UTF_8) + "&from_group=1&message=" + URLEncoder.encode(status, CharEncoding.UTF_8);
-
- HttpsURLConnection conn = (HttpsURLConnection) new URL(VKURL).openConnection();
- conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- conn.setRequestProperty("User-Agent", "Juick");
- conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
- conn.setUseCaches(false);
- conn.setDoInput(true);
- conn.setDoOutput(true);
- conn.setRequestMethod("POST");
- conn.connect();
-
- OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
- wr.write(body);
- wr.close();
-
- ret = StringUtils.isNotEmpty(IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8));
-
- conn.disconnect();
- } catch (Exception e) {
- logger.error("vkPost exception", e);
- }
- return ret;
- }
-
- private void twitterPost(final com.juick.Message jmsg) {
- crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> {
- String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
- if (status.length() > 255) {
- status = status.substring(0, 254) + "…";
- }
- status += " http://juick.com/" + jmsg.getMid();
- Twitter twitter = new TwitterTemplate(twitter_consumer_key, twitter_consumer_secret, t.getToken(), t.getSecret());
- twitter.timelineOperations().updateStatus(status);
- });
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/EmailManager.java b/juick-server/src/main/java/com/juick/api/EmailManager.java
deleted file mode 100644
index fa86b406..00000000
--- a/juick-server/src/main/java/com/juick/api/EmailManager.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.juick.api;
-
-import com.juick.Message;
-import com.juick.server.component.MessageEvent;
-import com.juick.service.EmailService;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-import com.juick.util.MessageUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationListener;
-import org.springframework.stereotype.Component;
-
-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.Properties;
-
-import static com.juick.formatters.PlainTextFormatter.formatPost;
-import static com.juick.formatters.PlainTextFormatter.formatUrl;
-
-@Component
-public class EmailManager implements ApplicationListener {
-
- public static final String MSGID_PATTERN = "\\.|@|<";
-
- private static final Logger logger = LoggerFactory.getLogger(EmailManager.class);
- @Inject
- private EmailService emailService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Override
- public void onApplicationEvent(@Nonnull MessageEvent event) {
- Message msg = event.getMessage();
- if (msg.getMid() > 0 && msg.getRid() == 0) {
- String subject = String.format("New message from %s", msg.getUser().getName());
- subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg.getMid())
- .forEach(user -> emailService.getEmails(user.getUid(), true)
- .forEach(email -> emailNotify(email, subject, msg)));
- } else if (msg.getRid() > 0) {
- Message originalMessage = messagesService.getMessage(msg.getMid());
- String subject = String.format("New reply to %s", originalMessage.getUser().getName());
- subscriptionService.getUsersSubscribedToComments(msg.getMid(), msg.getUser().getUid())
- .forEach(user -> emailService.getEmails(user.getUid(), true)
- .forEach(email -> emailNotify(email, subject, msg)));
- }
- }
-
- private void emailNotify(String email, String subject, Message msg) {
- 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 {
- setHeader("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid()));
- if (msg.getRid() > 0) {
- if (msg.getReplyto() > 0) {
- Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto());
- setHeader("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid()));
- } else {
- Message original = messagesService.getMessage(msg.getMid());
- setHeader("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid()));
- }
- }
- }
- };
- message.setFrom(new InternetAddress("juick@juick.com"));
- message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(email));
- message.setSubject(subject);
- String plainText = String.format("%s\n\n--\nYou are receiving this because you are subscribed to this user " +
- ", discussion or tag. Reply to this email directly or view it on Juick: %s.",
- formatPost(msg), formatUrl(msg));
- MimeBodyPart textBodyPart = new MimeBodyPart();
- textBodyPart.setContent(plainText, "text/plain; charset=UTF-8");
- String htmlText = String.format("%s
--
You are receiving this because you are subscribed to this user" +
- ", discussion or tag. Reply to this email directly or view it on Juick." +
- "
Configure or disable notifications",
- MessageUtils.formatHtml(msg), formatUrl(msg),
- userService.getHashByUID(userService.getUserByEmail(email).getUid()));
- MimeBodyPart htmlBodyPart = new MimeBodyPart();
- htmlBodyPart.setContent(htmlText, "text/html; charset=UTF-8");
- Multipart multipart = new MimeMultipart("alternative");
- multipart.addBodyPart(textBodyPart);
- multipart.addBodyPart(htmlBodyPart);
- message.setContent(multipart);
- message.setHeader("List-Unsubscribe", String.format("https://juick.com/settings?hash=%s",
- userService.getHashByUID(userService.getUserByEmail(email).getUid())));
- message.saveChanges();
- transport.connect();
- transport.sendMessage(message, message.getAllRecipients());
- } catch (MessagingException ex) {
- logger.error("mail exception", ex);
- }
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/FacebookPageManager.java b/juick-server/src/main/java/com/juick/api/FacebookPageManager.java
deleted file mode 100644
index e9ed0e59..00000000
--- a/juick-server/src/main/java/com/juick/api/FacebookPageManager.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.juick.api;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-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.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-@Component
-public class FacebookPageManager {
- private static Logger logger = LoggerFactory.getLogger(FacebookPageManager.class);
- @Value("${facebook_page_id:12345678}")
- private String pageId;
- @Value("${fb_page_access_token:12345678}")
- private String accessToken;
-
- public void post(String status, String link) {
- UriComponents uriComponents = UriComponentsBuilder.fromUriString("https://graph.facebook.com/{page_id}/feed")
- .queryParam("message", status)
- .queryParam("link", link)
- .queryParam("access_token", accessToken)
- .buildAndExpand(pageId);
- RestTemplate api = new RestTemplate();
- ResponseEntity response = api.exchange(uriComponents.toUri(),
- HttpMethod.POST, null, String.class);
- logger.info("Facebook response: {}", response.getBody());
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/MessengerManager.java b/juick-server/src/main/java/com/juick/api/MessengerManager.java
deleted file mode 100644
index 7341b946..00000000
--- a/juick-server/src/main/java/com/juick/api/MessengerManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.juick.api;
-
-import com.github.messenger4j.Messenger;
-import com.github.messenger4j.exception.MessengerApiException;
-import com.github.messenger4j.exception.MessengerIOException;
-import com.github.messenger4j.exception.MessengerVerificationException;
-import com.github.messenger4j.send.MessagePayload;
-import com.github.messenger4j.send.message.TemplateMessage;
-import com.github.messenger4j.send.message.TextMessage;
-import com.github.messenger4j.send.message.template.ButtonTemplate;
-import com.github.messenger4j.send.message.template.button.UrlButton;
-import com.github.messenger4j.userprofile.UserProfile;
-import com.github.messenger4j.webhook.event.TextMessageEvent;
-import com.juick.Message;
-import com.juick.User;
-import com.juick.server.component.MessageEvent;
-import com.juick.service.MessagesService;
-import com.juick.service.MessengerService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationListener;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-
-import javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Optional;
-
-import static com.juick.formatters.PlainTextFormatter.formatPost;
-import static com.juick.formatters.PlainTextFormatter.formatUrl;
-
-@Component
-public class MessengerManager implements ApplicationListener {
- private static final Logger logger = LoggerFactory.getLogger(MessengerManager.class);
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private UserService userService;
- @Inject
- private MessengerService messengerService;
- @Inject
- private ApiServer apiServer;
-
- @Value("${fb_page_access_token:12345678}")
- private String facebookPageAccessToken;
- @Value("${fb_verify_token:12345678}")
- private String facebookVerifyToken;
- @Value("${fb_secret:12345678}")
- private String facebookSecret;
-
- private Messenger messenger;
-
- @PostConstruct
- public void init() {
- messenger = Messenger.create(facebookPageAccessToken, facebookSecret, facebookVerifyToken);
- }
-
- public String getFacebookVerifyToken() {
- return facebookVerifyToken;
- }
-
- public void processUpdate(String signature , String data) throws MessengerVerificationException {
- messenger.onReceiveEvents(data, Optional.of(signature), event -> {
- final String senderId = event.senderId();
- final Instant timestamp = event.timestamp();
-
- User user_from = userService.getUserByUID(messengerService.getUserId(senderId)).orElse(new User());
- logger.info("Found juick user {}", user_from.getUid());
- if (user_from.getUid() == 0) {
- try {
- UserProfile profile = messenger.queryUserProfile(senderId);
- signupNotify(senderId, messengerService.getSignUpHash(senderId, profile.firstName()));
- } catch (MessengerApiException | MessengerIOException | MalformedURLException e) {
- logger.warn("messenger profile error", e);
- try {
- signupNotify(senderId, messengerService.getSignUpHash(senderId, "anonymous"));
- } catch (MalformedURLException | MessengerApiException | MessengerIOException e1) {
- logger.warn("signup error", e1);
- }
- }
- } else {
- if (event.isTextMessageEvent()) {
- final TextMessageEvent textMessageEvent = event.asTextMessageEvent();
- final String messageId = textMessageEvent.messageId();
- final String text = textMessageEvent.text();
- logger.info("Received text message from '{}' at '{}' with content: {} (mid: {})",
- senderId, timestamp, text, messageId);
- apiServer.processMessage(user_from, text, null);
- messengerNotify(senderId, "Message sent", null);
- }
- }
- });
- }
-
- @Override
- public void onApplicationEvent(@Nonnull MessageEvent event) {
- Message msg = event.getMessage();
- if (msg.getMid() > 0 && msg.getRid() == 0) {
- String subject = formatPost(msg);
- subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg.getMid())
- .forEach(user -> messengerService.getSenderId(user)
- .ifPresent(t -> messengerNotify(t, subject, formatUrl(msg))));
- } else if (msg.getRid() > 0) {
- // get quote
- com.juick.Message jmsg = messagesService.getReply(msg.getMid(), msg.getRid());
- String subject = formatPost(jmsg);
- subscriptionService.getUsersSubscribedToComments(msg.getMid(), msg.getUser().getUid())
- .forEach(user -> messengerService.getSenderId(user)
- .ifPresent(t -> messengerNotify(t, subject, formatUrl(jmsg))));
- }
- }
-
- private void messengerNotify(String messengerUser, String text, String url) {
- try {
- if (!StringUtils.isEmpty(url)) {
- final UrlButton showMessage = UrlButton.create("VIEW MESSAGE", new URL(url));
- ButtonTemplate template = ButtonTemplate.create(text, Collections.singletonList(showMessage));
- messenger.send(MessagePayload.create(messengerUser, TemplateMessage.create(template)));
- } else {
- messenger.send(MessagePayload.create(messengerUser, TextMessage.create(text)));
- }
- } catch (MessengerApiException | MessengerIOException | MalformedURLException e) {
- logger.warn("messenger error", e);
- }
- }
- private void signupNotify(String messengerUser, String hash) throws MalformedURLException, MessengerApiException, MessengerIOException {
- final UrlButton urlButton = UrlButton.create("LOGIN",
- new URL("https://juick.com/signup?type=messenger&hash=" + hash));
- ButtonTemplate template = ButtonTemplate.create("Login to receive notifications",
- Collections.singletonList(urlButton));
- messenger.send(MessagePayload.create(messengerUser, TemplateMessage.create(template)));
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/TelegramBotManager.java b/juick-server/src/main/java/com/juick/api/TelegramBotManager.java
deleted file mode 100644
index 6f2e50d1..00000000
--- a/juick-server/src/main/java/com/juick/api/TelegramBotManager.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api;
-
-import com.juick.User;
-import com.juick.server.component.MessageEvent;
-import com.juick.server.util.HttpUtils;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.TelegramService;
-import com.juick.service.UserService;
-import com.pengrad.telegrambot.BotUtils;
-import com.pengrad.telegrambot.Callback;
-import com.pengrad.telegrambot.TelegramBot;
-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.SetWebhook;
-import com.pengrad.telegrambot.response.GetFileResponse;
-import com.pengrad.telegrambot.response.SendResponse;
-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.ApplicationListener;
-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.net.URL;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Optional;
-
-import static com.juick.formatters.PlainTextFormatter.formatPost;
-import static com.juick.formatters.PlainTextFormatter.formatUrl;
-
-/**
- * Created by vt on 12/05/16.
- */
-@Component
-public class TelegramBotManager implements ApplicationListener {
- private static final Logger logger = LoggerFactory.getLogger(TelegramBotManager.class);
-
- private TelegramBot bot;
-
- @Value("${telegram_token}")
- private String telegramToken;
- @Inject
- private TelegramService telegramService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private UserService userService;
- @Inject
- private ApiServer apiServer;
- @Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}")
- private String tmpDir;
-
- private static final String MSG_LINK = "🔗";
-
- @PostConstruct
- public void init() {
- if (StringUtils.isBlank(telegramToken)) {
- logger.info("telegram token is not set, exiting");
- return;
- }
- bot = new TelegramBot(telegramToken);
- try {
- SetWebhook webhook = new SetWebhook().url("https://api.juick.com/tlgmbtwbhk");
- if (!bot.execute(webhook).isOk()) {
- logger.error("error setting webhook");
- }
- } catch (Exception e) {
- logger.warn("couldn't initialize telegram bot", e);
- }
- }
-
- public void processUpdate(String data) throws Exception {
- Update update = BotUtils.parseUpdate(data);
- 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(new User());
- logger.info("Found juick user {}", user_from.getUid());
-
- List chats = telegramService.getChats();
- String username = message.from().username();
- if (username == null) {
- username = message.from().firstName();
- }
- if (!chats.contains(message.chat().id())) {
- telegramService.addChat(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));
- } else {
- if (user_from.getUid() == 0) {
- telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username));
- } else {
- String attachment = 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());
- URL fileURL = new URL(bot.getFullFilePath(response.file()));
- attachment = HttpUtils.downloadImage(fileURL, tmpDir);
- 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)) {
- if (text.equalsIgnoreCase("LOGIN")
- || text.equalsIgnoreCase("PING")
- || text.equalsIgnoreCase("HELP")
- || text.equalsIgnoreCase("/login")
- || 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);
- } else {
- Message replyMessage = message.replyToMessage();
- if (replyMessage != null) {
- if (replyMessage.entities() != null) {
- Optional juickLink = Arrays.stream(replyMessage.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 = Integer.valueOf(path.substring(1));
- String prefix = String.format("#%d ", mid);
- if (StringUtils.isNotEmpty(uriComponents.getFragment())) {
- int rid = Integer.valueOf(uriComponents.getFragment());
- prefix = String.format("#%d/%d ", mid, rid);
- }
- apiServer.processMessage(user_from, prefix + text, attachment);
- telegramNotify(message.from().id().longValue(), "Reply sent");
- } 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());
- }
- } else {
- telegramNotify(message.from().id().longValue(),
- "Can not reply to this message", replyMessage.messageId());
- }
- } else {
- apiServer.processMessage(user_from, text, attachment);
- telegramNotify(message.from().id().longValue(), "Message sent");
- }
- }
- }
- }
- }
- }
-
- private boolean isJuickLink(MessageEntity e) {
- return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2;
- }
-
- @Override
- public void onApplicationEvent(@Nonnull MessageEvent event) {
- com.juick.Message jmsg = event.getMessage();
- String msgUrl = formatUrl(jmsg);
- if (jmsg.getMid() > 0 && jmsg.getRid() == 0) {
- String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true));
-
- List users = telegramService.getTelegramIdentifiers(subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid()));
- List chats = telegramService.getChats();
- // registered subscribed users
-
- users.forEach(c -> telegramNotify(c, msg));
- // anonymous
- chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg));
- } else if (jmsg.getRid() > 0) {
- // get quote
- com.juick.Message msg = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
- String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(msg, true));
- telegramService.getTelegramIdentifiers(
- subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid())
- ).forEach(c -> telegramNotify(c, fmsg));
- }
- }
-
- public void telegramNotify(Long chatId, String msg) {
- telegramNotify(chatId, msg, 0);
- }
-
- public void telegramNotify(Long chatId, String msg, Integer replyTo) {
- 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) {
- if (!response.isOk()) {
- if (response.errorCode() == 403) {
- // remove from anonymous chat
- telegramService.getChats().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent(
- d -> {
- telegramService.deleteChat(d);
- logger.info("deleted {} chat", d);
- }
- );
- int userId = telegramService.getUser(chatId);
- if (userId > 0) {
- User userToDelete = userService.getUserByUID(userId)
- .orElse(new User());
- boolean status = telegramService.deleteTelegramUser(userToDelete.getUid());
- logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status);
- boolean chatStatus = telegramService.deleteChat(chatId);
- logger.info("deleting telegram chat {} : {}", chatId, chatStatus);
- }
- } else {
- logger.warn("error response, isOk: {}, errorCode: {}, description: {}",
- response.isOk(), response.errorCode(), response.description());
- }
- }
- }
-
- @Override
- public void onFailure(SendMessage request, IOException e) {
- logger.warn("telegram failure", e);
- }
- });
- }
-
- 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);
- }
- });
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/TopManager.java b/juick-server/src/main/java/com/juick/api/TopManager.java
deleted file mode 100644
index b8123639..00000000
--- a/juick-server/src/main/java/com/juick/api/TopManager.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api;
-
-import com.juick.Message;
-import com.juick.service.MessagesService;
-import com.juick.util.MessageUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.HttpClientErrorException;
-
-import javax.inject.Inject;
-
-@Component
-public class TopManager {
- private static Logger logger = LoggerFactory.getLogger(TopManager.class);
- @Inject
- private MessagesService messagesService;
- @Inject
- private FacebookPageManager facebookPageManager;
-
- @Scheduled(fixedRate = 3600000)
- public void updateTop() {
- messagesService.getPopularCandidates().forEach(m -> {
- logger.info("added {} to popular", m);
- messagesService.setMessagePopular(m, 1);
- Message jmsg = messagesService.getMessage(m);
- String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
- try {
- facebookPageManager.post(status, "https://juick.com/" + jmsg.getMid());
- } catch (HttpClientErrorException ex) {
- HttpStatus statusCode = ex.getStatusCode();
- String responseString = ex.getResponseBodyAsString();
- logger.warn("facebook error {}: {}", statusCode.value(), responseString);
- }
- });
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/WebsocketManager.java b/juick-server/src/main/java/com/juick/api/WebsocketManager.java
deleted file mode 100644
index 2ed70b3c..00000000
--- a/juick-server/src/main/java/com/juick/api/WebsocketManager.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api;
-
-import com.juick.User;
-import com.juick.server.helpers.AnonymousUser;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpHeaders;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.web.socket.CloseStatus;
-import org.springframework.web.socket.PingMessage;
-import org.springframework.web.socket.WebSocketSession;
-import org.springframework.web.socket.handler.TextWebSocketHandler;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Created by vitalyster on 28.06.2016.
- */
-public class WebsocketManager extends TextWebSocketHandler {
- private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class);
-
- private final List clients = Collections.synchronizedList(new LinkedList<>());
-
- @Inject
- private UserService userService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
-
-
- @Override
- public void afterConnectionEstablished(WebSocketSession session) throws Exception {
- URI hLocation;
- String hXRealIP;
-
- hLocation = session.getUri();
- HttpHeaders headers = session.getHandshakeHeaders();
- hXRealIP = headers.getOrDefault("X-Real-IP",
- Collections.singletonList(session.getRemoteAddress().toString())).get(0);
-
- // Auth
- User visitor = AnonymousUser.INSTANCE;
- UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build();
- List hash = uriComponents.getQueryParams().get("hash");
- if (hash != null && hash.get(0).length() == 16) {
- visitor = userService.getUserByHash(hash.get(0));
- } else {
- logger.debug("wrong hash for {} from {}", visitor.getUid(), hXRealIP);
- }
-
- int MID = 0;
- SocketSubscribed sockSubscr = null;
- if (hLocation.getPath().equals("/ws/")) {
- logger.debug("user {} connected", visitor.getUid());
- sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false);
- } else if (hLocation.getPath().equals("/ws/_all")) {
- logger.debug("user {} connected to legacy _all ({})", visitor.getUid(), hLocation.getPath());
- sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
- sockSubscr.allMessages = true;
- } else if (hLocation.getPath().equals("/ws/_replies")) {
- logger.debug("user {} connected to legacy _replies ({})", visitor.getUid(), hLocation.getPath());
- sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
- sockSubscr.allReplies = true;
- } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) {
- MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0);
- if (MID > 0) {
- if (messagesService.canViewThread(MID, visitor.getUid())) {
- logger.debug("user {} connected to legacy thread ({}) from {}", visitor.getUid(), MID, hXRealIP);
- sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
- sockSubscr.MID = MID;
- } else {
- throw new HttpForbiddenException();
- }
- }
- } else {
- throw new HttpNotFoundException();
- }
- if (sockSubscr != null) {
- synchronized (clients) {
- clients.add(sockSubscr);
- logger.debug("{} clients connected", clients.size());
- }
- } else {
- throw new HttpBadRequestException();
- }
- }
-
- @Override
- public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
- synchronized (clients) {
- logger.debug("session closed with status {}: {}", status.getCode(), status.getReason());
- clients.removeIf(c -> c.session.getId().equals(session.getId()));
- logger.debug("{} clients connected", clients.size());
- }
-
- }
-
- @Scheduled(fixedRate = 30000)
- public void ping() {
- clients.forEach(c -> {
- try {
- c.session.sendMessage(new PingMessage());
- } catch (IOException e) {
- logger.error("WebSocket PING exception", e);
- }
- });
- }
- public List getClients() {
- return clients;
- }
-
- class SocketSubscribed {
- WebSocketSession session;
- String clientName;
- User visitor;
- int MID;
- boolean allMessages;
- boolean allReplies;
- long tsConnected;
- long tsLastData;
- boolean legacy;
-
- public SocketSubscribed(WebSocketSession session, String clientName, User visitor, boolean legacy) {
- this.session = session;
- this.clientName = clientName;
- this.visitor = visitor;
- tsConnected = tsLastData = System.currentTimeMillis();
- this.legacy = legacy;
- }
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java b/juick-server/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java
deleted file mode 100644
index ea59ac52..00000000
--- a/juick-server/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.configuration;
-
-import com.juick.api.ApiServer;
-import com.juick.api.WebsocketManager;
-import com.juick.server.component.JuickServerComponent;
-import com.juick.server.component.JuickServerReconnectManager;
-import com.juick.server.configuration.BaseWebConfiguration;
-import com.juick.service.UserService;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.*;
-import org.springframework.core.Ordered;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.socket.client.WebSocketConnectionManager;
-import org.springframework.web.socket.client.standard.StandardWebSocketClient;
-import org.springframework.web.socket.config.annotation.EnableWebSocket;
-import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry;
-import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
-import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
-import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
-import org.springframework.web.util.UriComponentsBuilder;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.service.ApiInfo;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import java.util.Collections;
-
-/**
- * Created by aalexeev on 11/12/16.
- */
-@Configuration
-@EnableAsync
-@EnableWebMvc
-@EnableSwagger2
-@EnableScheduling
-@EnableWebSocket
-@PropertySource("classpath:juick.conf")
-@ComponentScan(basePackages = "com.juick")
-public class ApiAppConfiguration extends BaseWebConfiguration implements WebSocketConfigurer {
- @Inject
- UserService userService;
- @Value("${api_user:juick}")
- private String serviceUser;
- @Value("${websocket_url:ws://localhost:8080/ws/}")
- private String baseUri;
- @Lazy
- @Bean
- public JuickServerComponent juickServerComponent() {
- return new JuickServerComponent();
- }
- @Lazy
- @Bean
- public JuickServerReconnectManager juickServerReconnectManager() {
- return new JuickServerReconnectManager();
- }
- @Bean
- public WebSocketConnectionManager connectionManager() {
- String websocketURI = UriComponentsBuilder.fromUriString(baseUri)
- .queryParam("hash", userService.getHashByUID(userService.getUIDbyName(serviceUser))).build().toUriString();
- return new WebSocketConnectionManager(client(), juickServerComponent(), websocketURI);
- }
- @Bean
- public StandardWebSocketClient client() {
- return new StandardWebSocketClient();
- }
- @Bean
- public ApiServer apiServer() {
- return new ApiServer();
- }
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .select()
- .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.any()).build().apiInfo(new ApiInfo("Juick API", "Juick REST API Documentation",
- "2.0", "https://juick.com/help/tos", null,
- "AGPLv3", "https://www.gnu.org/licenses/agpl-3.0.html", Collections.emptyList()));
- }
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("/swagger-ui.html")
- .addResourceLocations("classpath:/META-INF/resources/");
-
- registry.addResourceHandler("/webjars/**")
- .addResourceLocations("classpath:/META-INF/resources/webjars/");
- }
- @Bean
- public WebsocketManager wsHandler() {
- return new WebsocketManager();
- }
-
- @Override
- public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) {
- //((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.LOWEST_PRECEDENCE);
- registry.addHandler(wsHandler(), "/ws/**").setAllowedOrigins("*");
- }
-
- @Bean
- public ServletServerContainerFactoryBean createWebSocketContainer() {
- ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
- container.setMaxTextMessageBufferSize(8192);
- container.setMaxBinaryMessageBufferSize(8192);
- return container;
- }
- @Override
- public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
- configurer.enable();
- }
-
-}
diff --git a/juick-server/src/main/java/com/juick/api/configuration/ApiInitializer.java b/juick-server/src/main/java/com/juick/api/configuration/ApiInitializer.java
deleted file mode 100644
index 8f35e3f7..00000000
--- a/juick-server/src/main/java/com/juick/api/configuration/ApiInitializer.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.configuration;
-
-import com.juick.configuration.DataConfiguration;
-import com.juick.server.configuration.JuickServerWebsocketConfiguration;
-import com.juick.server.configuration.StorageConfiguration;
-import org.apache.commons.codec.CharEncoding;
-import org.springframework.web.filter.CharacterEncodingFilter;
-import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
-
-import javax.annotation.Nonnull;
-import javax.servlet.Filter;
-
-/**
- * Created by vt on 09/02/16.
- */
-public class ApiInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
-
- @Override
- protected Class>[] getRootConfigClasses() {
- return new Class>[]{
- ApiSecurityConfig.class,
- DataConfiguration.class,
- StorageConfiguration.class
- };
- }
-
- @Override
- protected Class>[] getServletConfigClasses() {
- return null;
- }
-
- @Override
- @Nonnull
- protected String[] getServletMappings() {
- return new String[]{"/"};
- }
-
- @Override
- protected Filter[] getServletFilters() {
- return new Filter[]{new CharacterEncodingFilter(CharEncoding.UTF_8)};
- }
-
- @Override
- @Nonnull
- protected String getServletName() {
- return "API dispatcher servlet";
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java b/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java
deleted file mode 100644
index 2ae1d9e5..00000000
--- a/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityConfig.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.configuration;
-
-import com.juick.service.UserService;
-import com.juick.service.security.JuickUserDetailsService;
-import com.juick.service.security.NotAuthorizedAuthenticationEntryPoint;
-import com.juick.service.security.deprecated.RequestParamHashRememberMeServices;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.http.HttpMethod;
-import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
-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.web.authentication.RememberMeServices;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.CorsConfigurationSource;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-
-import javax.inject.Inject;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Created by aalexeev on 11/21/16.
- */
-@Configuration
-@EnableWebSecurity
-@PropertySource("classpath:juick.conf")
-@Import(ApiAppConfiguration.class)
-public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
- @Value("${auth_remember_me_key}")
- private String rememberMeKey;
- @Inject
- private UserService userService;
-
- ApiSecurityConfig() {
- super(true);
- }
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers(HttpMethod.OPTIONS).permitAll()
- .antMatchers("/messages", "/users", "/thread", "/tags", "/tlgmbtwbhk", "/fbwbhk",
- "/skypebotendpoint").permitAll()
- .anyRequest().hasRole("USER")
- .and().httpBasic().authenticationEntryPoint(getJuickAuthenticationEntryPoint())
- .and().anonymous()
- .and().cors().configurationSource(corsConfigurationSource())
- .and().servletApi()
- .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and().exceptionHandling().authenticationEntryPoint(getJuickAuthenticationEntryPoint())
- .and()
- .rememberMe()
- .alwaysRemember(true)
- .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30))
- .rememberMeServices(rememberMeServices())
- .key(rememberMeKey)
- .and().authenticationProvider(authenticationProvider())
- .headers().defaultsDisabled().cacheControl();
- }
-
- @Bean
- public DaoAuthenticationProvider authenticationProvider() {
- DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
-
- authenticationProvider.setUserDetailsService(userDetailsService());
-
- return authenticationProvider;
- }
-
- @Bean
- public JuickUserDetailsService userDetailsService() {
- return new JuickUserDetailsService(userService);
- }
-
- @Bean
- public RememberMeServices rememberMeServices() throws Exception {
- return new RequestParamHashRememberMeServices(rememberMeKey, userService);
- }
-
- @Bean
- public NotAuthorizedAuthenticationEntryPoint getJuickAuthenticationEntryPoint() {
- return new NotAuthorizedAuthenticationEntryPoint();
- }
-
- @Bean
- public CorsConfigurationSource corsConfigurationSource() {
- CorsConfiguration configuration = new CorsConfiguration();
-
- configuration.setAllowedOrigins(Collections.singletonList("*"));
- configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE"));
- configuration.setAllowedHeaders(Collections.singletonList("*"));
-
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/**", configuration);
-
- return source;
- }
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
- "/configuration/**", "/swagger-ui.html", "/webjars/**", "/ws/**");
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java b/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java
deleted file mode 100644
index 259315fb..00000000
--- a/juick-server/src/main/java/com/juick/api/configuration/ApiSecurityInitializer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.configuration;
-
-/**
- * Created by vitalyster on 25.11.2016.
- */
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
-
-import javax.servlet.ServletContext;
-
-public class ApiSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- @Override
- protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
- logger.info("SpringSecurityFilterChain initialized");
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/ApiController.java b/juick-server/src/main/java/com/juick/api/controllers/ApiController.java
deleted file mode 100644
index 0929d1ea..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/ApiController.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.Status;
-import com.juick.api.WebsocketManager;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.net.URI;
-
-/**
- * Created by vitalyster on 25.07.2016.
- */
-@ApiIgnore
-@RestController
-public class ApiController {
- @Inject
- private WebsocketManager wsHandler;
-
- @RequestMapping(value = "/", method = RequestMethod.GET, headers = "Connection!=Upgrade")
- public ResponseEntity description() {
- URI redirectUri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/swagger-ui.html").build().toUri();
- return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).location(redirectUri).build();
- }
-
- @RequestMapping(value = "/api/status", method = RequestMethod.GET,
- produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade")
- public Status status() {
- return Status.getStatus(String.valueOf(wsHandler.getClients().size()));
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Messages.java b/juick-server/src/main/java/com/juick/api/controllers/Messages.java
deleted file mode 100644
index e16e46d5..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Messages.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.Status;
-import com.juick.Tag;
-import com.juick.User;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.UserUtils;
-import com.juick.service.MessagesService;
-import com.juick.service.TagService;
-import com.juick.service.UserService;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.*;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author ugnich
- */
-@RestController
-@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-public class Messages {
-
- private static final ResponseEntity> NOT_FOUND = ResponseEntity
- .status(HttpStatus.NOT_FOUND)
- .body(Collections.emptyList());
-
- private static final ResponseEntity> FORBIDDEN = ResponseEntity
- .status(HttpStatus.FORBIDDEN)
- .body(Collections.emptyList());
-
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private TagService tagService;
-
- // TODO: serialize image urls
-
- @RequestMapping("/home")
- public ResponseEntity> getHome(
- @RequestParam(defaultValue = "0") int before_mid) {
- User visitor = UserUtils.getCurrentUser();
- if (!visitor.isAnonymous()) {
- int vuid = visitor.getUid();
- List mids = messagesService.getMyFeed(vuid, before_mid, true);
-
- if (!mids.isEmpty())
- return ResponseEntity.ok(messagesService.getMessages(mids));
-
- return NOT_FOUND;
- }
- return FORBIDDEN;
- }
-
- @RequestMapping("/messages")
- public ResponseEntity> getMessages(
- @RequestParam(required = false) String uname,
- @RequestParam(name = "before_mid", defaultValue = "0") Integer before,
- @RequestParam(required = false, defaultValue = "0") Integer daysback,
- @RequestParam(required = false) String withrecommended,
- @RequestParam(required = false) String popular,
- @RequestParam(required = false) String media,
- @RequestParam(required = false) String tag) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
-
- List mids;
- if (!StringUtils.isEmpty(uname)) {
- User user = userService.getUserByName(uname);
- if (user != null) {
- if (!StringUtils.isEmpty(media)) {
- mids = messagesService.getUserPhotos(user.getUid(), 0, before);
- } else if (!StringUtils.isEmpty(tag)) {
- Tag tagObject = tagService.getTag(tag, false);
- if (tagObject != null) {
- mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before);
- } else {
- return NOT_FOUND;
- }
- } else if (!StringUtils.isEmpty(withrecommended)) {
- mids = messagesService.getUserBlogWithRecommendations(user.getUid(), 0, before);
- } else if (daysback > 0) {
- mids = messagesService.getUserBlogAtDay(user.getUid(), 0, daysback);
- } else {
- mids = messagesService.getUserBlog(user.getUid(), 0, before);
- }
- } else {
- return NOT_FOUND;
- }
- } else {
- if (!StringUtils.isEmpty(popular)) {
- mids = messagesService.getPopular(vuid, before);
- } else if (!StringUtils.isEmpty(media)) {
- mids = messagesService.getPhotos(vuid, before);
- } else if (!StringUtils.isEmpty(tag)) {
- Tag tagObject = tagService.getTag(tag, false);
- if (tagObject != null) {
- mids = messagesService.getTag(tagObject.TID, vuid, before, 20);
- } else {
- return NOT_FOUND;
- }
- } else {
- mids = messagesService.getAll(vuid, before);
- }
- }
- return ResponseEntity.ok(messagesService.getMessages(mids));
- }
-
- @GetMapping("/messages/notifications")
- public ResponseEntity> getNotifications(
- @RequestParam(required = false) Long before
- ) {
- User visitor = UserUtils.getCurrentUser();
- LocalDateTime beforeTime = before != null ?
- LocalDateTime.ofInstant(Instant.ofEpochMilli(before), ZoneId.systemDefault())
- : null;
- return ResponseEntity.ok(messagesService.getNotifications(visitor, beforeTime));
- }
- @RequestMapping("/thread")
- public ResponseEntity> getThread(
- @RequestParam(defaultValue = "0") int mid) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- com.juick.Message msg = messagesService.getMessage(mid);
- if (msg != null) {
- if (!messagesService.canViewThread(mid, vuid)) {
- return FORBIDDEN;
- } else {
- List replies = messagesService.getReplies(mid);
- replies.add(0, msg);
- return ResponseEntity.ok(replies);
- }
- }
- return NOT_FOUND;
- }
-
- @ApiIgnore
- @RequestMapping("/messages/set_privacy")
- @ResponseBody
- public ResponseEntity doSetPrivacy(
- @RequestParam(defaultValue = "0") int mid) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- com.juick.User user = messagesService.getMessageAuthor(mid);
- if (user != null && user.getUid() == vuid && messagesService.setMessagePrivacy(mid)) {
- return ResponseEntity.ok(Status.OK);
- }
- throw new HttpForbiddenException();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/MessengerWebhook.java b/juick-server/src/main/java/com/juick/api/controllers/MessengerWebhook.java
deleted file mode 100644
index 835165ba..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/MessengerWebhook.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.github.messenger4j.exception.MessengerVerificationException;
-import com.juick.api.MessengerManager;
-import com.juick.server.util.HttpForbiddenException;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Created by vitalyster on 27.03.2017.
- */
-@ApiIgnore
-@RestController
-public class MessengerWebhook {
- private static Logger logger = LoggerFactory.getLogger(MessengerWebhook.class);
-
- @Inject
- private MessengerManager messengerManager;
-
- @RequestMapping(value = "/fbwbhk", method = RequestMethod.GET)
- public ResponseEntity verifyHook(@RequestParam(name = "hub.mode") String hubMode,
- @RequestParam(name = "hub.challenge") Integer hubChallenge,
- @RequestParam(name = "hub.verify_token") String verifyToken) {
- if (hubMode.equals("subscribe") && verifyToken.equals(messengerManager.getFacebookVerifyToken())) {
- return new ResponseEntity<>(hubChallenge, HttpStatus.OK);
- }
- throw new HttpForbiddenException();
- }
- @RequestMapping(value = "/fbwbhk", method = RequestMethod.POST)
- @ResponseStatus(value = HttpStatus.OK)
- public void processUpdate(@RequestHeader(name = "X-Hub-Signature", required = false) String signature, InputStream body) throws IOException, MessengerVerificationException {
- String data = IOUtils.toString(body, StandardCharsets.UTF_8);
- messengerManager.processUpdate(signature, data);
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Notifications.java b/juick-server/src/main/java/com/juick/api/controllers/Notifications.java
deleted file mode 100644
index e4474e6e..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Notifications.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.Message;
-import com.juick.Status;
-import com.juick.ExternalToken;
-import com.juick.User;
-import com.juick.server.helpers.AnonymousUser;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.service.MessagesService;
-import com.juick.service.PushQueriesService;
-import com.juick.service.SubscriptionService;
-import com.juick.server.util.UserUtils;
-import com.juick.service.UserService;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Created by vitalyster on 24.10.2016.
- */
-@RestController
-public class Notifications {
-
- @Inject
- private PushQueriesService pushQueriesService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private UserService userService;
-
-
- private User collectTokens(Integer uid) {
- User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE);
- pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null)));
- pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null)));
- pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null)));
- return user;
- }
-
- @ApiIgnore
- @RequestMapping(value = "/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public ResponseEntity> doGet(
- @RequestParam(required = false, defaultValue = "0") int uid,
- @RequestParam(required = false, defaultValue = "0") int mid,
- @RequestParam(required = false, defaultValue = "0") int rid) {
- User visitor = UserUtils.getCurrentUser();
- if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) {
- throw new HttpForbiddenException();
- }
- if (uid > 0 && mid == 0) {
- // PM
- return ResponseEntity.ok(Collections.singletonList(collectTokens(uid)));
- } else {
- if (mid > 0) {
- Message msg = messagesService.getMessage(mid);
- if (msg != null) {
- List users;
- if (rid > 0) {
- Message reply = messagesService.getReply(mid, rid);
- users = subscriptionService.getUsersSubscribedToComments(mid, reply.getUser().getUid());
- } else {
- users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), mid);
- }
-
- return ResponseEntity.ok(users.stream().map(User::getUid)
- .map(this::collectTokens).collect(Collectors.toList()));
- }
- }
- }
- throw new HttpBadRequestException();
- }
-
- @ApiIgnore
- @RequestMapping(value = "/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doDelete(
- @RequestBody List list) throws IOException {
- User visitor = UserUtils.getCurrentUser();
- // FIXME: it is possible to delete other user's tokens
- if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) {
- throw new HttpForbiddenException();
- }
- list.forEach(t -> {
- switch (t.getType()) {
- case "gcm":
- pushQueriesService.deleteGCMToken(t.getToken());
- break;
- case "apns":
- pushQueriesService.deleteAPNSToken(t.getToken());
- break;
- case "mpns":
- pushQueriesService.deleteMPNSToken(t.getToken());
- break;
- default:
- throw new HttpBadRequestException();
- }
- });
-
- return Status.OK;
- }
-
- @ApiIgnore
- @RequestMapping(value = "/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doPut(
- @RequestBody List list) throws IOException {
- User visitor = UserUtils.getCurrentUser();
- if (visitor.getUid() == 0) {
- throw new HttpForbiddenException();
- }
- list.forEach(t -> {
- switch (t.getType()) {
- case "gcm":
- pushQueriesService.addGCMToken(visitor.getUid(), t.getToken());
- break;
- case "apns":
- pushQueriesService.addAPNSToken(visitor.getUid(), t.getToken());
- break;
- case "mpns":
- pushQueriesService.addMPNSToken(visitor.getUid(), t.getToken());
- break;
- default:
- throw new HttpBadRequestException();
- }
- });
- return Status.OK;
- }
-
- @Deprecated
- @RequestMapping(value = "/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doAndroidRegister(
- @RequestParam(name = "regid") String regId) {
- User visitor = UserUtils.getCurrentUser();
- if (visitor.getUid() == 0) {
- throw new HttpForbiddenException();
- }
- pushQueriesService.addGCMToken(visitor.getUid(), regId);
- return Status.OK;
- }
-
- @Deprecated
- @RequestMapping(value = "/android/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doAndroidUnRegister(@RequestParam(name = "regid") String regId) {
- pushQueriesService.deleteGCMToken(regId);
- return Status.OK;
- }
-
- @Deprecated
- @RequestMapping(value = "/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doWinphoneRegister(
- Principal principal,
- @RequestParam(name = "url") String regId) {
- User visitor = UserUtils.getCurrentUser();
- pushQueriesService.addMPNSToken(visitor.getUid(), regId);
- return Status.OK;
- }
-
- @Deprecated
- @RequestMapping(value = "/winphone/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public Status doWinphoneUnRegister(@RequestParam(name = "url") String regId) {
- pushQueriesService.deleteMPNSToken(regId);
- return Status.OK;
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Others.java b/juick-server/src/main/java/com/juick/api/controllers/Others.java
deleted file mode 100644
index 4245de81..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Others.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.User;
-import com.juick.server.helpers.PrivateChats;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.server.util.UserUtils;
-import com.juick.service.PMQueriesService;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.inject.Inject;
-import java.util.List;
-
-/**
- * @author ugnich
- */
-@RestController
-public class Others {
- @Inject
- private PMQueriesService pmQueriesService;
-
- @RequestMapping(value = "groups_pms", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public PrivateChats doGetGroupsPMs(
- @RequestParam(defaultValue = "5") int cnt) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- if (cnt < 3) {
- cnt = 3;
- }
- if (cnt > 10) {
- cnt = 10;
- }
-
- List lastconv = pmQueriesService.getPMLastConversationsUsers(vuid, cnt);
- if (lastconv != null && !lastconv.isEmpty()) {
- PrivateChats pms = new PrivateChats();
- pms.setUsers(lastconv);
- return pms;
- }
- throw new HttpNotFoundException();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/PM.java b/juick-server/src/main/java/com/juick/api/controllers/PM.java
deleted file mode 100644
index 87858e8c..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/PM.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.User;
-import com.juick.api.ApiServer;
-import com.juick.server.helpers.AnonymousUser;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.service.PMQueriesService;
-import com.juick.service.UserService;
-import com.juick.server.util.UserUtils;
-import com.juick.server.util.WebUtils;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.stanza.model.Message;
-
-import javax.inject.Inject;
-import java.util.List;
-
-/**
- * @author ugnich
- */
-@RestController
-public class PM {
- @Inject
- private UserService userService;
- @Inject
- private PMQueriesService pmQueriesService;
- @Inject
- private ApiServer apiServer;
-
- @RequestMapping(value = "/pm", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List doGetPM(
- @RequestParam(required = false) String uname) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- int uid = 0;
- if (uname != null && uname.matches("^[a-zA-Z0-9\\-]{2,16}$")) {
- uid = userService.getUIDbyName(uname);
- }
-
- if (uid == 0) {
- throw new HttpBadRequestException();
- }
-
- return pmQueriesService.getPMMessages(vuid, uid);
- }
-
- @RequestMapping(value = "/pm", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public com.juick.Message doPostPM(
- @RequestParam String uname,
- @RequestParam String body) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- User userTo = AnonymousUser.INSTANCE;
- if (WebUtils.isUserName(uname)) {
- userTo = userService.getUserByName(uname);
- }
-
- if (userTo.getUid() == 0 || body == null || body.length() < 1 || body.length() > 10240) {
- throw new HttpBadRequestException();
- }
-
- if (userService.isInBLAny(userTo.getUid(), vuid)) {
- throw new HttpForbiddenException();
- }
-
- if (pmQueriesService.createPM(vuid, userTo.getUid(), body)) {
- Message msg = new Message();
- msg.setFrom(Jid.of("juick@juick.com"));
- msg.setTo(Jid.of(String.format("%d@push.juick.com", userTo.getUid())));
- com.juick.Message jmsg = new com.juick.Message();
- jmsg.setUser(visitor);
- jmsg.setText(body);
- jmsg.setTo(userTo);
- msg.addExtension(jmsg);
- apiServer.sendMessage(msg);
-
- msg.setTo(Jid.of(String.format("%d@ws.juick.com", userTo.getUid())));
- apiServer.sendMessage(msg);
-
- List jids = userService.getJIDsbyUID(userTo.getUid());
- for (String jid : jids) {
- Message mm = new Message();
- mm.setTo(Jid.of(jid));
- mm.setType(Message.Type.CHAT);
- if (pmQueriesService.havePMinRoster(vuid, jid)) {
- mm.setFrom(Jid.of(jmsg.getUser().getName(), "juick.com", "Juick"));
- mm.setBody(body);
- } else {
- mm.setFrom(Jid.of("juick", "juick.com", "Juick"));
- mm.setBody("Private message from @" + jmsg.getUser().getName() + ":\n" + body);
- }
- apiServer.sendMessage(mm);
- }
- return jmsg;
-
- }
- throw new HttpBadRequestException();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Post.java b/juick-server/src/main/java/com/juick/api/controllers/Post.java
deleted file mode 100644
index a89786b1..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Post.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.User;
-import com.juick.api.ApiServer;
-import com.juick.api.EmailManager;
-import com.juick.server.util.*;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.mail.util.MimeMessageParser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.stanza.model.Message;
-import rocks.xmpp.extensions.nick.model.Nickname;
-import rocks.xmpp.extensions.oob.model.x.OobX;
-
-import javax.inject.Inject;
-import javax.mail.Session;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Paths;
-import java.util.Properties;
-import java.util.Scanner;
-import java.util.UUID;
-
-/**
- * Created by vt on 24/11/2016.
- */
-@RestController
-public class Post {
- private static Logger logger = LoggerFactory.getLogger(ApiServer.class);
-
- @Inject
- private UserService userService;
- @Inject
- private ApiServer apiServer;
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}")
- private String tmpDir;
- @Value("${img_path:/var/www/juick.com/i/}")
- private String imgDir;
-
- @RequestMapping(value = "/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- @ResponseStatus(value = HttpStatus.OK)
- public void doPostMessage(
- @RequestParam String body,
- @RequestParam(required = false) String img,
- @RequestParam(required = false) MultipartFile attach) throws IOException {
- User visitor = UserUtils.getCurrentUser();
-
- if (visitor.isAnonymous())
- throw new HttpForbiddenException();
-
- if (body == null || body.length() < 1 || body.length() > 4096) {
- throw new HttpBadRequestException();
- }
- body = body.replace("\r", StringUtils.EMPTY);
-
- String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
-
- if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
- try {
- URL imgUrl = new URL(img);
- attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
- }
- }
- apiServer.processMessage(visitor, body, attachmentFName);
- }
-
- @RequestMapping(value = "/comment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public com.juick.Message doPostComment(
- @RequestParam(defaultValue = "0") int mid,
- @RequestParam(defaultValue = "0") int rid,
- @RequestParam String body,
- @RequestParam(required = false) String img,
- @RequestParam(required = false) MultipartFile attach)
- throws IOException {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- if (mid == 0) {
- throw new HttpBadRequestException();
- }
- com.juick.Message msg = messagesService.getMessage(mid);
- if (msg == null) {
- throw new HttpNotFoundException();
- }
-
- com.juick.Message reply = null;
- if (rid > 0) {
- reply = messagesService.getReply(mid, rid);
- if (reply == null) {
- throw new HttpNotFoundException();
- }
- }
-
- if (body == null || body.length() < 1 || body.length() > 4096) {
- throw new HttpBadRequestException();
- }
- body = body.replace("\r", StringUtils.EMPTY);
-
- if ((msg.ReadOnly && msg.getUser().getUid() != vuid) || userService.isInBLAny(msg.getUser().getUid(), vuid)
- || (reply != null && userService.isInBLAny(reply.getUser().getUid(), vuid))) {
- throw new HttpForbiddenException();
- }
-
- String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
-
- if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
- try {
- attachmentFName = HttpUtils.downloadImage(new URL(img), tmpDir);
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
- }
- }
-
- String attachmentType = StringUtils.isNotEmpty(attachmentFName) ? attachmentFName.substring(attachmentFName.length() - 3) : null;
- int ridnew = messagesService.createReply(mid, rid, vuid, body, attachmentType);
- subscriptionService.subscribeMessage(mid, vuid);
-
- com.juick.Message jmsg = messagesService.getReply(mid, ridnew);
-
- Message xmsg = new Message();
- xmsg.setFrom(Jid.of("juick@juick.com"));
- xmsg.setType(Message.Type.CHAT);
- xmsg.setThread("juick-" + mid);
- xmsg.addExtension(jmsg);
-
- String quote = reply != null ? StringUtils.defaultString(reply.getText()) : StringUtils.defaultString(msg.getText());
- if (quote.length() >= 50) {
- quote = quote.substring(0, 47) + "...";
- }
-
- xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName()));
-
- if (StringUtils.isNotEmpty(attachmentFName)) {
- String fname = mid + "-" + ridnew + "." + attachmentType;
- String attachmentURL = "http://i.juick.com/photos-1024/" + fname;
-
- ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir);
-
- body = attachmentURL + "\n" + body;
- try {
- xmsg.addExtension(new OobX(new URI(attachmentURL)));
- } catch (URISyntaxException e) {
- logger.error("invalid uri: {}, exception {}", attachmentURL, e);
- }
- }
-
- xmsg.setBody("Reply by @" + jmsg.getUser().getName() + ":\n>" + quote + "\n" + body + "\n\n#" +
- mid + "/" + ridnew + " http://juick.com/" + mid + "#" + ridnew);
-
- xmsg.setTo(Jid.of("juick@s2s.juick.com"));
- apiServer.sendMessage(xmsg);
-
- xmsg.setTo(Jid.of("juick@ws.juick.com"));
- apiServer.sendMessage(xmsg);
-
- xmsg.setTo(Jid.of("juick@push.juick.com"));
- apiServer.sendMessage(xmsg);
- return jmsg;
- }
-
- Session session = Session.getDefaultInstance(new Properties());
-
- @PostMapping("/mail")
- @ResponseStatus(value = HttpStatus.OK)
- public void processMail(InputStream data) throws Exception {
- MimeMessage msg = new MimeMessage(session, data);
- String from = msg.getFrom().length > 1 ? ((InternetAddress) msg.getSender()).getAddress()
- : ((InternetAddress) msg.getFrom()[0]).getAddress();
- logger.info("got msg from {}", from);
-
- User visitor = userService.getUserByEmail(from);
- if (!visitor.isAnonymous()) {
- MimeMessageParser parser = new MimeMessageParser(msg);
- parser.parse();
- final String[] body = {parser.getPlainContent()};
- if (body[0] == null) {
- parser.getAttachmentList().stream()
- .filter(a -> a.getContentType().equals("text/plain")).findFirst()
- .ifPresent(a -> {
- try {
- body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8);
- logger.info("got text: {}", body[0]);
- } catch (IOException e) {
- logger.info("attachment error: {}", e);
- }
- });
- }
- final String[] attachmentFName = new String[1];
- parser.getAttachmentList().stream().filter(a ->
- a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png"))
- .findFirst().ifPresent(a -> {
- logger.info("got attachment: {}", a.getContentType());
- String attachmentType;
- if (a.getContentType().equals("image/jpeg")) {
- attachmentType = "jpg";
- } else {
- attachmentType = "png";
- }
- attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
- try {
- logger.info("got inputstream: {}", a.getInputStream());
- FileOutputStream fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName[0]).toString());
- IOUtils.copy(a.getInputStream(), fos);
- fos.close();
- } catch (IOException e) {
- logger.info("attachment error: {}", e);
- }
- });
- String[] inReplyToHeaders = msg.getHeader("In-Reply-To");
- if (inReplyToHeaders != null && inReplyToHeaders.length > 0) {
- Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()).useDelimiter(EmailManager.MSGID_PATTERN);
- int mid = Integer.parseInt(inReplyToScanner.next());
- int rid = Integer.parseInt(inReplyToScanner.next());
- logger.info("Message is reply to #{}/{}", mid, rid);
- body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0])
- : String.format("#%d %s", mid, body[0]);
- }
- rocks.xmpp.core.stanza.model.Message xmsg = new rocks.xmpp.core.stanza.model.Message();
- xmsg.setType(rocks.xmpp.core.stanza.model.Message.Type.CHAT);
- xmsg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "mail"));
- xmsg.setTo(Jid.of("juick@juick.com/Juick"));
- xmsg.setBody(body[0]);
- try {
- if (StringUtils.isNotEmpty(attachmentFName[0])) {
- String attachmentUrl = String.format("juick://%s", attachmentFName[0]);
- xmsg.addExtension(new OobX(new URI(attachmentUrl), "!!!!Juick!!"));
- }
- apiServer.sendMessage(xmsg);
- } catch (URISyntaxException e1) {
- logger.warn("attachment error", e1);
- }
- } else {
- logger.info("not registered: {}", from);
- }
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Service.java b/juick-server/src/main/java/com/juick/api/controllers/Service.java
deleted file mode 100644
index 9e7f3333..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Service.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.Message;
-import com.juick.User;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.UserUtils;
-import com.juick.service.CrosspostService;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * TODO: configure spring-security to allow only admin role
- */
-@ApiIgnore
-@RestController
-public class Service {
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private CrosspostService crosspostService;
- @Inject
- private UserService userService;
-
- @RequestMapping(value = "/subscriptions", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List doGet(
- @RequestParam(defaultValue = "0") int mid,
- @RequestParam(defaultValue = "0") int uid) throws IOException {
- User visitor = UserUtils.getCurrentUser();
- if ((visitor.getUid() == 0) && !(visitor.getName().equals("juick"))) {
- throw new HttpForbiddenException();
- }
- if (uid > 0) {
- return subscriptionService.getSubscribedUsers(uid, mid);
- } else {
- // thread
- Message msg = messagesService.getMessage(mid);
- if (msg != null) {
- return subscriptionService.getUsersSubscribedToComments(mid, msg.getUser().getUid());
- }
- }
- throw new HttpBadRequestException();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/SkypeEndpoint.java b/juick-server/src/main/java/com/juick/api/controllers/SkypeEndpoint.java
deleted file mode 100644
index 91a617b4..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/SkypeEndpoint.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.annotations.ApiIgnore;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Created by vitalyster on 18.07.2016.
- */
-@ApiIgnore
-@RestController
-public class SkypeEndpoint {
- private static final Logger logger = LoggerFactory.getLogger(SkypeEndpoint.class);
- @RequestMapping(value = "/skypebotendpoint", method = RequestMethod.POST)
- @ResponseStatus(value = HttpStatus.OK)
- public void doPost(InputStream body) throws IOException {
- String data = IOUtils.toString(body, StandardCharsets.UTF_8);
- logger.info("got data: {}", data);
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Tags.java b/juick-server/src/main/java/com/juick/api/controllers/Tags.java
deleted file mode 100644
index 85bb1ba4..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Tags.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.User;
-import com.juick.server.helpers.TagStats;
-import com.juick.server.util.UserUtils;
-import com.juick.service.TagService;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.inject.Inject;
-import java.util.List;
-
-/**
- * Created by vitalyster on 29.11.2016.
- */
-@RestController
-public class Tags {
- @Inject
- private TagService tagService;
-
- @RequestMapping(value = "/tags", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List tags(
- @RequestParam(required = false, defaultValue = "0") int user_id
- ) {
- User visitor = UserUtils.getCurrentUser();
- if (user_id == 0) {
- user_id = visitor.getUid();
- }
- if (user_id > 0) {
- return tagService.getUserTagStats(user_id);
- }
- return tagService.getTagStats();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/TelegramWebhook.java b/juick-server/src/main/java/com/juick/api/controllers/TelegramWebhook.java
deleted file mode 100644
index 31e7b4ea..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/TelegramWebhook.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.api.TelegramBotManager;
-import org.apache.commons.io.IOUtils;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Created by vt on 24/11/2016.
- */
-@ApiIgnore
-@RestController
-public class TelegramWebhook {
- @Inject
- private TelegramBotManager telegramBotManager;
-
- @RequestMapping(value = "/tlgmbtwbhk", method = RequestMethod.POST)
- @ResponseStatus(value = HttpStatus.OK)
- public void processUpdate(InputStream body) throws Exception {
- String data = IOUtils.toString(body, StandardCharsets.UTF_8);
- telegramBotManager.processUpdate(data);
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/Users.java b/juick-server/src/main/java/com/juick/api/controllers/Users.java
deleted file mode 100644
index b221d9fe..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/Users.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers;
-
-import com.juick.User;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.service.UserService;
-import com.juick.server.util.UserUtils;
-import com.juick.server.util.WebUtils;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
-
-import javax.inject.Inject;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author ugnich
- */
-@RestController
-public class Users {
- @Inject
- private UserService userService;
-
- @RequestMapping(value = "/auth", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public String getAuthToken() {
- return userService.getHashByUID(UserUtils.getCurrentUser().getUid());
- }
-
- @RequestMapping(value = "/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List doGetUsers(
- @RequestParam(value = "uname", required = false) List unames) {
- List users = new ArrayList<>();
-
- if (unames != null) {
- unames.removeIf(WebUtils::isNotUserName);
-
- if (!unames.isEmpty() && unames.size() < 20)
- users.addAll(userService.getUsersByName(unames));
- }
-
- if (!users.isEmpty())
- return users;
- if (!UserUtils.getCurrentUser().isAnonymous()) {
- return Collections.singletonList(UserUtils.getCurrentUser());
- }
-
- throw new HttpNotFoundException();
- }
-
- @RequestMapping(value = "/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List doGetUserRead(
- @RequestParam String uname) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- int uid = 0;
- if (uname == null) {
- uid = vuid;
- } else {
- if (WebUtils.isUserName(uname)) {
- com.juick.User u = userService.getUserByName(uname);
- if (u != null && u.getUid() > 0) {
- uid = u.getUid();
- }
- }
- }
-
- if (uid > 0) {
- List uids = userService.getUserRead(uid);
- if (uids.size() > 0) {
- List users = userService.getUsersByID(uids);
- if (users.size() > 0) {
- return users;
- }
- }
- }
- throw new HttpNotFoundException();
- }
-
- @RequestMapping(value = "/users/readers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public List doGetUserReaders(
- @RequestParam String uname) {
- User visitor = UserUtils.getCurrentUser();
- int vuid = visitor.getUid();
- if (vuid == 0) {
- throw new HttpForbiddenException();
- }
- int uid = 0;
- if (uname == null) {
- uid = vuid;
- } else {
- if (WebUtils.isUserName(uname)) {
- com.juick.User u = userService.getUserByName(uname);
- if (u != null && u.getUid() > 0) {
- uid = u.getUid();
- }
- }
- }
-
- if (uid > 0) {
- return userService.getUserReaders(uid);
- }
- throw new HttpNotFoundException();
- }
-}
diff --git a/juick-server/src/main/java/com/juick/api/controllers/util/JsonpAdvice.java b/juick-server/src/main/java/com/juick/api/controllers/util/JsonpAdvice.java
deleted file mode 100644
index 90a01da0..00000000
--- a/juick-server/src/main/java/com/juick/api/controllers/util/JsonpAdvice.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.controllers.util;
-
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
-
-/**
- * Created by vitalyster on 25.11.2016.
- */
-@ControllerAdvice
-public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
- public JsonpAdvice() {
- super("callback");
- }
-}
diff --git a/juick-server/src/main/java/com/juick/server/CrosspostManager.java b/juick-server/src/main/java/com/juick/server/CrosspostManager.java
new file mode 100644
index 00000000..edffaf71
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/CrosspostManager.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package com.juick.server;
+
+import com.juick.Message;
+import com.juick.server.component.MessageEvent;
+import com.juick.service.CrosspostService;
+import com.juick.util.MessageUtils;
+import org.apache.commons.codec.CharEncoding;
+import org.apache.commons.io.IOUtils;
+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.ApplicationListener;
+import org.springframework.social.twitter.api.Twitter;
+import org.springframework.social.twitter.api.impl.TwitterTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Nonnull;
+import javax.inject.Inject;
+import javax.net.ssl.HttpsURLConnection;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author Ugnich Anton
+ */
+@Component
+public class CrosspostManager implements ApplicationListener {
+ private final static String FBURL = "https://graph.facebook.com/me/feed";
+ private final static String VKURL = "https://api.vk.com/method/wall.post";
+
+ private static Logger logger = LoggerFactory.getLogger(CrosspostManager.class);
+
+ @Inject
+ private CrosspostService crosspostService;
+
+ @Value("${twitter_consumer_key:}")
+ private String twitter_consumer_key;
+ @Value("${twitter_consumer_secret:}")
+ private String twitter_consumer_secret;
+
+ @Override
+ public void onApplicationEvent(@Nonnull MessageEvent event) {
+ Message msg = event.getMessage();
+ if (msg.getMid() > 0 && msg.getRid() == 0) {
+ if (StringUtils.isNotEmpty(crosspostService.getTwitterName(msg.getUser().getUid()))) {
+ if (msg.getTags().stream().noneMatch(t -> t.getName().equals("notwitter"))) {
+ twitterPost(msg);
+ }
+ }
+ // TODO: approve application for facebook crosspost
+ }
+ }
+
+ private boolean facebookPost(final com.juick.Message jmsg) {
+ String token = crosspostService.getFacebookTokens(jmsg.getUser().getUid())
+ .orElse(Pair.of(StringUtils.EMPTY, StringUtils.EMPTY)).getRight();
+ if (token.isEmpty()) {
+ return false;
+ }
+
+ logger.info("FB: #{}", jmsg.getMid());
+
+ String status = MessageUtils.getMessageHashTags(jmsg) + "\n" + jmsg.getText();
+
+ boolean ret = false;
+ try {
+ String body = "access_token="
+ + URLEncoder.encode(token, CharEncoding.UTF_8)
+ + "&message="
+ + URLEncoder.encode(status, CharEncoding.UTF_8)
+ + "&link=http%3A%2F%2Fjuick.com%2F"
+ + jmsg.getMid();
+
+ HttpsURLConnection conn = (HttpsURLConnection) new URL(FBURL).openConnection();
+ conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ conn.setRequestProperty("User-Agent", "Juick");
+ conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
+ conn.setUseCaches(false);
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setRequestMethod("POST");
+ conn.connect();
+
+ OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
+ wr.write(body);
+ wr.close();
+
+ ret = StringUtils.isNotEmpty(IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8));
+
+ conn.disconnect();
+ } catch (Exception e) {
+ logger.error("fbPost exception", e);
+ }
+ return ret;
+ }
+
+ private boolean vkontaktePost(final com.juick.Message jmsg) {
+ Pair tokens = crosspostService.getVkTokens(jmsg.getUser().getUid()).orElse(Pair.of(StringUtils.EMPTY, StringUtils.EMPTY));
+ if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) {
+ return false;
+ }
+
+ logger.info("VK: #", jmsg.getMid());
+
+ String status = MessageUtils.getMessageHashTags(jmsg) + "\n" + jmsg.getText() + "\nhttp://juick.com/" + jmsg.getMid();
+
+ boolean ret = false;
+ try {
+ String body = "owner_id=" + tokens.getLeft() + "&access_token=" + URLEncoder.encode(tokens.getRight(), CharEncoding.UTF_8) + "&from_group=1&message=" + URLEncoder.encode(status, CharEncoding.UTF_8);
+
+ HttpsURLConnection conn = (HttpsURLConnection) new URL(VKURL).openConnection();
+ conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ conn.setRequestProperty("User-Agent", "Juick");
+ conn.setRequestProperty("Content-Length", Integer.toString(body.length()));
+ conn.setUseCaches(false);
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setRequestMethod("POST");
+ conn.connect();
+
+ OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
+ wr.write(body);
+ wr.close();
+
+ ret = StringUtils.isNotEmpty(IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8));
+
+ conn.disconnect();
+ } catch (Exception e) {
+ logger.error("vkPost exception", e);
+ }
+ return ret;
+ }
+
+ private void twitterPost(final com.juick.Message jmsg) {
+ crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> {
+ String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
+ if (status.length() > 255) {
+ status = status.substring(0, 254) + "…";
+ }
+ status += " http://juick.com/" + jmsg.getMid();
+ Twitter twitter = new TwitterTemplate(twitter_consumer_key, twitter_consumer_secret, t.getToken(), t.getSecret());
+ twitter.timelineOperations().updateStatus(status);
+ });
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/EmailManager.java b/juick-server/src/main/java/com/juick/server/EmailManager.java
new file mode 100644
index 00000000..4c9bf3da
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/EmailManager.java
@@ -0,0 +1,110 @@
+package com.juick.server;
+
+import com.juick.Message;
+import com.juick.server.component.MessageEvent;
+import com.juick.service.EmailService;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
+import com.juick.util.MessageUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+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.Properties;
+
+import static com.juick.formatters.PlainTextFormatter.formatPost;
+import static com.juick.formatters.PlainTextFormatter.formatUrl;
+
+@Component
+public class EmailManager implements ApplicationListener {
+
+ public static final String MSGID_PATTERN = "\\.|@|<";
+
+ private static final Logger logger = LoggerFactory.getLogger(EmailManager.class);
+ @Inject
+ private EmailService emailService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private UserService userService;
+ @Override
+ public void onApplicationEvent(@Nonnull MessageEvent event) {
+ Message msg = event.getMessage();
+ if (msg.getMid() > 0 && msg.getRid() == 0) {
+ String subject = String.format("New message from %s", msg.getUser().getName());
+ subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg.getMid())
+ .forEach(user -> emailService.getEmails(user.getUid(), true)
+ .forEach(email -> emailNotify(email, subject, msg)));
+ } else if (msg.getRid() > 0) {
+ Message originalMessage = messagesService.getMessage(msg.getMid());
+ String subject = String.format("New reply to %s", originalMessage.getUser().getName());
+ subscriptionService.getUsersSubscribedToComments(msg.getMid(), msg.getUser().getUid())
+ .forEach(user -> emailService.getEmails(user.getUid(), true)
+ .forEach(email -> emailNotify(email, subject, msg)));
+ }
+ }
+
+ private void emailNotify(String email, String subject, Message msg) {
+ 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 {
+ setHeader("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid()));
+ if (msg.getRid() > 0) {
+ if (msg.getReplyto() > 0) {
+ Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto());
+ setHeader("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid()));
+ } else {
+ Message original = messagesService.getMessage(msg.getMid());
+ setHeader("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid()));
+ }
+ }
+ }
+ };
+ message.setFrom(new InternetAddress("juick@juick.com"));
+ message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(email));
+ message.setSubject(subject);
+ String plainText = String.format("%s\n\n--\nYou are receiving this because you are subscribed to this user " +
+ ", discussion or tag. Reply to this email directly or view it on Juick: %s.",
+ formatPost(msg), formatUrl(msg));
+ MimeBodyPart textBodyPart = new MimeBodyPart();
+ textBodyPart.setContent(plainText, "text/plain; charset=UTF-8");
+ String htmlText = String.format("%s
--
You are receiving this because you are subscribed to this user" +
+ ", discussion or tag. Reply to this email directly or view it on Juick." +
+ "
Configure or disable notifications",
+ MessageUtils.formatHtml(msg), formatUrl(msg),
+ userService.getHashByUID(userService.getUserByEmail(email).getUid()));
+ MimeBodyPart htmlBodyPart = new MimeBodyPart();
+ htmlBodyPart.setContent(htmlText, "text/html; charset=UTF-8");
+ Multipart multipart = new MimeMultipart("alternative");
+ multipart.addBodyPart(textBodyPart);
+ multipart.addBodyPart(htmlBodyPart);
+ message.setContent(multipart);
+ message.setHeader("List-Unsubscribe", String.format("https://juick.com/settings?hash=%s",
+ userService.getHashByUID(userService.getUserByEmail(email).getUid())));
+ message.saveChanges();
+ transport.connect();
+ transport.sendMessage(message, message.getAllRecipients());
+ } catch (MessagingException ex) {
+ logger.error("mail exception", ex);
+ }
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/FacebookPageManager.java b/juick-server/src/main/java/com/juick/server/FacebookPageManager.java
new file mode 100644
index 00000000..971b206c
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/FacebookPageManager.java
@@ -0,0 +1,32 @@
+package com.juick.server;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+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.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Component
+public class FacebookPageManager {
+ private static Logger logger = LoggerFactory.getLogger(FacebookPageManager.class);
+ @Value("${facebook_page_id:12345678}")
+ private String pageId;
+ @Value("${fb_page_access_token:12345678}")
+ private String accessToken;
+
+ public void post(String status, String link) {
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString("https://graph.facebook.com/{page_id}/feed")
+ .queryParam("message", status)
+ .queryParam("link", link)
+ .queryParam("access_token", accessToken)
+ .buildAndExpand(pageId);
+ RestTemplate api = new RestTemplate();
+ ResponseEntity response = api.exchange(uriComponents.toUri(),
+ HttpMethod.POST, null, String.class);
+ logger.info("Facebook response: {}", response.getBody());
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/MessengerManager.java b/juick-server/src/main/java/com/juick/server/MessengerManager.java
new file mode 100644
index 00000000..2979fa28
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/MessengerManager.java
@@ -0,0 +1,143 @@
+package com.juick.server;
+
+import com.github.messenger4j.Messenger;
+import com.github.messenger4j.exception.MessengerApiException;
+import com.github.messenger4j.exception.MessengerIOException;
+import com.github.messenger4j.exception.MessengerVerificationException;
+import com.github.messenger4j.send.MessagePayload;
+import com.github.messenger4j.send.message.TemplateMessage;
+import com.github.messenger4j.send.message.TextMessage;
+import com.github.messenger4j.send.message.template.ButtonTemplate;
+import com.github.messenger4j.send.message.template.button.UrlButton;
+import com.github.messenger4j.userprofile.UserProfile;
+import com.github.messenger4j.webhook.event.TextMessageEvent;
+import com.juick.Message;
+import com.juick.User;
+import com.juick.server.component.MessageEvent;
+import com.juick.service.MessagesService;
+import com.juick.service.MessengerService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Optional;
+
+import static com.juick.formatters.PlainTextFormatter.formatPost;
+import static com.juick.formatters.PlainTextFormatter.formatUrl;
+
+@Component
+public class MessengerManager implements ApplicationListener {
+ private static final Logger logger = LoggerFactory.getLogger(MessengerManager.class);
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private UserService userService;
+ @Inject
+ private MessengerService messengerService;
+ @Inject
+ private ServerManager serverManager;
+
+ @Value("${fb_page_access_token:12345678}")
+ private String facebookPageAccessToken;
+ @Value("${fb_verify_token:12345678}")
+ private String facebookVerifyToken;
+ @Value("${fb_secret:12345678}")
+ private String facebookSecret;
+
+ private Messenger messenger;
+
+ @PostConstruct
+ public void init() {
+ messenger = Messenger.create(facebookPageAccessToken, facebookSecret, facebookVerifyToken);
+ }
+
+ public String getFacebookVerifyToken() {
+ return facebookVerifyToken;
+ }
+
+ public void processUpdate(String signature , String data) throws MessengerVerificationException {
+ messenger.onReceiveEvents(data, Optional.of(signature), event -> {
+ final String senderId = event.senderId();
+ final Instant timestamp = event.timestamp();
+
+ User user_from = userService.getUserByUID(messengerService.getUserId(senderId)).orElse(new User());
+ logger.info("Found juick user {}", user_from.getUid());
+ if (user_from.getUid() == 0) {
+ try {
+ UserProfile profile = messenger.queryUserProfile(senderId);
+ signupNotify(senderId, messengerService.getSignUpHash(senderId, profile.firstName()));
+ } catch (MessengerApiException | MessengerIOException | MalformedURLException e) {
+ logger.warn("messenger profile error", e);
+ try {
+ signupNotify(senderId, messengerService.getSignUpHash(senderId, "anonymous"));
+ } catch (MalformedURLException | MessengerApiException | MessengerIOException e1) {
+ logger.warn("signup error", e1);
+ }
+ }
+ } else {
+ if (event.isTextMessageEvent()) {
+ final TextMessageEvent textMessageEvent = event.asTextMessageEvent();
+ final String messageId = textMessageEvent.messageId();
+ final String text = textMessageEvent.text();
+ logger.info("Received text message from '{}' at '{}' with content: {} (mid: {})",
+ senderId, timestamp, text, messageId);
+ serverManager.processMessage(user_from, text, null);
+ messengerNotify(senderId, "Message sent", null);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onApplicationEvent(@Nonnull MessageEvent event) {
+ Message msg = event.getMessage();
+ if (msg.getMid() > 0 && msg.getRid() == 0) {
+ String subject = formatPost(msg);
+ subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg.getMid())
+ .forEach(user -> messengerService.getSenderId(user)
+ .ifPresent(t -> messengerNotify(t, subject, formatUrl(msg))));
+ } else if (msg.getRid() > 0) {
+ // get quote
+ com.juick.Message jmsg = messagesService.getReply(msg.getMid(), msg.getRid());
+ String subject = formatPost(jmsg);
+ subscriptionService.getUsersSubscribedToComments(msg.getMid(), msg.getUser().getUid())
+ .forEach(user -> messengerService.getSenderId(user)
+ .ifPresent(t -> messengerNotify(t, subject, formatUrl(jmsg))));
+ }
+ }
+
+ private void messengerNotify(String messengerUser, String text, String url) {
+ try {
+ if (!StringUtils.isEmpty(url)) {
+ final UrlButton showMessage = UrlButton.create("VIEW MESSAGE", new URL(url));
+ ButtonTemplate template = ButtonTemplate.create(text, Collections.singletonList(showMessage));
+ messenger.send(MessagePayload.create(messengerUser, TemplateMessage.create(template)));
+ } else {
+ messenger.send(MessagePayload.create(messengerUser, TextMessage.create(text)));
+ }
+ } catch (MessengerApiException | MessengerIOException | MalformedURLException e) {
+ logger.warn("messenger error", e);
+ }
+ }
+ private void signupNotify(String messengerUser, String hash) throws MalformedURLException, MessengerApiException, MessengerIOException {
+ final UrlButton urlButton = UrlButton.create("LOGIN",
+ new URL("https://juick.com/signup?type=messenger&hash=" + hash));
+ ButtonTemplate template = ButtonTemplate.create("Login to receive notifications",
+ Collections.singletonList(urlButton));
+ messenger.send(MessagePayload.create(messengerUser, TemplateMessage.create(template)));
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/ServerManager.java b/juick-server/src/main/java/com/juick/server/ServerManager.java
new file mode 100644
index 00000000..79ccb1e4
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/ServerManager.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package com.juick.server;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.juick.User;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.TextMessage;
+import rocks.xmpp.addr.Jid;
+import rocks.xmpp.core.XmppException;
+import rocks.xmpp.core.session.Extension;
+import rocks.xmpp.core.session.XmppSessionConfiguration;
+import rocks.xmpp.core.stanza.model.Message;
+import rocks.xmpp.extensions.component.accept.ExternalComponent;
+import rocks.xmpp.extensions.oob.model.x.OobX;
+import rocks.xmpp.util.XmppUtils;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Ugnich Anton
+ */
+@Component
+public class ServerManager implements AutoCloseable {
+ private static Logger logger = LoggerFactory.getLogger(ServerManager.class);
+
+ private ExternalComponent xmpp;
+
+ @Value("${xmpp_host:localhost}")
+ private String xmppHost;
+ @Value("${xmpp_password:secret}")
+ private String xmppPassword;
+ @Value("${ws_jid:ws.localhost}")
+ private String xmppJid;
+ @Value("${xmpp_port:5347}")
+ private int xmppPort;
+ @Value("${xmpp_disabled:false}")
+ private boolean isXmppDisabled;
+ @Inject
+ private ObjectMapper jsonMapper;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private WebsocketManager wsHandler;
+ @Value("${service_user:juick}")
+ private String serviceUser;
+
+ @PostConstruct
+ public void init() {
+ if (!isXmppDisabled) {
+ setupXmppComponent(xmppHost, xmppPort, xmppJid, xmppPassword);
+ } else {
+ logger.warn("XMPP is not enabled");
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ if (xmpp != null)
+ xmpp.close();
+
+ logger.info("ExternalComponent on juick-server destroyed");
+ } catch (Exception e) {
+ logger.warn("Exception occurs on juick-server destroy", e);
+ }
+ }
+
+ public void setupXmppComponent(final String host, final int port, final String jid, final String password) {
+ XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
+ .extensions(Extension.of(com.juick.Message.class))
+ .build();
+ xmpp = ExternalComponent.create(jid, password, configuration, host, port);
+ xmpp.addInboundMessageListener(e -> {
+ try {
+ Message msg = e.getMessage();
+ com.juick.Message jmsg = msg.getExtension(com.juick.Message.class);
+ if (jmsg != null) {
+ if (logger.isInfoEnabled()) { // prevent writeValueAsString execution if log is disabled
+ try {
+ StringWriter stanzaWriter = new StringWriter();
+ XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter(
+ xmpp.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter));
+ xmpp.createMarshaller().marshal(msg, xmppStreamWriter);
+ xmppStreamWriter.flush();
+ xmppStreamWriter.close();
+ logger.info("got msg: {}", stanzaWriter.toString());
+ } catch (XMLStreamException e1) {
+ logger.info("jaxb exception", e1);
+ }
+
+ }
+ if (jmsg.getMid() == 0) {
+ int uid_to = NumberUtils.toInt(msg.getTo().getLocal(), 0);
+ if (uid_to > 0) {
+ onJuickPM(uid_to, jmsg);
+ }
+ } else if (jmsg.getRid() == 0) {
+ // to get full message with attachment, etc.
+ onJuickMessagePost(messagesService.getMessage(jmsg.getMid()));
+ } else {
+ // to get quote and attachment
+ com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
+ onJuickMessageReply(reply);
+ }
+ }
+ } catch (JsonProcessingException ex) {
+ logger.error("mapper exception", ex);
+ } catch (JAXBException exc) {
+ logger.error("jaxb exception", exc);
+ }
+ });
+ try {
+ xmpp.connect();
+ } catch (XmppException e) {
+ logger.warn("xmpp extension", e);
+ }
+ }
+
+ public void sendMessage(Message message) {
+ if (!isXmppDisabled) {
+ xmpp.sendMessage(message);
+ }
+ }
+
+ public void processMessage(User visitor, String body, String attachmentName) {
+ Message xmsg = new Message();
+ xmsg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "perl"));
+ xmsg.setTo(Jid.of("juick@juick.com/Juick"));
+ xmsg.setBody(body);
+ try {
+ if (StringUtils.isNotEmpty(attachmentName)) {
+ String attachmentUrl = String.format("juick://%s", attachmentName);
+ xmsg.addExtension(new OobX(new URI(attachmentUrl), "!!!!Juick!!"));
+ }
+ sendMessage(xmsg);
+ } catch (URISyntaxException e1) {
+ logger.warn("attachment error", e1);
+ }
+ }
+
+ private void onJuickPM(final int uid_to, final com.juick.Message jmsg) throws JsonProcessingException {
+ String json = jsonMapper.writeValueAsString(jmsg);
+ synchronized (wsHandler.getClients()) {
+ wsHandler.getClients().stream().filter(c ->
+ (!c.legacy && c.visitor.getUid() == uid_to) || c.visitor.getName().equals(serviceUser))
+ .forEach(c -> {
+ try {
+ logger.info("sending pm to {}", c.visitor.getUid());
+ c.session.sendMessage(new TextMessage(json));
+ } catch (IOException e) {
+ logger.warn("ws error", e);
+ }
+ });
+ }
+ }
+
+ private void onJuickMessagePost(final com.juick.Message jmsg) throws JsonProcessingException {
+ String json = jsonMapper.writeValueAsString(jmsg);
+ List uids = subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid())
+ .stream().map(User::getUid).collect(Collectors.toList());
+ synchronized (wsHandler.getClients()) {
+ wsHandler.getClients().stream().filter(c ->
+ (!c.legacy && c.visitor.getUid() == 0) // anonymous users
+ || c.visitor.getName().equals(serviceUser) // services
+ || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions
+ .forEach(c -> {
+ try {
+ logger.info("sending message to {}", c.visitor.getUid());
+ c.session.sendMessage(new TextMessage(json));
+ } catch (IOException e) {
+ logger.warn("ws error", e);
+ }
+ });
+ wsHandler.getClients().stream().filter(c ->
+ c.legacy && c.allMessages) // legacy all posts
+ .forEach(c -> {
+ try {
+ logger.info("sending message to legacy client {}", c.visitor.getUid());
+ c.session.sendMessage(new TextMessage(json));
+ } catch (IOException e) {
+ logger.warn("ws error", e);
+ }
+ });
+ }
+ }
+
+ private void onJuickMessageReply(final com.juick.Message jmsg) throws JsonProcessingException {
+ String json = jsonMapper.writeValueAsString(jmsg);
+ List threadUsers =
+ subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid())
+ .stream().map(User::getUid).collect(Collectors.toList());
+ synchronized (wsHandler.getClients()) {
+ wsHandler.getClients().stream().filter(c ->
+ (!c.legacy && c.visitor.getUid() == 0) // anonymous users
+ || c.visitor.getName().equals(serviceUser) // services
+ || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions
+ .forEach(c -> {
+ try {
+ logger.info("sending reply to {}", c.visitor.getUid());
+ c.session.sendMessage(new TextMessage(json));
+ } catch (IOException e) {
+ logger.warn("ws error", e);
+ }
+ });
+ wsHandler.getClients().stream().filter(c ->
+ (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies
+ .forEach(c -> {
+ try {
+ logger.info("sending reply to legacy client {}", c.visitor.getUid());
+ c.session.sendMessage(new TextMessage(json));
+ } catch (IOException e) {
+ logger.warn("ws error", e);
+ }
+ });
+ }
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java
new file mode 100644
index 00000000..564a2255
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server;
+
+import com.juick.User;
+import com.juick.server.component.MessageEvent;
+import com.juick.server.util.HttpUtils;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.TelegramService;
+import com.juick.service.UserService;
+import com.pengrad.telegrambot.BotUtils;
+import com.pengrad.telegrambot.Callback;
+import com.pengrad.telegrambot.TelegramBot;
+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.SetWebhook;
+import com.pengrad.telegrambot.response.GetFileResponse;
+import com.pengrad.telegrambot.response.SendResponse;
+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.ApplicationListener;
+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.net.URL;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+import static com.juick.formatters.PlainTextFormatter.formatPost;
+import static com.juick.formatters.PlainTextFormatter.formatUrl;
+
+/**
+ * Created by vt on 12/05/16.
+ */
+@Component
+public class TelegramBotManager implements ApplicationListener {
+ private static final Logger logger = LoggerFactory.getLogger(TelegramBotManager.class);
+
+ private TelegramBot bot;
+
+ @Value("${telegram_token}")
+ private String telegramToken;
+ @Inject
+ private TelegramService telegramService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private UserService userService;
+ @Inject
+ private ServerManager serverManager;
+ @Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}")
+ private String tmpDir;
+
+ private static final String MSG_LINK = "🔗";
+
+ @PostConstruct
+ public void init() {
+ if (StringUtils.isBlank(telegramToken)) {
+ logger.info("telegram token is not set, exiting");
+ return;
+ }
+ bot = new TelegramBot(telegramToken);
+ try {
+ SetWebhook webhook = new SetWebhook().url("https://api.juick.com/tlgmbtwbhk");
+ if (!bot.execute(webhook).isOk()) {
+ logger.error("error setting webhook");
+ }
+ } catch (Exception e) {
+ logger.warn("couldn't initialize telegram bot", e);
+ }
+ }
+
+ public void processUpdate(String data) throws Exception {
+ Update update = BotUtils.parseUpdate(data);
+ 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(new User());
+ logger.info("Found juick user {}", user_from.getUid());
+
+ List chats = telegramService.getChats();
+ String username = message.from().username();
+ if (username == null) {
+ username = message.from().firstName();
+ }
+ if (!chats.contains(message.chat().id())) {
+ telegramService.addChat(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));
+ } else {
+ if (user_from.getUid() == 0) {
+ telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username));
+ } else {
+ String attachment = 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());
+ URL fileURL = new URL(bot.getFullFilePath(response.file()));
+ attachment = HttpUtils.downloadImage(fileURL, tmpDir);
+ 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)) {
+ if (text.equalsIgnoreCase("LOGIN")
+ || text.equalsIgnoreCase("PING")
+ || text.equalsIgnoreCase("HELP")
+ || text.equalsIgnoreCase("/login")
+ || 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);
+ } else {
+ Message replyMessage = message.replyToMessage();
+ if (replyMessage != null) {
+ if (replyMessage.entities() != null) {
+ Optional juickLink = Arrays.stream(replyMessage.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 = Integer.valueOf(path.substring(1));
+ String prefix = String.format("#%d ", mid);
+ if (StringUtils.isNotEmpty(uriComponents.getFragment())) {
+ int rid = Integer.valueOf(uriComponents.getFragment());
+ prefix = String.format("#%d/%d ", mid, rid);
+ }
+ serverManager.processMessage(user_from, prefix + text, attachment);
+ telegramNotify(message.from().id().longValue(), "Reply sent");
+ } 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());
+ }
+ } else {
+ telegramNotify(message.from().id().longValue(),
+ "Can not reply to this message", replyMessage.messageId());
+ }
+ } else {
+ serverManager.processMessage(user_from, text, attachment);
+ telegramNotify(message.from().id().longValue(), "Message sent");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isJuickLink(MessageEntity e) {
+ return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2;
+ }
+
+ @Override
+ public void onApplicationEvent(@Nonnull MessageEvent event) {
+ com.juick.Message jmsg = event.getMessage();
+ String msgUrl = formatUrl(jmsg);
+ if (jmsg.getMid() > 0 && jmsg.getRid() == 0) {
+ String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true));
+
+ List users = telegramService.getTelegramIdentifiers(subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid()));
+ List chats = telegramService.getChats();
+ // registered subscribed users
+
+ users.forEach(c -> telegramNotify(c, msg));
+ // anonymous
+ chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg));
+ } else if (jmsg.getRid() > 0) {
+ // get quote
+ com.juick.Message msg = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
+ String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(msg, true));
+ telegramService.getTelegramIdentifiers(
+ subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid())
+ ).forEach(c -> telegramNotify(c, fmsg));
+ }
+ }
+
+ public void telegramNotify(Long chatId, String msg) {
+ telegramNotify(chatId, msg, 0);
+ }
+
+ public void telegramNotify(Long chatId, String msg, Integer replyTo) {
+ 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) {
+ if (!response.isOk()) {
+ if (response.errorCode() == 403) {
+ // remove from anonymous chat
+ telegramService.getChats().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent(
+ d -> {
+ telegramService.deleteChat(d);
+ logger.info("deleted {} chat", d);
+ }
+ );
+ int userId = telegramService.getUser(chatId);
+ if (userId > 0) {
+ User userToDelete = userService.getUserByUID(userId)
+ .orElse(new User());
+ boolean status = telegramService.deleteTelegramUser(userToDelete.getUid());
+ logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status);
+ boolean chatStatus = telegramService.deleteChat(chatId);
+ logger.info("deleting telegram chat {} : {}", chatId, chatStatus);
+ }
+ } else {
+ logger.warn("error response, isOk: {}, errorCode: {}, description: {}",
+ response.isOk(), response.errorCode(), response.description());
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(SendMessage request, IOException e) {
+ logger.warn("telegram failure", e);
+ }
+ });
+ }
+
+ 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);
+ }
+ });
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/TopManager.java b/juick-server/src/main/java/com/juick/server/TopManager.java
new file mode 100644
index 00000000..77518d37
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/TopManager.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server;
+
+import com.juick.Message;
+import com.juick.service.MessagesService;
+import com.juick.util.MessageUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpClientErrorException;
+
+import javax.inject.Inject;
+
+@Component
+public class TopManager {
+ private static Logger logger = LoggerFactory.getLogger(TopManager.class);
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private FacebookPageManager facebookPageManager;
+
+ @Scheduled(fixedRate = 3600000)
+ public void updateTop() {
+ messagesService.getPopularCandidates().forEach(m -> {
+ logger.info("added {} to popular", m);
+ messagesService.setMessagePopular(m, 1);
+ Message jmsg = messagesService.getMessage(m);
+ String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
+ try {
+ facebookPageManager.post(status, "https://juick.com/" + jmsg.getMid());
+ } catch (HttpClientErrorException ex) {
+ HttpStatus statusCode = ex.getStatusCode();
+ String responseString = ex.getResponseBodyAsString();
+ logger.warn("facebook error {}: {}", statusCode.value(), responseString);
+ }
+ });
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/WebsocketManager.java b/juick-server/src/main/java/com/juick/server/WebsocketManager.java
new file mode 100644
index 00000000..6e3fbea2
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/WebsocketManager.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server;
+
+import com.juick.User;
+import com.juick.server.helpers.AnonymousUser;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.PingMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by vitalyster on 28.06.2016.
+ */
+public class WebsocketManager extends TextWebSocketHandler {
+ private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class);
+
+ private final List clients = Collections.synchronizedList(new LinkedList<>());
+
+ @Inject
+ private UserService userService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+
+
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+ URI hLocation;
+ String hXRealIP;
+
+ hLocation = session.getUri();
+ HttpHeaders headers = session.getHandshakeHeaders();
+ hXRealIP = headers.getOrDefault("X-Real-IP",
+ Collections.singletonList(session.getRemoteAddress().toString())).get(0);
+
+ // Auth
+ User visitor = AnonymousUser.INSTANCE;
+ UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build();
+ List hash = uriComponents.getQueryParams().get("hash");
+ if (hash != null && hash.get(0).length() == 16) {
+ visitor = userService.getUserByHash(hash.get(0));
+ } else {
+ logger.debug("wrong hash for {} from {}", visitor.getUid(), hXRealIP);
+ }
+
+ int MID = 0;
+ SocketSubscribed sockSubscr = null;
+ if (hLocation.getPath().equals("/ws/")) {
+ logger.debug("user {} connected", visitor.getUid());
+ sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false);
+ } else if (hLocation.getPath().equals("/ws/_all")) {
+ logger.debug("user {} connected to legacy _all ({})", visitor.getUid(), hLocation.getPath());
+ sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
+ sockSubscr.allMessages = true;
+ } else if (hLocation.getPath().equals("/ws/_replies")) {
+ logger.debug("user {} connected to legacy _replies ({})", visitor.getUid(), hLocation.getPath());
+ sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
+ sockSubscr.allReplies = true;
+ } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) {
+ MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0);
+ if (MID > 0) {
+ if (messagesService.canViewThread(MID, visitor.getUid())) {
+ logger.debug("user {} connected to legacy thread ({}) from {}", visitor.getUid(), MID, hXRealIP);
+ sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true);
+ sockSubscr.MID = MID;
+ } else {
+ throw new HttpForbiddenException();
+ }
+ }
+ } else {
+ throw new HttpNotFoundException();
+ }
+ if (sockSubscr != null) {
+ synchronized (clients) {
+ clients.add(sockSubscr);
+ logger.debug("{} clients connected", clients.size());
+ }
+ } else {
+ throw new HttpBadRequestException();
+ }
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+ synchronized (clients) {
+ logger.debug("session closed with status {}: {}", status.getCode(), status.getReason());
+ clients.removeIf(c -> c.session.getId().equals(session.getId()));
+ logger.debug("{} clients connected", clients.size());
+ }
+
+ }
+
+ @Scheduled(fixedRate = 30000)
+ public void ping() {
+ clients.forEach(c -> {
+ try {
+ c.session.sendMessage(new PingMessage());
+ } catch (IOException e) {
+ logger.error("WebSocket PING exception", e);
+ }
+ });
+ }
+ public List getClients() {
+ return clients;
+ }
+
+ class SocketSubscribed {
+ WebSocketSession session;
+ String clientName;
+ User visitor;
+ int MID;
+ boolean allMessages;
+ boolean allReplies;
+ long tsConnected;
+ long tsLastData;
+ boolean legacy;
+
+ public SocketSubscribed(WebSocketSession session, String clientName, User visitor, boolean legacy) {
+ this.session = session;
+ this.clientName = clientName;
+ this.visitor = visitor;
+ tsConnected = tsLastData = System.currentTimeMillis();
+ this.legacy = legacy;
+ }
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Index.java b/juick-server/src/main/java/com/juick/server/api/Index.java
new file mode 100644
index 00000000..dba8357d
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Index.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.Status;
+import com.juick.server.WebsocketManager;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.net.URI;
+
+/**
+ * Created by vitalyster on 25.07.2016.
+ */
+@ApiIgnore
+@RestController
+public class Index {
+ @Inject
+ private WebsocketManager wsHandler;
+
+ @RequestMapping(value = "/", method = RequestMethod.GET, headers = "Connection!=Upgrade")
+ public ResponseEntity description() {
+ URI redirectUri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/swagger-ui.html").build().toUri();
+ return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).location(redirectUri).build();
+ }
+
+ @RequestMapping(value = "/api/status", method = RequestMethod.GET,
+ produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade")
+ public Status status() {
+ return Status.getStatus(String.valueOf(wsHandler.getClients().size()));
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Messages.java b/juick-server/src/main/java/com/juick/server/api/Messages.java
new file mode 100644
index 00000000..21156d79
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Messages.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.Status;
+import com.juick.Tag;
+import com.juick.User;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.UserUtils;
+import com.juick.service.MessagesService;
+import com.juick.service.TagService;
+import com.juick.service.UserService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author ugnich
+ */
+@RestController
+@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+public class Messages {
+
+ private static final ResponseEntity> NOT_FOUND = ResponseEntity
+ .status(HttpStatus.NOT_FOUND)
+ .body(Collections.emptyList());
+
+ private static final ResponseEntity> FORBIDDEN = ResponseEntity
+ .status(HttpStatus.FORBIDDEN)
+ .body(Collections.emptyList());
+
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private UserService userService;
+ @Inject
+ private TagService tagService;
+
+ // TODO: serialize image urls
+
+ @RequestMapping("/home")
+ public ResponseEntity> getHome(
+ @RequestParam(defaultValue = "0") int before_mid) {
+ User visitor = UserUtils.getCurrentUser();
+ if (!visitor.isAnonymous()) {
+ int vuid = visitor.getUid();
+ List mids = messagesService.getMyFeed(vuid, before_mid, true);
+
+ if (!mids.isEmpty())
+ return ResponseEntity.ok(messagesService.getMessages(mids));
+
+ return NOT_FOUND;
+ }
+ return FORBIDDEN;
+ }
+
+ @RequestMapping("/messages")
+ public ResponseEntity> getMessages(
+ @RequestParam(required = false) String uname,
+ @RequestParam(name = "before_mid", defaultValue = "0") Integer before,
+ @RequestParam(required = false, defaultValue = "0") Integer daysback,
+ @RequestParam(required = false) String withrecommended,
+ @RequestParam(required = false) String popular,
+ @RequestParam(required = false) String media,
+ @RequestParam(required = false) String tag) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+
+ List mids;
+ if (!StringUtils.isEmpty(uname)) {
+ User user = userService.getUserByName(uname);
+ if (user != null) {
+ if (!StringUtils.isEmpty(media)) {
+ mids = messagesService.getUserPhotos(user.getUid(), 0, before);
+ } else if (!StringUtils.isEmpty(tag)) {
+ Tag tagObject = tagService.getTag(tag, false);
+ if (tagObject != null) {
+ mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before);
+ } else {
+ return NOT_FOUND;
+ }
+ } else if (!StringUtils.isEmpty(withrecommended)) {
+ mids = messagesService.getUserBlogWithRecommendations(user.getUid(), 0, before);
+ } else if (daysback > 0) {
+ mids = messagesService.getUserBlogAtDay(user.getUid(), 0, daysback);
+ } else {
+ mids = messagesService.getUserBlog(user.getUid(), 0, before);
+ }
+ } else {
+ return NOT_FOUND;
+ }
+ } else {
+ if (!StringUtils.isEmpty(popular)) {
+ mids = messagesService.getPopular(vuid, before);
+ } else if (!StringUtils.isEmpty(media)) {
+ mids = messagesService.getPhotos(vuid, before);
+ } else if (!StringUtils.isEmpty(tag)) {
+ Tag tagObject = tagService.getTag(tag, false);
+ if (tagObject != null) {
+ mids = messagesService.getTag(tagObject.TID, vuid, before, 20);
+ } else {
+ return NOT_FOUND;
+ }
+ } else {
+ mids = messagesService.getAll(vuid, before);
+ }
+ }
+ return ResponseEntity.ok(messagesService.getMessages(mids));
+ }
+
+ @GetMapping("/messages/notifications")
+ public ResponseEntity> getNotifications(
+ @RequestParam(required = false) Long before
+ ) {
+ User visitor = UserUtils.getCurrentUser();
+ LocalDateTime beforeTime = before != null ?
+ LocalDateTime.ofInstant(Instant.ofEpochMilli(before), ZoneId.systemDefault())
+ : null;
+ return ResponseEntity.ok(messagesService.getNotifications(visitor, beforeTime));
+ }
+ @RequestMapping("/thread")
+ public ResponseEntity> getThread(
+ @RequestParam(defaultValue = "0") int mid) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ com.juick.Message msg = messagesService.getMessage(mid);
+ if (msg != null) {
+ if (!messagesService.canViewThread(mid, vuid)) {
+ return FORBIDDEN;
+ } else {
+ List replies = messagesService.getReplies(mid);
+ replies.add(0, msg);
+ return ResponseEntity.ok(replies);
+ }
+ }
+ return NOT_FOUND;
+ }
+
+ @ApiIgnore
+ @RequestMapping("/messages/set_privacy")
+ @ResponseBody
+ public ResponseEntity doSetPrivacy(
+ @RequestParam(defaultValue = "0") int mid) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ com.juick.User user = messagesService.getMessageAuthor(mid);
+ if (user != null && user.getUid() == vuid && messagesService.setMessagePrivacy(mid)) {
+ return ResponseEntity.ok(Status.OK);
+ }
+ throw new HttpForbiddenException();
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Notifications.java b/juick-server/src/main/java/com/juick/server/api/Notifications.java
new file mode 100644
index 00000000..5f849080
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Notifications.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.Message;
+import com.juick.Status;
+import com.juick.ExternalToken;
+import com.juick.User;
+import com.juick.server.helpers.AnonymousUser;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.service.MessagesService;
+import com.juick.service.PushQueriesService;
+import com.juick.service.SubscriptionService;
+import com.juick.server.util.UserUtils;
+import com.juick.service.UserService;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Created by vitalyster on 24.10.2016.
+ */
+@RestController
+public class Notifications {
+
+ @Inject
+ private PushQueriesService pushQueriesService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private UserService userService;
+
+
+ private User collectTokens(Integer uid) {
+ User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE);
+ pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null)));
+ pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null)));
+ pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null)));
+ return user;
+ }
+
+ @ApiIgnore
+ @RequestMapping(value = "/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity> doGet(
+ @RequestParam(required = false, defaultValue = "0") int uid,
+ @RequestParam(required = false, defaultValue = "0") int mid,
+ @RequestParam(required = false, defaultValue = "0") int rid) {
+ User visitor = UserUtils.getCurrentUser();
+ if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) {
+ throw new HttpForbiddenException();
+ }
+ if (uid > 0 && mid == 0) {
+ // PM
+ return ResponseEntity.ok(Collections.singletonList(collectTokens(uid)));
+ } else {
+ if (mid > 0) {
+ Message msg = messagesService.getMessage(mid);
+ if (msg != null) {
+ List users;
+ if (rid > 0) {
+ Message reply = messagesService.getReply(mid, rid);
+ users = subscriptionService.getUsersSubscribedToComments(mid, reply.getUser().getUid());
+ } else {
+ users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), mid);
+ }
+
+ return ResponseEntity.ok(users.stream().map(User::getUid)
+ .map(this::collectTokens).collect(Collectors.toList()));
+ }
+ }
+ }
+ throw new HttpBadRequestException();
+ }
+
+ @ApiIgnore
+ @RequestMapping(value = "/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doDelete(
+ @RequestBody List list) throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+ // FIXME: it is possible to delete other user's tokens
+ if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) {
+ throw new HttpForbiddenException();
+ }
+ list.forEach(t -> {
+ switch (t.getType()) {
+ case "gcm":
+ pushQueriesService.deleteGCMToken(t.getToken());
+ break;
+ case "apns":
+ pushQueriesService.deleteAPNSToken(t.getToken());
+ break;
+ case "mpns":
+ pushQueriesService.deleteMPNSToken(t.getToken());
+ break;
+ default:
+ throw new HttpBadRequestException();
+ }
+ });
+
+ return Status.OK;
+ }
+
+ @ApiIgnore
+ @RequestMapping(value = "/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doPut(
+ @RequestBody List list) throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+ if (visitor.getUid() == 0) {
+ throw new HttpForbiddenException();
+ }
+ list.forEach(t -> {
+ switch (t.getType()) {
+ case "gcm":
+ pushQueriesService.addGCMToken(visitor.getUid(), t.getToken());
+ break;
+ case "apns":
+ pushQueriesService.addAPNSToken(visitor.getUid(), t.getToken());
+ break;
+ case "mpns":
+ pushQueriesService.addMPNSToken(visitor.getUid(), t.getToken());
+ break;
+ default:
+ throw new HttpBadRequestException();
+ }
+ });
+ return Status.OK;
+ }
+
+ @Deprecated
+ @RequestMapping(value = "/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doAndroidRegister(
+ @RequestParam(name = "regid") String regId) {
+ User visitor = UserUtils.getCurrentUser();
+ if (visitor.getUid() == 0) {
+ throw new HttpForbiddenException();
+ }
+ pushQueriesService.addGCMToken(visitor.getUid(), regId);
+ return Status.OK;
+ }
+
+ @Deprecated
+ @RequestMapping(value = "/android/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doAndroidUnRegister(@RequestParam(name = "regid") String regId) {
+ pushQueriesService.deleteGCMToken(regId);
+ return Status.OK;
+ }
+
+ @Deprecated
+ @RequestMapping(value = "/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doWinphoneRegister(
+ Principal principal,
+ @RequestParam(name = "url") String regId) {
+ User visitor = UserUtils.getCurrentUser();
+ pushQueriesService.addMPNSToken(visitor.getUid(), regId);
+ return Status.OK;
+ }
+
+ @Deprecated
+ @RequestMapping(value = "/winphone/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Status doWinphoneUnRegister(@RequestParam(name = "url") String regId) {
+ pushQueriesService.deleteMPNSToken(regId);
+ return Status.OK;
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/PM.java b/juick-server/src/main/java/com/juick/server/api/PM.java
new file mode 100644
index 00000000..4c3128ed
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/PM.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.User;
+import com.juick.server.ServerManager;
+import com.juick.server.helpers.AnonymousUser;
+import com.juick.server.helpers.PrivateChats;
+import com.juick.server.util.*;
+import com.juick.service.PMQueriesService;
+import com.juick.service.UserService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import rocks.xmpp.addr.Jid;
+import rocks.xmpp.core.stanza.model.Message;
+
+import javax.inject.Inject;
+import java.util.List;
+
+/**
+ * @author ugnich
+ */
+@RestController
+public class PM {
+ @Inject
+ private UserService userService;
+ @Inject
+ private PMQueriesService pmQueriesService;
+ @Inject
+ private ServerManager serverManager;
+
+ @RequestMapping(value = "/pm", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List doGetPM(
+ @RequestParam(required = false) String uname) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ int uid = 0;
+ if (uname != null && uname.matches("^[a-zA-Z0-9\\-]{2,16}$")) {
+ uid = userService.getUIDbyName(uname);
+ }
+
+ if (uid == 0) {
+ throw new HttpBadRequestException();
+ }
+
+ return pmQueriesService.getPMMessages(vuid, uid);
+ }
+
+ @RequestMapping(value = "/pm", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public com.juick.Message doPostPM(
+ @RequestParam String uname,
+ @RequestParam String body) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ User userTo = AnonymousUser.INSTANCE;
+ if (WebUtils.isUserName(uname)) {
+ userTo = userService.getUserByName(uname);
+ }
+
+ if (userTo.getUid() == 0 || body == null || body.length() < 1 || body.length() > 10240) {
+ throw new HttpBadRequestException();
+ }
+
+ if (userService.isInBLAny(userTo.getUid(), vuid)) {
+ throw new HttpForbiddenException();
+ }
+
+ if (pmQueriesService.createPM(vuid, userTo.getUid(), body)) {
+ Message msg = new Message();
+ msg.setFrom(Jid.of("juick@juick.com"));
+ msg.setTo(Jid.of(String.format("%d@push.juick.com", userTo.getUid())));
+ com.juick.Message jmsg = new com.juick.Message();
+ jmsg.setUser(visitor);
+ jmsg.setText(body);
+ jmsg.setTo(userTo);
+ msg.addExtension(jmsg);
+ serverManager.sendMessage(msg);
+
+ msg.setTo(Jid.of(String.format("%d@ws.juick.com", userTo.getUid())));
+ serverManager.sendMessage(msg);
+
+ List jids = userService.getJIDsbyUID(userTo.getUid());
+ for (String jid : jids) {
+ Message mm = new Message();
+ mm.setTo(Jid.of(jid));
+ mm.setType(Message.Type.CHAT);
+ if (pmQueriesService.havePMinRoster(vuid, jid)) {
+ mm.setFrom(Jid.of(jmsg.getUser().getName(), "juick.com", "Juick"));
+ mm.setBody(body);
+ } else {
+ mm.setFrom(Jid.of("juick", "juick.com", "Juick"));
+ mm.setBody("Private message from @" + jmsg.getUser().getName() + ":\n" + body);
+ }
+ serverManager.sendMessage(mm);
+ }
+ return jmsg;
+
+ }
+ throw new HttpBadRequestException();
+ }
+ @RequestMapping(value = "groups_pms", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public PrivateChats doGetGroupsPMs(
+ @RequestParam(defaultValue = "5") int cnt) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ if (cnt < 3) {
+ cnt = 3;
+ }
+ if (cnt > 10) {
+ cnt = 10;
+ }
+
+ List lastconv = pmQueriesService.getPMLastConversationsUsers(vuid, cnt);
+ if (lastconv != null && !lastconv.isEmpty()) {
+ PrivateChats pms = new PrivateChats();
+ pms.setUsers(lastconv);
+ return pms;
+ }
+ throw new HttpNotFoundException();
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Post.java b/juick-server/src/main/java/com/juick/server/api/Post.java
new file mode 100644
index 00000000..7f014d9e
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Post.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.User;
+import com.juick.server.ServerManager;
+import com.juick.server.EmailManager;
+import com.juick.server.util.*;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.mail.util.MimeMessageParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import rocks.xmpp.addr.Jid;
+import rocks.xmpp.core.stanza.model.Message;
+import rocks.xmpp.extensions.nick.model.Nickname;
+import rocks.xmpp.extensions.oob.model.x.OobX;
+
+import javax.inject.Inject;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.util.Properties;
+import java.util.Scanner;
+import java.util.UUID;
+
+/**
+ * Created by vt on 24/11/2016.
+ */
+@RestController
+public class Post {
+ private static Logger logger = LoggerFactory.getLogger(ServerManager.class);
+
+ @Inject
+ private UserService userService;
+ @Inject
+ private ServerManager serverManager;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}")
+ private String tmpDir;
+ @Value("${img_path:/var/www/juick.com/i/}")
+ private String imgDir;
+
+ @RequestMapping(value = "/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void doPostMessage(
+ @RequestParam String body,
+ @RequestParam(required = false) String img,
+ @RequestParam(required = false) MultipartFile attach) throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+
+ if (visitor.isAnonymous())
+ throw new HttpForbiddenException();
+
+ if (body == null || body.length() < 1 || body.length() > 4096) {
+ throw new HttpBadRequestException();
+ }
+ body = body.replace("\r", StringUtils.EMPTY);
+
+ String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
+
+ if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
+ try {
+ URL imgUrl = new URL(img);
+ attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
+ } catch (Exception e) {
+ logger.error("DOWNLOAD ERROR", e);
+ throw new HttpBadRequestException();
+ }
+ }
+ serverManager.processMessage(visitor, body, attachmentFName);
+ }
+
+ @RequestMapping(value = "/comment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public com.juick.Message doPostComment(
+ @RequestParam(defaultValue = "0") int mid,
+ @RequestParam(defaultValue = "0") int rid,
+ @RequestParam String body,
+ @RequestParam(required = false) String img,
+ @RequestParam(required = false) MultipartFile attach)
+ throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ if (mid == 0) {
+ throw new HttpBadRequestException();
+ }
+ com.juick.Message msg = messagesService.getMessage(mid);
+ if (msg == null) {
+ throw new HttpNotFoundException();
+ }
+
+ com.juick.Message reply = null;
+ if (rid > 0) {
+ reply = messagesService.getReply(mid, rid);
+ if (reply == null) {
+ throw new HttpNotFoundException();
+ }
+ }
+
+ if (body == null || body.length() < 1 || body.length() > 4096) {
+ throw new HttpBadRequestException();
+ }
+ body = body.replace("\r", StringUtils.EMPTY);
+
+ if ((msg.ReadOnly && msg.getUser().getUid() != vuid) || userService.isInBLAny(msg.getUser().getUid(), vuid)
+ || (reply != null && userService.isInBLAny(reply.getUser().getUid(), vuid))) {
+ throw new HttpForbiddenException();
+ }
+
+ String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
+
+ if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
+ try {
+ attachmentFName = HttpUtils.downloadImage(new URL(img), tmpDir);
+ } catch (Exception e) {
+ logger.error("DOWNLOAD ERROR", e);
+ throw new HttpBadRequestException();
+ }
+ }
+
+ String attachmentType = StringUtils.isNotEmpty(attachmentFName) ? attachmentFName.substring(attachmentFName.length() - 3) : null;
+ int ridnew = messagesService.createReply(mid, rid, vuid, body, attachmentType);
+ subscriptionService.subscribeMessage(mid, vuid);
+
+ com.juick.Message jmsg = messagesService.getReply(mid, ridnew);
+
+ Message xmsg = new Message();
+ xmsg.setFrom(Jid.of("juick@juick.com"));
+ xmsg.setType(Message.Type.CHAT);
+ xmsg.setThread("juick-" + mid);
+ xmsg.addExtension(jmsg);
+
+ String quote = reply != null ? StringUtils.defaultString(reply.getText()) : StringUtils.defaultString(msg.getText());
+ if (quote.length() >= 50) {
+ quote = quote.substring(0, 47) + "...";
+ }
+
+ xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName()));
+
+ if (StringUtils.isNotEmpty(attachmentFName)) {
+ String fname = mid + "-" + ridnew + "." + attachmentType;
+ String attachmentURL = "http://i.juick.com/photos-1024/" + fname;
+
+ ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir);
+
+ body = attachmentURL + "\n" + body;
+ try {
+ xmsg.addExtension(new OobX(new URI(attachmentURL)));
+ } catch (URISyntaxException e) {
+ logger.error("invalid uri: {}, exception {}", attachmentURL, e);
+ }
+ }
+
+ xmsg.setBody("Reply by @" + jmsg.getUser().getName() + ":\n>" + quote + "\n" + body + "\n\n#" +
+ mid + "/" + ridnew + " http://juick.com/" + mid + "#" + ridnew);
+
+ xmsg.setTo(Jid.of("juick@s2s.juick.com"));
+ serverManager.sendMessage(xmsg);
+
+ xmsg.setTo(Jid.of("juick@ws.juick.com"));
+ serverManager.sendMessage(xmsg);
+
+ xmsg.setTo(Jid.of("juick@push.juick.com"));
+ serverManager.sendMessage(xmsg);
+ return jmsg;
+ }
+
+ Session session = Session.getDefaultInstance(new Properties());
+
+ @PostMapping("/mail")
+ @ResponseStatus(value = HttpStatus.OK)
+ public void processMail(InputStream data) throws Exception {
+ MimeMessage msg = new MimeMessage(session, data);
+ String from = msg.getFrom().length > 1 ? ((InternetAddress) msg.getSender()).getAddress()
+ : ((InternetAddress) msg.getFrom()[0]).getAddress();
+ logger.info("got msg from {}", from);
+
+ User visitor = userService.getUserByEmail(from);
+ if (!visitor.isAnonymous()) {
+ MimeMessageParser parser = new MimeMessageParser(msg);
+ parser.parse();
+ final String[] body = {parser.getPlainContent()};
+ if (body[0] == null) {
+ parser.getAttachmentList().stream()
+ .filter(a -> a.getContentType().equals("text/plain")).findFirst()
+ .ifPresent(a -> {
+ try {
+ body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8);
+ logger.info("got text: {}", body[0]);
+ } catch (IOException e) {
+ logger.info("attachment error: {}", e);
+ }
+ });
+ }
+ final String[] attachmentFName = new String[1];
+ parser.getAttachmentList().stream().filter(a ->
+ a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png"))
+ .findFirst().ifPresent(a -> {
+ logger.info("got attachment: {}", a.getContentType());
+ String attachmentType;
+ if (a.getContentType().equals("image/jpeg")) {
+ attachmentType = "jpg";
+ } else {
+ attachmentType = "png";
+ }
+ attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
+ try {
+ logger.info("got inputstream: {}", a.getInputStream());
+ FileOutputStream fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName[0]).toString());
+ IOUtils.copy(a.getInputStream(), fos);
+ fos.close();
+ } catch (IOException e) {
+ logger.info("attachment error: {}", e);
+ }
+ });
+ String[] inReplyToHeaders = msg.getHeader("In-Reply-To");
+ if (inReplyToHeaders != null && inReplyToHeaders.length > 0) {
+ Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()).useDelimiter(EmailManager.MSGID_PATTERN);
+ int mid = Integer.parseInt(inReplyToScanner.next());
+ int rid = Integer.parseInt(inReplyToScanner.next());
+ logger.info("Message is reply to #{}/{}", mid, rid);
+ body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0])
+ : String.format("#%d %s", mid, body[0]);
+ }
+ rocks.xmpp.core.stanza.model.Message xmsg = new rocks.xmpp.core.stanza.model.Message();
+ xmsg.setType(rocks.xmpp.core.stanza.model.Message.Type.CHAT);
+ xmsg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "mail"));
+ xmsg.setTo(Jid.of("juick@juick.com/Juick"));
+ xmsg.setBody(body[0]);
+ try {
+ if (StringUtils.isNotEmpty(attachmentFName[0])) {
+ String attachmentUrl = String.format("juick://%s", attachmentFName[0]);
+ xmsg.addExtension(new OobX(new URI(attachmentUrl), "!!!!Juick!!"));
+ }
+ serverManager.sendMessage(xmsg);
+ } catch (URISyntaxException e1) {
+ logger.warn("attachment error", e1);
+ }
+ } else {
+ logger.info("not registered: {}", from);
+ }
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Service.java b/juick-server/src/main/java/com/juick/server/api/Service.java
new file mode 100644
index 00000000..12ffee9c
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Service.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.Message;
+import com.juick.User;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.UserUtils;
+import com.juick.service.CrosspostService;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * TODO: configure spring-security to allow only admin role
+ */
+@ApiIgnore
+@RestController
+public class Service {
+ @Inject
+ private SubscriptionService subscriptionService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private CrosspostService crosspostService;
+ @Inject
+ private UserService userService;
+
+ @RequestMapping(value = "/subscriptions", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List doGet(
+ @RequestParam(defaultValue = "0") int mid,
+ @RequestParam(defaultValue = "0") int uid) throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+ if ((visitor.getUid() == 0) && !(visitor.getName().equals("juick"))) {
+ throw new HttpForbiddenException();
+ }
+ if (uid > 0) {
+ return subscriptionService.getSubscribedUsers(uid, mid);
+ } else {
+ // thread
+ Message msg = messagesService.getMessage(mid);
+ if (msg != null) {
+ return subscriptionService.getUsersSubscribedToComments(mid, msg.getUser().getUid());
+ }
+ }
+ throw new HttpBadRequestException();
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Tags.java b/juick-server/src/main/java/com/juick/server/api/Tags.java
new file mode 100644
index 00000000..8ced4ec9
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Tags.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.User;
+import com.juick.server.helpers.TagStats;
+import com.juick.server.util.UserUtils;
+import com.juick.service.TagService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.inject.Inject;
+import java.util.List;
+
+/**
+ * Created by vitalyster on 29.11.2016.
+ */
+@RestController
+public class Tags {
+ @Inject
+ private TagService tagService;
+
+ @RequestMapping(value = "/tags", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List tags(
+ @RequestParam(required = false, defaultValue = "0") int user_id
+ ) {
+ User visitor = UserUtils.getCurrentUser();
+ if (user_id == 0) {
+ user_id = visitor.getUid();
+ }
+ if (user_id > 0) {
+ return tagService.getUserTagStats(user_id);
+ }
+ return tagService.getTagStats();
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/Users.java b/juick-server/src/main/java/com/juick/server/api/Users.java
new file mode 100644
index 00000000..8b273354
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/Users.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api;
+
+import com.juick.User;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.service.UserService;
+import com.juick.server.util.UserUtils;
+import com.juick.server.util.WebUtils;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author ugnich
+ */
+@RestController
+public class Users {
+ @Inject
+ private UserService userService;
+
+ @RequestMapping(value = "/auth", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public String getAuthToken() {
+ return userService.getHashByUID(UserUtils.getCurrentUser().getUid());
+ }
+
+ @RequestMapping(value = "/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List doGetUsers(
+ @RequestParam(value = "uname", required = false) List unames) {
+ List users = new ArrayList<>();
+
+ if (unames != null) {
+ unames.removeIf(WebUtils::isNotUserName);
+
+ if (!unames.isEmpty() && unames.size() < 20)
+ users.addAll(userService.getUsersByName(unames));
+ }
+
+ if (!users.isEmpty())
+ return users;
+ if (!UserUtils.getCurrentUser().isAnonymous()) {
+ return Collections.singletonList(UserUtils.getCurrentUser());
+ }
+
+ throw new HttpNotFoundException();
+ }
+
+ @RequestMapping(value = "/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List doGetUserRead(
+ @RequestParam String uname) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ int uid = 0;
+ if (uname == null) {
+ uid = vuid;
+ } else {
+ if (WebUtils.isUserName(uname)) {
+ com.juick.User u = userService.getUserByName(uname);
+ if (u != null && u.getUid() > 0) {
+ uid = u.getUid();
+ }
+ }
+ }
+
+ if (uid > 0) {
+ List uids = userService.getUserRead(uid);
+ if (uids.size() > 0) {
+ List users = userService.getUsersByID(uids);
+ if (users.size() > 0) {
+ return users;
+ }
+ }
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @RequestMapping(value = "/users/readers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public List doGetUserReaders(
+ @RequestParam String uname) {
+ User visitor = UserUtils.getCurrentUser();
+ int vuid = visitor.getUid();
+ if (vuid == 0) {
+ throw new HttpForbiddenException();
+ }
+ int uid = 0;
+ if (uname == null) {
+ uid = vuid;
+ } else {
+ if (WebUtils.isUserName(uname)) {
+ com.juick.User u = userService.getUserByName(uname);
+ if (u != null && u.getUid() > 0) {
+ uid = u.getUid();
+ }
+ }
+ }
+
+ if (uid > 0) {
+ return userService.getUserReaders(uid);
+ }
+ throw new HttpNotFoundException();
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/webhooks/MessengerWebhook.java b/juick-server/src/main/java/com/juick/server/api/webhooks/MessengerWebhook.java
new file mode 100644
index 00000000..e746b67d
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/webhooks/MessengerWebhook.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api.webhooks;
+
+import com.github.messenger4j.exception.MessengerVerificationException;
+import com.juick.server.MessengerManager;
+import com.juick.server.util.HttpForbiddenException;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Created by vitalyster on 27.03.2017.
+ */
+@ApiIgnore
+@RestController
+public class MessengerWebhook {
+ private static Logger logger = LoggerFactory.getLogger(MessengerWebhook.class);
+
+ @Inject
+ private MessengerManager messengerManager;
+
+ @RequestMapping(value = "/fbwbhk", method = RequestMethod.GET)
+ public ResponseEntity verifyHook(@RequestParam(name = "hub.mode") String hubMode,
+ @RequestParam(name = "hub.challenge") Integer hubChallenge,
+ @RequestParam(name = "hub.verify_token") String verifyToken) {
+ if (hubMode.equals("subscribe") && verifyToken.equals(messengerManager.getFacebookVerifyToken())) {
+ return new ResponseEntity<>(hubChallenge, HttpStatus.OK);
+ }
+ throw new HttpForbiddenException();
+ }
+ @RequestMapping(value = "/fbwbhk", method = RequestMethod.POST)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void processUpdate(@RequestHeader(name = "X-Hub-Signature", required = false) String signature, InputStream body) throws IOException, MessengerVerificationException {
+ String data = IOUtils.toString(body, StandardCharsets.UTF_8);
+ messengerManager.processUpdate(signature, data);
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/webhooks/SkypeWebhook.java b/juick-server/src/main/java/com/juick/server/api/webhooks/SkypeWebhook.java
new file mode 100644
index 00000000..425a9d10
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/webhooks/SkypeWebhook.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api.webhooks;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Created by vitalyster on 18.07.2016.
+ */
+@ApiIgnore
+@RestController
+public class SkypeWebhook {
+ private static final Logger logger = LoggerFactory.getLogger(SkypeWebhook.class);
+ @RequestMapping(value = "/skypebotendpoint", method = RequestMethod.POST)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void doPost(InputStream body) throws IOException {
+ String data = IOUtils.toString(body, StandardCharsets.UTF_8);
+ logger.info("got data: {}", data);
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java b/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java
new file mode 100644
index 00000000..9c4f64ce
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.api.webhooks;
+
+import com.juick.server.TelegramBotManager;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.inject.Inject;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Created by vt on 24/11/2016.
+ */
+@ApiIgnore
+@RestController
+public class TelegramWebhook {
+ @Inject
+ private TelegramBotManager telegramBotManager;
+
+ @RequestMapping(value = "/tlgmbtwbhk", method = RequestMethod.POST)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void processUpdate(InputStream body) throws Exception {
+ String data = IOUtils.toString(body, StandardCharsets.UTF_8);
+ telegramBotManager.processUpdate(data);
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
new file mode 100644
index 00000000..3839248d
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.configuration;
+
+import com.juick.server.ServerManager;
+import com.juick.server.WebsocketManager;
+import com.juick.server.component.JuickServerComponent;
+import com.juick.server.component.JuickServerReconnectManager;
+import com.juick.service.UserService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.*;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.socket.client.WebSocketConnectionManager;
+import org.springframework.web.socket.client.standard.StandardWebSocketClient;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
+import org.springframework.web.util.UriComponentsBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import javax.annotation.Nonnull;
+import javax.inject.Inject;
+import java.util.Collections;
+
+/**
+ * Created by aalexeev on 11/12/16.
+ */
+@Configuration
+@EnableAsync
+@EnableWebMvc
+@EnableSwagger2
+@EnableScheduling
+@EnableWebSocket
+@PropertySource("classpath:juick.conf")
+@ComponentScan(basePackages = "com.juick.server")
+public class ApiAppConfiguration extends BaseWebConfiguration implements WebSocketConfigurer {
+ @Inject
+ UserService userService;
+ @Value("${api_user:juick}")
+ private String serviceUser;
+ @Value("${websocket_url:ws://localhost:8080/ws/}")
+ private String baseUri;
+ @Lazy
+ @Bean
+ public JuickServerComponent juickServerComponent() {
+ return new JuickServerComponent();
+ }
+ @Lazy
+ @Bean
+ public JuickServerReconnectManager juickServerReconnectManager() {
+ return new JuickServerReconnectManager();
+ }
+ @Bean
+ public WebSocketConnectionManager connectionManager() {
+ String websocketURI = UriComponentsBuilder.fromUriString(baseUri)
+ .queryParam("hash", userService.getHashByUID(userService.getUIDbyName(serviceUser))).build().toUriString();
+ return new WebSocketConnectionManager(client(), juickServerComponent(), websocketURI);
+ }
+ @Bean
+ public StandardWebSocketClient client() {
+ return new StandardWebSocketClient();
+ }
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any()).build().apiInfo(new ApiInfo("Juick API", "Juick REST API Documentation",
+ "2.0", "https://juick.com/help/tos", null,
+ "AGPLv3", "https://www.gnu.org/licenses/agpl-3.0.html", Collections.emptyList()));
+ }
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/swagger-ui.html")
+ .addResourceLocations("classpath:/META-INF/resources/");
+
+ registry.addResourceHandler("/webjars/**")
+ .addResourceLocations("classpath:/META-INF/resources/webjars/");
+ }
+ @Bean
+ public WebsocketManager wsHandler() {
+ return new WebsocketManager();
+ }
+
+ @Override
+ public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) {
+ //((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.LOWEST_PRECEDENCE);
+ registry.addHandler(wsHandler(), "/ws/**").setAllowedOrigins("*");
+ }
+
+ @Bean
+ public ServletServerContainerFactoryBean createWebSocketContainer() {
+ ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
+ container.setMaxTextMessageBufferSize(8192);
+ container.setMaxBinaryMessageBufferSize(8192);
+ return container;
+ }
+ @Override
+ public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+ configurer.enable();
+ }
+
+}
diff --git a/juick-server/src/main/java/com/juick/server/configuration/ApiInitializer.java b/juick-server/src/main/java/com/juick/server/configuration/ApiInitializer.java
new file mode 100644
index 00000000..b25edd9a
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/configuration/ApiInitializer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.configuration;
+
+import com.juick.configuration.DataConfiguration;
+import com.juick.server.configuration.JuickServerWebsocketConfiguration;
+import com.juick.server.configuration.StorageConfiguration;
+import org.apache.commons.codec.CharEncoding;
+import org.springframework.web.filter.CharacterEncodingFilter;
+import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
+
+import javax.annotation.Nonnull;
+import javax.servlet.Filter;
+
+/**
+ * Created by vt on 09/02/16.
+ */
+public class ApiInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
+
+ @Override
+ protected Class>[] getRootConfigClasses() {
+ return new Class>[]{
+ ApiSecurityConfig.class,
+ DataConfiguration.class,
+ StorageConfiguration.class
+ };
+ }
+
+ @Override
+ protected Class>[] getServletConfigClasses() {
+ return null;
+ }
+
+ @Override
+ @Nonnull
+ protected String[] getServletMappings() {
+ return new String[]{"/"};
+ }
+
+ @Override
+ protected Filter[] getServletFilters() {
+ return new Filter[]{new CharacterEncodingFilter(CharEncoding.UTF_8)};
+ }
+
+ @Override
+ @Nonnull
+ protected String getServletName() {
+ return "API dispatcher servlet";
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/configuration/ApiSecurityConfig.java b/juick-server/src/main/java/com/juick/server/configuration/ApiSecurityConfig.java
new file mode 100644
index 00000000..4f7045a6
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/configuration/ApiSecurityConfig.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.configuration;
+
+import com.juick.service.UserService;
+import com.juick.service.security.JuickUserDetailsService;
+import com.juick.service.security.NotAuthorizedAuthenticationEntryPoint;
+import com.juick.service.security.deprecated.RequestParamHashRememberMeServices;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+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.web.authentication.RememberMeServices;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import javax.inject.Inject;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by aalexeev on 11/21/16.
+ */
+@Configuration
+@EnableWebSecurity
+@PropertySource("classpath:juick.conf")
+@Import(ApiAppConfiguration.class)
+public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
+ @Value("${auth_remember_me_key}")
+ private String rememberMeKey;
+ @Inject
+ private UserService userService;
+
+ ApiSecurityConfig() {
+ super(true);
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .antMatchers(HttpMethod.OPTIONS).permitAll()
+ .antMatchers("/messages", "/users", "/thread", "/tags", "/tlgmbtwbhk", "/fbwbhk",
+ "/skypebotendpoint").permitAll()
+ .anyRequest().hasRole("USER")
+ .and().httpBasic().authenticationEntryPoint(getJuickAuthenticationEntryPoint())
+ .and().anonymous()
+ .and().cors().configurationSource(corsConfigurationSource())
+ .and().servletApi()
+ .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and().exceptionHandling().authenticationEntryPoint(getJuickAuthenticationEntryPoint())
+ .and()
+ .rememberMe()
+ .alwaysRemember(true)
+ .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30))
+ .rememberMeServices(rememberMeServices())
+ .key(rememberMeKey)
+ .and().authenticationProvider(authenticationProvider())
+ .headers().defaultsDisabled().cacheControl();
+ }
+
+ @Bean
+ public DaoAuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
+
+ authenticationProvider.setUserDetailsService(userDetailsService());
+
+ return authenticationProvider;
+ }
+
+ @Bean
+ public JuickUserDetailsService userDetailsService() {
+ return new JuickUserDetailsService(userService);
+ }
+
+ @Bean
+ public RememberMeServices rememberMeServices() throws Exception {
+ return new RequestParamHashRememberMeServices(rememberMeKey, userService);
+ }
+
+ @Bean
+ public NotAuthorizedAuthenticationEntryPoint getJuickAuthenticationEntryPoint() {
+ return new NotAuthorizedAuthenticationEntryPoint();
+ }
+
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+
+ configuration.setAllowedOrigins(Collections.singletonList("*"));
+ configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE"));
+ configuration.setAllowedHeaders(Collections.singletonList("*"));
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+
+ return source;
+ }
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
+ "/configuration/**", "/swagger-ui.html", "/webjars/**", "/ws/**");
+ }
+}
diff --git a/juick-server/src/main/java/com/juick/server/util/JsonpAdvice.java b/juick-server/src/main/java/com/juick/server/util/JsonpAdvice.java
new file mode 100644
index 00000000..457a7df7
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/server/util/JsonpAdvice.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.util;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
+
+/**
+ * Created by vitalyster on 25.11.2016.
+ */
+@ControllerAdvice
+public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
+ public JsonpAdvice() {
+ super("callback");
+ }
+}
diff --git a/juick-server/src/test/java/com/juick/api/tests/MessagesTests.java b/juick-server/src/test/java/com/juick/api/tests/MessagesTests.java
deleted file mode 100644
index fff7e02c..00000000
--- a/juick-server/src/test/java/com/juick/api/tests/MessagesTests.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.api.tests;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.ExternalToken;
-import com.juick.Message;
-import com.juick.Tag;
-import com.juick.User;
-import com.juick.api.EmailManager;
-import com.juick.api.configuration.ApiAppConfiguration;
-import com.juick.api.configuration.ApiSecurityConfig;
-import com.juick.configuration.RepositoryConfiguration;
-import com.juick.server.helpers.TagStats;
-import com.juick.service.ImagesService;
-import com.juick.service.MessagesService;
-import com.juick.service.TagService;
-import com.juick.service.UserService;
-import com.juick.util.DateFormattersHolder;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.http.MediaType;
-import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.MvcResult;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.inject.Inject;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Scanner;
-import java.util.stream.IntStream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-/**
- * Created by vitalyster on 25.11.2016.
- */
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes = {ApiAppConfiguration.class, ApiSecurityConfig.class, RepositoryConfiguration.class})
-@WebAppConfiguration
-public class MessagesTests extends AbstractJUnit4SpringContextTests {
-
- private MockMvc mockMvc;
- @Inject
- private WebApplicationContext webApplicationContext;
-
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private TagService tagService;
- @Inject
- private ObjectMapper jsonMapper;
- @Inject
- private ImagesService imagesService;
-
- private static User ugnich, freefd, juick;
- static String ugnichName, ugnichPassword, freefdName, freefdPassword, juickName, juickPassword;
- static Message msg;
- static int juickTagId;
-
- private static boolean isSetUp = false;
-
-
- @Before
- public void setUp() {
- mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
- .apply(SecurityMockMvcConfigurers.springSecurity())
- .dispatchOptions(true)
- .build();
- if (!isSetUp) {
- ugnichName = "ugnich";
- ugnichPassword = "MyPassw0rd!";
- freefdName = "freefd";
- freefdPassword = "MyPassw0rd!";
- juickName = "juick";
- juickPassword = "demo";
- int ugnichId = userService.createUser(ugnichName, ugnichPassword);
- ugnich = userService.getUserByUID(ugnichId).orElseThrow(IllegalStateException::new);
- int freefdId = userService.createUser(freefdName, freefdPassword);
- freefd = userService.getUserByUID(freefdId).orElseThrow(IllegalStateException::new);
- int juickId = userService.createUser(juickName, juickPassword);
- juick = userService.getUserByUID(juickId).orElseThrow(IllegalStateException::new);
-
- String msgText = "Привет, я - Угнич";
-
- int mid = messagesService.createMessage(ugnich.getUid(), msgText, "png", null);
- msg = messagesService.getMessage(mid);
- tagService.createTag("тест");
- juickTagId = tagService.createTag("juick");
- isSetUp = true;
- }
- }
-
- @Test
- public void testAllUnAuthorized() throws Exception {
-
- mockMvc.perform(get("/"))
- .andExpect(status().is4xxClientError());
-
- mockMvc.perform(get("/auth"))
- .andExpect(status().is4xxClientError());
-
- mockMvc.perform(get("/home"))
- .andExpect(status().is4xxClientError());
-
- mockMvc.perform(get("/messages/recommended"))
- .andExpect(status().is4xxClientError());
-
- mockMvc.perform(get("/messages/set_privacy"))
- .andExpect(status().is4xxClientError());
- }
-
- @Test
- public void homeTestWithMessages() throws Exception {
- mockMvc.perform(
- get("/home")
- .with(httpBasic(ugnichName, ugnichPassword)))
- .andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
- .andExpect(jsonPath("$[-1].mid", is(msg.getMid())))
- .andExpect(jsonPath("$[-1].timestamp",
- is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getTimestamp()))))
- .andExpect(jsonPath("$[-1].body", is(msg.getText())))
- .andExpect(jsonPath("$[-1].attachment.url", is("https://i.juick.com/p/1.png")))
- .andExpect(jsonPath("$[-1].attachment.small.url", is("https://i.juick.com/photos-512/1.png")));
- }
-
- @Test
- public void homeTestWithMessagesAndRememberMe() throws Exception {
- String ugnichHash = userService.getHashByUID(ugnich.getUid());
- mockMvc.perform(
- get("/home")
- .with(httpBasic(ugnichName, ugnichPassword)))
- .andExpect(status().isOk())
- .andReturn();
-
- mockMvc.perform(get("/home")
- .param("hash", ugnichHash))
- .andExpect(status().isOk());
- }
-
- @Test
- public void homeTestWithMessagesAndSimpleCors() throws Exception {
- mockMvc.perform(
- get("/home")
- .with(httpBasic(ugnichName, ugnichPassword))
- .header("Origin", "http://api.example.net"))
- .andExpect(status().isOk())
- .andExpect(header().string("Access-Control-Allow-Origin", "*"));
- }
-
- @Test
- public void homeTestWithPreflightCors() throws Exception {
- mockMvc.perform(
- options("/home")
- .with(httpBasic(ugnichName, ugnichPassword))
- .header("Origin", "http://api.example.net")
- .header("Access-Control-Request-Method", "POST")
- .header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type"))
- .andExpect(status().isOk())
- .andExpect(header().string("Access-Control-Allow-Origin", "*"))
- .andExpect(header().string("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE"))
- .andExpect(header().string("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type"));
- }
-
- @Test
- public void anonymousApis() throws Exception {
-
-
- mockMvc.perform(get("/messages"))
- .andExpect(status().isOk());
-
- mockMvc.perform(get("/users")
- .param("uname", "ugnich")
- .param("uname", "freefd"))
- .andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
- .andExpect(jsonPath("$", hasSize(2)));
- }
-
- @Test
- public void tags() throws Exception {
- Tag weather = tagService.getTag("weather", true);
- Tag yo = tagService.getTag("yo", true);
- messagesService.createMessage(ugnich.getUid(), "text", null, Arrays.asList(yo, weather));
- messagesService.createMessage(freefd.getUid(), "text2", null, Collections.singletonList(yo));
- MvcResult result = mockMvc.perform(get("/tags"))
- .andExpect(status().isOk())
- .andReturn();
- List tagsFromApi = jsonMapper.readValue(result.getResponse().getContentAsString(),
- new TypeReference>(){});
- TagStats yoStats = tagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get();
- assertThat(yoStats.getUsageCount(), is(2));
- MvcResult result2 = mockMvc.perform(get("/tags")
- .param("user_id", String.valueOf(ugnich.getUid())))
- .andExpect(status().isOk())
- .andReturn();
- List ugnichTagsFromApi = jsonMapper.readValue(result2.getResponse().getContentAsString(),
- new TypeReference>(){});
- TagStats yoUgnichStats = ugnichTagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get();
- assertThat(yoUgnichStats.getUsageCount(), is(1));
- }
-
- @Test
- public void postWithReferer() throws Exception {
- mockMvc.perform(post("/post")
- .param("body", "yo")
- .with(httpBasic(ugnichName, ugnichPassword)))
- .andExpect(status().isOk());
- }
- @Test
- public void threadWithEphemeralNumberShouldReturn404() throws Exception {
- mockMvc.perform(get("/thread").param("mid", "999999999")
- .with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().is4xxClientError());
- }
- @Test
- public void performRequestsWithIssuedToken() throws Exception {
- String ugnichHash = userService.getHashByUID(ugnich.getUid());
- mockMvc.perform(get("/home")).andExpect(status().isUnauthorized());
- mockMvc.perform(get("/auth"))
- .andExpect(status().isUnauthorized());
- mockMvc.perform(get("/auth").with(httpBasic(ugnichName, "wrongpassword")))
- .andExpect(status().isUnauthorized());
- MvcResult result = mockMvc.perform(get("/auth").with(httpBasic(ugnichName, ugnichPassword)))
- .andExpect(status().isOk())
- .andReturn();
- String authHash = jsonMapper.readValue(result.getResponse().getContentAsString(), String.class);
- assertThat(authHash, equalTo(ugnichHash));
- mockMvc.perform(get("/home").param("hash", ugnichHash)).andExpect(status().isOk());
- }
- @Test
- public void registerForNotificationsTests() throws Exception {
- String token = "123456";
- ExternalToken registration = new ExternalToken(null, "apns", token, null);
- mockMvc.perform(put("/notifications").with(httpBasic(ugnichName, ugnichPassword))
- .contentType(MediaType.APPLICATION_JSON_UTF8)
- .content(jsonMapper.writeValueAsBytes(Collections.singletonList(registration))))
- .andExpect(status().isOk());
- MvcResult result = mockMvc.perform(get("/notifications")
- .param("uid", String.valueOf(ugnich.getUid()))
- .with(httpBasic(juickName, juickPassword)))
- .andExpect(status().isOk())
- .andReturn();
- List user = jsonMapper.readValue(result.getResponse().getContentAsString(),
- new TypeReference>() {
- });
- assertThat(user.get(0).getTokens().get(0).getToken(), equalTo(token));
- }
- @Test
- public void tg2juickLinks() {
- UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://juick.com/123456#23").build();
- assertThat(uriComponents.getPath().substring(1), is("123456"));
- assertThat(uriComponents.getFragment(), is("23"));
- }
- @Test
- public void notificationsTokensTest() throws Exception {
- List tokens = Collections.singletonList(new ExternalToken(null, "gcm", "123456", null));
- mockMvc.perform(delete("/notifications").with(httpBasic(ugnichName, ugnichPassword))
- .contentType(MediaType.APPLICATION_JSON_UTF8)
- .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isForbidden());
- mockMvc.perform(delete("/notifications").with(httpBasic(juickName, juickPassword))
- .contentType(MediaType.APPLICATION_JSON_UTF8)
- .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isOk());
- }
- @Test
- public void topTest() throws Exception {
- int topmid = messagesService.createMessage(ugnich.getUid(), "top message", null, null);
- IntStream.rangeClosed(6, 12).forEach(i -> {
- messagesService.createReply(topmid, 0, i, "yo", null);
- });
-
- assertThat(messagesService.getPopularCandidates().get(0), is(topmid));
- Tag juickTag = tagService.getTag(juickTagId);
- assertThat(juickTag.TID, is(2));
- tagService.updateTags(topmid, Collections.singletonList(juickTag));
- assertThat(messagesService.getPopularCandidates().isEmpty(), is(true));
- }
- @Test
- public void inReplyToScannerTest() {
- String header = "<123456.56@juick.com>";
- Scanner headerScanner = new Scanner(header).useDelimiter(EmailManager.MSGID_PATTERN);
- int mid = Integer.parseInt(headerScanner.next());
- int rid = Integer.parseInt(headerScanner.next());
- assertThat(mid, equalTo(123456));
- assertThat(rid, equalTo(56));
- }
-}
diff --git a/juick-server/src/test/java/com/juick/server/tests/MessagesTests.java b/juick-server/src/test/java/com/juick/server/tests/MessagesTests.java
new file mode 100644
index 00000000..53267a0f
--- /dev/null
+++ b/juick-server/src/test/java/com/juick/server/tests/MessagesTests.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2008-2017, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.server.tests;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.juick.ExternalToken;
+import com.juick.Message;
+import com.juick.Tag;
+import com.juick.User;
+import com.juick.server.EmailManager;
+import com.juick.server.configuration.ApiAppConfiguration;
+import com.juick.server.configuration.ApiSecurityConfig;
+import com.juick.configuration.RepositoryConfiguration;
+import com.juick.server.helpers.TagStats;
+import com.juick.service.ImagesService;
+import com.juick.service.MessagesService;
+import com.juick.service.TagService;
+import com.juick.service.UserService;
+import com.juick.util.DateFormattersHolder;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import javax.inject.Inject;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Scanner;
+import java.util.stream.IntStream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * Created by vitalyster on 25.11.2016.
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = {ApiAppConfiguration.class, ApiSecurityConfig.class, RepositoryConfiguration.class})
+@WebAppConfiguration
+public class MessagesTests extends AbstractJUnit4SpringContextTests {
+
+ private MockMvc mockMvc;
+ @Inject
+ private WebApplicationContext webApplicationContext;
+
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private UserService userService;
+ @Inject
+ private TagService tagService;
+ @Inject
+ private ObjectMapper jsonMapper;
+ @Inject
+ private ImagesService imagesService;
+
+ private static User ugnich, freefd, juick;
+ static String ugnichName, ugnichPassword, freefdName, freefdPassword, juickName, juickPassword;
+ static Message msg;
+ static int juickTagId;
+
+ private static boolean isSetUp = false;
+
+
+ @Before
+ public void setUp() {
+ mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
+ .apply(SecurityMockMvcConfigurers.springSecurity())
+ .dispatchOptions(true)
+ .build();
+ if (!isSetUp) {
+ ugnichName = "ugnich";
+ ugnichPassword = "MyPassw0rd!";
+ freefdName = "freefd";
+ freefdPassword = "MyPassw0rd!";
+ juickName = "juick";
+ juickPassword = "demo";
+ int ugnichId = userService.createUser(ugnichName, ugnichPassword);
+ ugnich = userService.getUserByUID(ugnichId).orElseThrow(IllegalStateException::new);
+ int freefdId = userService.createUser(freefdName, freefdPassword);
+ freefd = userService.getUserByUID(freefdId).orElseThrow(IllegalStateException::new);
+ int juickId = userService.createUser(juickName, juickPassword);
+ juick = userService.getUserByUID(juickId).orElseThrow(IllegalStateException::new);
+
+ String msgText = "Привет, я - Угнич";
+
+ int mid = messagesService.createMessage(ugnich.getUid(), msgText, "png", null);
+ msg = messagesService.getMessage(mid);
+ tagService.createTag("тест");
+ juickTagId = tagService.createTag("juick");
+ isSetUp = true;
+ }
+ }
+
+ @Test
+ public void testAllUnAuthorized() throws Exception {
+
+ mockMvc.perform(get("/"))
+ .andExpect(status().is4xxClientError());
+
+ mockMvc.perform(get("/auth"))
+ .andExpect(status().is4xxClientError());
+
+ mockMvc.perform(get("/home"))
+ .andExpect(status().is4xxClientError());
+
+ mockMvc.perform(get("/messages/recommended"))
+ .andExpect(status().is4xxClientError());
+
+ mockMvc.perform(get("/messages/set_privacy"))
+ .andExpect(status().is4xxClientError());
+ }
+
+ @Test
+ public void homeTestWithMessages() throws Exception {
+ mockMvc.perform(
+ get("/home")
+ .with(httpBasic(ugnichName, ugnichPassword)))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$[-1].mid", is(msg.getMid())))
+ .andExpect(jsonPath("$[-1].timestamp",
+ is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getTimestamp()))))
+ .andExpect(jsonPath("$[-1].body", is(msg.getText())))
+ .andExpect(jsonPath("$[-1].attachment.url", is("https://i.juick.com/p/1.png")))
+ .andExpect(jsonPath("$[-1].attachment.small.url", is("https://i.juick.com/photos-512/1.png")));
+ }
+
+ @Test
+ public void homeTestWithMessagesAndRememberMe() throws Exception {
+ String ugnichHash = userService.getHashByUID(ugnich.getUid());
+ mockMvc.perform(
+ get("/home")
+ .with(httpBasic(ugnichName, ugnichPassword)))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ mockMvc.perform(get("/home")
+ .param("hash", ugnichHash))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void homeTestWithMessagesAndSimpleCors() throws Exception {
+ mockMvc.perform(
+ get("/home")
+ .with(httpBasic(ugnichName, ugnichPassword))
+ .header("Origin", "http://api.example.net"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Access-Control-Allow-Origin", "*"));
+ }
+
+ @Test
+ public void homeTestWithPreflightCors() throws Exception {
+ mockMvc.perform(
+ options("/home")
+ .with(httpBasic(ugnichName, ugnichPassword))
+ .header("Origin", "http://api.example.net")
+ .header("Access-Control-Request-Method", "POST")
+ .header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Access-Control-Allow-Origin", "*"))
+ .andExpect(header().string("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE"))
+ .andExpect(header().string("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type"));
+ }
+
+ @Test
+ public void anonymousApis() throws Exception {
+
+
+ mockMvc.perform(get("/messages"))
+ .andExpect(status().isOk());
+
+ mockMvc.perform(get("/users")
+ .param("uname", "ugnich")
+ .param("uname", "freefd"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$", hasSize(2)));
+ }
+
+ @Test
+ public void tags() throws Exception {
+ Tag weather = tagService.getTag("weather", true);
+ Tag yo = tagService.getTag("yo", true);
+ messagesService.createMessage(ugnich.getUid(), "text", null, Arrays.asList(yo, weather));
+ messagesService.createMessage(freefd.getUid(), "text2", null, Collections.singletonList(yo));
+ MvcResult result = mockMvc.perform(get("/tags"))
+ .andExpect(status().isOk())
+ .andReturn();
+ List tagsFromApi = jsonMapper.readValue(result.getResponse().getContentAsString(),
+ new TypeReference>(){});
+ TagStats yoStats = tagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get();
+ assertThat(yoStats.getUsageCount(), is(2));
+ MvcResult result2 = mockMvc.perform(get("/tags")
+ .param("user_id", String.valueOf(ugnich.getUid())))
+ .andExpect(status().isOk())
+ .andReturn();
+ List ugnichTagsFromApi = jsonMapper.readValue(result2.getResponse().getContentAsString(),
+ new TypeReference>(){});
+ TagStats yoUgnichStats = ugnichTagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get();
+ assertThat(yoUgnichStats.getUsageCount(), is(1));
+ }
+
+ @Test
+ public void postWithReferer() throws Exception {
+ mockMvc.perform(post("/post")
+ .param("body", "yo")
+ .with(httpBasic(ugnichName, ugnichPassword)))
+ .andExpect(status().isOk());
+ }
+ @Test
+ public void threadWithEphemeralNumberShouldReturn404() throws Exception {
+ mockMvc.perform(get("/thread").param("mid", "999999999")
+ .with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().is4xxClientError());
+ }
+ @Test
+ public void performRequestsWithIssuedToken() throws Exception {
+ String ugnichHash = userService.getHashByUID(ugnich.getUid());
+ mockMvc.perform(get("/home")).andExpect(status().isUnauthorized());
+ mockMvc.perform(get("/auth"))
+ .andExpect(status().isUnauthorized());
+ mockMvc.perform(get("/auth").with(httpBasic(ugnichName, "wrongpassword")))
+ .andExpect(status().isUnauthorized());
+ MvcResult result = mockMvc.perform(get("/auth").with(httpBasic(ugnichName, ugnichPassword)))
+ .andExpect(status().isOk())
+ .andReturn();
+ String authHash = jsonMapper.readValue(result.getResponse().getContentAsString(), String.class);
+ assertThat(authHash, equalTo(ugnichHash));
+ mockMvc.perform(get("/home").param("hash", ugnichHash)).andExpect(status().isOk());
+ }
+ @Test
+ public void registerForNotificationsTests() throws Exception {
+ String token = "123456";
+ ExternalToken registration = new ExternalToken(null, "apns", token, null);
+ mockMvc.perform(put("/notifications").with(httpBasic(ugnichName, ugnichPassword))
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(jsonMapper.writeValueAsBytes(Collections.singletonList(registration))))
+ .andExpect(status().isOk());
+ MvcResult result = mockMvc.perform(get("/notifications")
+ .param("uid", String.valueOf(ugnich.getUid()))
+ .with(httpBasic(juickName, juickPassword)))
+ .andExpect(status().isOk())
+ .andReturn();
+ List user = jsonMapper.readValue(result.getResponse().getContentAsString(),
+ new TypeReference>() {
+ });
+ assertThat(user.get(0).getTokens().get(0).getToken(), equalTo(token));
+ }
+ @Test
+ public void tg2juickLinks() {
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://juick.com/123456#23").build();
+ assertThat(uriComponents.getPath().substring(1), is("123456"));
+ assertThat(uriComponents.getFragment(), is("23"));
+ }
+ @Test
+ public void notificationsTokensTest() throws Exception {
+ List tokens = Collections.singletonList(new ExternalToken(null, "gcm", "123456", null));
+ mockMvc.perform(delete("/notifications").with(httpBasic(ugnichName, ugnichPassword))
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isForbidden());
+ mockMvc.perform(delete("/notifications").with(httpBasic(juickName, juickPassword))
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isOk());
+ }
+ @Test
+ public void topTest() throws Exception {
+ int topmid = messagesService.createMessage(ugnich.getUid(), "top message", null, null);
+ IntStream.rangeClosed(6, 12).forEach(i -> {
+ messagesService.createReply(topmid, 0, i, "yo", null);
+ });
+
+ assertThat(messagesService.getPopularCandidates().get(0), is(topmid));
+ Tag juickTag = tagService.getTag(juickTagId);
+ assertThat(juickTag.TID, is(2));
+ tagService.updateTags(topmid, Collections.singletonList(juickTag));
+ assertThat(messagesService.getPopularCandidates().isEmpty(), is(true));
+ }
+ @Test
+ public void inReplyToScannerTest() {
+ String header = "<123456.56@juick.com>";
+ Scanner headerScanner = new Scanner(header).useDelimiter(EmailManager.MSGID_PATTERN);
+ int mid = Integer.parseInt(headerScanner.next());
+ int rid = Integer.parseInt(headerScanner.next());
+ assertThat(mid, equalTo(123456));
+ assertThat(rid, equalTo(56));
+ }
+}
--
cgit v1.2.3