diff options
author | 2016-11-24 21:38:52 +0300 | |
---|---|---|
committer | 2016-11-24 23:26:57 +0300 | |
commit | d233943fc29508dc37714852b11b96b0b46b55d9 (patch) | |
tree | 129cd564c021261247e72ac70605c1e559c6dac4 | |
parent | bcd9ae221522fa779ea1fd1ff1ab5b561ca0895f (diff) |
juick-api: now on spring-webmvc
32 files changed, 1627 insertions, 1323 deletions
diff --git a/juick-api/build.gradle b/juick-api/build.gradle index 9c6e98a5..08991eed 100644 --- a/juick-api/build.gradle +++ b/juick-api/build.gradle @@ -13,8 +13,17 @@ apply plugin: 'org.akhikhl.gretty' apply plugin: 'com.github.ben-manes.versions' apply plugin: 'com.github.tkruse.groovysh' +def springFrameworkVersion = "4.3.4.RELEASE" +def springSecurityVersion = "4.2.0.RELEASE" + dependencies { compile project(':juick-server') + + compile "org.springframework:spring-webmvc:${springFrameworkVersion}" + + compile "org.springframework.security:spring-security-web:${springSecurityVersion}" + compile "org.springframework.security:spring-security-config:${springSecurityVersion}" + providedCompile 'javax.servlet:javax.servlet-api:3.1.0' compile 'com.github.pengrad:java-telegram-bot-api:2.1.4' compile 'org.msbotframework4j:msbotframework4j-builder:0.1.4' diff --git a/juick-api/src/main/java/com/juick/api/ApiServer.java b/juick-api/src/main/java/com/juick/api/ApiServer.java new file mode 100644 index 00000000..c76c165a --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/ApiServer.java @@ -0,0 +1,121 @@ +/* + * Juick + * Copyright (C) 2008-2013, Ugnich Anton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.juick.api; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import rocks.xmpp.core.XmppException; +import rocks.xmpp.core.session.Extension; +import rocks.xmpp.core.session.XmppSessionConfiguration; +import rocks.xmpp.extensions.component.accept.ExternalComponent; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author Ugnich Anton + */ +public class ApiServer implements AutoCloseable { + private static Logger logger = LoggerFactory.getLogger(ApiServer.class); + + private ExternalComponent xmpp; + + public String tmpDir; + public String imgDir; + + private String xmppHost, xmppPassword, xmppJid; + private int xmppPort; + private boolean isXmppDisabled; + + + public ApiServer(Environment conf) { + tmpDir = conf.getProperty("upload_tmp_dir", "/var/www/juick.com/i/tmp/"); + imgDir = conf.getProperty("img_path", "/var/www/juick.com/i/"); + isXmppDisabled = BooleanUtils.toBoolean(conf.getProperty("xmpp_disabled")); + xmppHost = conf.getProperty("xmpp_host", "localhost"); + xmppPort = NumberUtils.toInt(conf.getProperty("xmpp_port", "5347"), 5347); + xmppJid = conf.getProperty("xmpp_jid", "api.localhost"); + xmppPassword = conf.getProperty("xmpp_password"); + } + + @PostConstruct + public void init() { + if (!isXmppDisabled) { + setupXmppComponent(xmppHost, xmppPort, xmppJid, xmppPassword); + } + } + + @Override + public void close() { + try { + if (getXmpp() != null) + getXmpp().close(); + + logger.warn("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(); + setXmpp(ExternalComponent.create(jid, password, configuration, host, port)); + try { + getXmpp().connect(); + } catch (XmppException e) { + logger.warn("xmpp extension", e); + } + } + + public static void replyJSON(HttpServletRequest request, HttpServletResponse response, String json) throws IOException { + response.setContentType("application/json; charset=UTF-8"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + String callback = request.getParameter("callback"); + if (callback != null && (callback.length() > 64 || !callback.matches("[a-zA-Z0-9\\-\\_]+"))) { + callback = null; + } + + try (PrintWriter out = response.getWriter()) { + if (callback != null) { + out.print(callback + "("); + out.print(json); + out.print(")"); + } else { + out.print(json); + } + } + } + + public ExternalComponent getXmpp() { + return xmpp; + } + + public void setXmpp(ExternalComponent xmpp) { + this.xmpp = xmpp; + } +} diff --git a/juick-api/src/main/java/com/juick/api/Main.java b/juick-api/src/main/java/com/juick/api/Main.java deleted file mode 100644 index f902fc6b..00000000 --- a/juick-api/src/main/java/com/juick/api/Main.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.api; - -import com.juick.Tag; -import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; -import net.coobird.thumbnailator.Thumbnails; -import org.apache.commons.dbcp2.BasicDataSource; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.jdbc.core.JdbcTemplate; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.extensions.component.accept.ExternalComponent; -import rocks.xmpp.extensions.nick.model.Nickname; -import rocks.xmpp.extensions.oob.model.x.OobX; - -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -/** - * @author Ugnich Anton - */ -@WebServlet(name = "Main", urlPatterns = {"/"}) -@MultipartConfig -public class Main extends HttpServlet { - - JdbcTemplate jdbc; - ExternalComponent xmpp; - Messages messages; - Users users; - PM pm; - Others others; - Subscriptions subscriptions; - Notifications notifications; - TGBot tgb; - SkypeEndpoint sep; - - String tmpDir, imgDir; - - @Override - public void init() throws ServletException { - super.init(); - try { - Properties conf = new Properties(); - conf.load(getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); - dataSource.setUrl(conf.getProperty("datasource_url")); - jdbc = new JdbcTemplate(dataSource); - messages = new Messages(jdbc); - users = new Users(jdbc); - pm = new PM(jdbc); - others = new Others(jdbc); - subscriptions = new Subscriptions(jdbc); - notifications = new Notifications(jdbc); - tgb = new TGBot(jdbc, conf.getProperty("telegram_token", "")); - sep = new SkypeEndpoint(); - setupXmppComponent(conf.getProperty("xmpp_host", "localhost"), Integer.parseInt(conf.getProperty("xmpp_port", "5347")), - conf.getProperty("xmpp_jid", "api.localhost"), conf.getProperty("xmpp_password")); - tmpDir = conf.getProperty("upload_tmp_dir", "/var/www/juick.com/i/tmp/"); - imgDir = conf.getProperty("img_path", "/var/www/juick.com/i/"); - } catch (IOException e) { - log("API initialization error", e); - } - } - - @Override - public void destroy() { - try { - if (xmpp != null) - xmpp.close(); - - log("ExternalComponent on juick-api destroyed"); - } catch (Exception e) { - log("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); - try { - xmpp.connect(); - } catch (XmppException e) { - log("xmpp extension", e); - } - } - - /** - * Handles the HTTP <code>GET</code> method. - * - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (request.getCharacterEncoding() == null) { - request.setCharacterEncoding("UTF-8"); - } - - String uri = request.getRequestURI(); - - int vuid = Utils.getHttpAuthUID(jdbc, request); - if (vuid == 0) { - vuid = Utils.getVisitorQueryStringUID(jdbc, request); - } - - if (uri.equals("/home")) { - if (vuid > 0) { - messages.doGetHome(request, response, vuid); - } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } else if (uri.equals("/messages")) { - messages.doGet(request, response, vuid); - } else if (uri.equals("/thread")) { - messages.doThreadGet(request, response, vuid); - } else if (uri.equals("/users")) { - users.doGetUsers(request, response, vuid); - } else if (uri.equals("/users/read")) { - users.doGetUserRead(request, response, vuid); - } else if (uri.equals("/users/readers")) { - users.doGetUserReaders(request, response, vuid); - } else if (uri.equals("/pm")) { - if (vuid > 0) { - pm.doGetPM(request, response, vuid); - } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } else if (uri.equals("/groups_pms")) { - if (vuid > 0) { - others.doGetGroupsPMs(request, response, vuid); - } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } else if (uri.equals("/messages/recommended")) { - if (vuid > 0) { - messages.doGetRecommended(request, response, vuid); - } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } else if (uri.equals("/messages/set_popular") && vuid == 3694) { - messages.doSetPopular(request, response, xmpp); - } else if (uri.equals("/messages/set_privacy") && vuid > 0) { - messages.doSetPrivacy(request, response, xmpp, vuid); - } else if (uri.equals("/subscriptions")) { - subscriptions.doGet(request, response, vuid); - } else if (uri.equals("/notifications")) { - notifications.doGet(request, response, vuid); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - /** - * Handles the HTTP <code>POST</code> method. - * - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (request.getCharacterEncoding() == null) { - request.setCharacterEncoding("UTF-8"); - } - String uri = request.getRequestURI(); - if (uri.equals("/tlgmbtwbhk")) { - try { - tgb.doPost(request); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - log("telegram error", e); - } - return; - } - if (uri.equals("/skypebotendpoint")) { - sep.doPost(request); - return; - } - - int vuid = Utils.getHttpAuthUID(jdbc, request); - if (vuid == 0) { - vuid = Utils.getVisitorQueryStringUID(jdbc, request); - } - if (vuid == 0) { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - switch (uri) { - case "/post": - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - if (mid == 0) { - doPostMessage(jdbc, request, response, xmpp, vuid); - } else { - doPostComment(jdbc, request, response, xmpp, vuid); - } - break; - case "/pm": - pm.doPostPM(request, response, xmpp, vuid); - break; - default: - response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); - break; - } - } - - public void doPostMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, XmppSession xmpp, int vuid) - throws ServletException, IOException { - String body = request.getParameter("body"); - if (body == null || body.length() < 1 || body.length() > 4096) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - body = body.replace("\r", ""); - - String tagsStr = request.getParameter("tags"); - List<Tag> tags = new ArrayList<>(); - String tagsArr[] = new String[1]; - if (tagsStr != null && !tagsStr.isEmpty()) { - tagsArr = tagsStr.split("[ \\,]"); - for (int i = 0; i < tagsArr.length; i++) { - if (tagsArr[i].startsWith("*")) { - tagsArr[i] = tagsArr[i].substring(1); - } - if (tagsArr[i].length() > 64) { - tagsArr[i] = tagsArr[i].substring(0, 64); - } - } - tags = TagQueries.getTags(sql, tagsArr, true); - while (tags.size() > 5) { - tags.remove(5); - } - } - - String attachmentFName = null; - try { - attachmentFName = Utils.receiveMultiPartFile(request, "attach"); - } catch (Exception e) { - log("MULTIPART ERROR", e); - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - String paramImg = request.getParameter("img"); - if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { - try { - URL imgUrl = new URL(paramImg); - attachmentFName = Utils.downloadImage(imgUrl); - } catch (Exception e) { - log("DOWNLOAD ERROR", e); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - } - - String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; - int mid = MessagesQueries.createMessage(sql, vuid, body, attachmentType, tags); - SubscriptionsQueries.subscribeMessage(sql, mid, vuid); - com.juick.Message jmsg = MessagesQueries.getMessage(sql, mid); - if (xmpp != null) { - Message xmsg = new Message(); - xmsg.setFrom(Jid.of("juick@juick.com")); - xmsg.setType(Message.Type.CHAT); - xmsg.setThread("juick-" + mid); - - xmsg.addExtension(jmsg); - xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName())); - - if (attachmentFName != null) { - String fname = mid + "." + attachmentType; - String attachmentURL = "http://i.juick.com/photos-1024/" + fname; - - Path origName = Paths.get(imgDir, "p", fname); - Files.move(Paths.get(tmpDir, attachmentFName), origName); - Thumbnails.of(origName.toFile()).size(1024, 1024).outputQuality(0.9) - .toFile(Paths.get(imgDir, "photos-1024", fname).toFile()); - Thumbnails.of(origName.toFile()).size(512, 512).outputQuality(0.9) - .toFile(Paths.get(imgDir, "photos-512", fname).toFile()); - Thumbnails.of(origName.toFile()).size(160, 120).outputQuality(0.9) - .toFile(Paths.get(imgDir, "ps", fname).toFile()); - - body = attachmentURL + "\n" + body; - try { - OobX xoob = new OobX(new URI(attachmentURL)); - xmsg.addExtension(xoob); - } catch (URISyntaxException e) { - log("invalid uri: " + attachmentURL, e); - } - } - - String tagsStr2 = ""; - for (String tag : tagsArr) { - tagsStr2 += " *" + tag; - } - xmsg.setBody("@" + jmsg.getUser().getName() + ":" + tagsStr2 + "\n" + body + "\n\n#" + mid + " http://juick.com/" + mid); - - xmsg.setTo(Jid.of("juick@s2s.juick.com")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("juick@ws.juick.com")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("juick@push.juick.com")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("twitter@crosspost.juick.com")); - xmpp.send(xmsg); - xmsg.setTo(Jid.of("fb@crosspost.juick.com")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("jubo@nologin.ru")); - xmpp.send(xmsg); - } else { - log("XMPP unavailable"); - } - MessageSerializer serializer = new MessageSerializer(); - Main.replyJSON(request, response, serializer.serialize(jmsg).toString()); - } - - public void doPostComment(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, XmppSession xmpp, int vuid) - throws ServletException, IOException { - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - if (mid == 0) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - com.juick.Message msg = MessagesQueries.getMessage(sql, mid); - if (msg == null) { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - return; - } - - int rid = NumberUtils.toInt(request.getParameter("rid"), 0); - com.juick.Message reply = null; - if (rid > 0) { - reply = MessagesQueries.getReply(sql, mid, rid); - if (reply == null) { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - return; - } - } - - String body = request.getParameter("body"); - if (body == null || body.length() < 1 || body.length() > 4096) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - body = body.replace("\r", ""); - - if ((msg.ReadOnly && msg.getUser().getUid() != vuid) || UserQueries.isInBLAny(sql, msg.getUser().getUid(), vuid) - || (reply != null && UserQueries.isInBLAny(sql, reply.getUser().getUid(), vuid))) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - String attachmentFName = null; - try { - attachmentFName = Utils.receiveMultiPartFile(request, "attach"); - } catch (Exception e) { - log("MULTIPART ERROR", e); - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - String paramImg = request.getParameter("img"); - if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { - try { - attachmentFName = Utils.downloadImage(new URL(paramImg)); - } catch (Exception e) { - log("DOWNLOAD ERROR", e); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - } - - String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; - int ridnew = MessagesQueries.createReply(sql, mid, rid, vuid, body, attachmentType); - SubscriptionsQueries.subscribeMessage(sql, mid, vuid); - - com.juick.Message jmsg = MessagesQueries.getReply(sql, mid, ridnew); - - if (xmpp != null) { - 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 ? reply.getText() : msg.getText(); - if (quote.length() >= 50) { - quote = quote.substring(0, 47) + "..."; - } - - xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName())); - - if (attachmentFName != null) { - String fname = mid + "-" + ridnew + "." + attachmentType; - String attachmentURL = "http://i.juick.com/photos-1024/" + fname; - - Path origName = Paths.get(imgDir, "p", fname); - Files.move(Paths.get(tmpDir, attachmentFName), origName); - Thumbnails.of(origName.toFile()).size(1024, 1024).outputQuality(0.9) - .toFile(Paths.get(imgDir, "photos-1024", fname).toFile()); - Thumbnails.of(origName.toFile()).size(512, 512).outputQuality(0.9) - .toFile(Paths.get(imgDir, "photos-512", fname).toFile()); - Thumbnails.of(origName.toFile()).size(160, 120).outputQuality(0.9) - .toFile(Paths.get(imgDir, "ps", fname).toFile()); - - body = attachmentURL + "\n" + body; - try { - xmsg.addExtension(new OobX(new URI(attachmentURL))); - } catch (URISyntaxException e) { - log("invalid uri: " + 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")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("juick@ws.juick.com")); - xmpp.send(xmsg); - - xmsg.setTo(Jid.of("juick@push.juick.com")); - xmpp.send(xmsg); - } else { - log("XMPP unavailable"); - } - MessageSerializer serializer = new MessageSerializer(); - Main.replyJSON(request, response, serializer.serialize(jmsg).toString()); - } - - @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (req.getCharacterEncoding() == null) { - req.setCharacterEncoding("UTF-8"); - } - - int vuid = Utils.getHttpAuthUID(jdbc, req); - if (vuid == 0) { - vuid = Utils.getVisitorQueryStringUID(jdbc, req); - } - if (vuid == 0) { - resp.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - String uri = req.getRequestURI(); - switch (uri) { - case "/notifications": - notifications.doDelete(req, resp, vuid); - break; - default: - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); - break; - } - } - - public static void replyJSON(HttpServletRequest request, HttpServletResponse response, String json) throws IOException { - response.setContentType("application/json; charset=UTF-8"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - String callback = request.getParameter("callback"); - if (callback != null && (callback.length() > 64 || !callback.matches("[a-zA-Z0-9\\-\\_]+"))) { - callback = null; - } - - try (PrintWriter out = response.getWriter()) { - if (callback != null) { - out.print(callback + "("); - out.print(json); - out.print(")"); - } else { - out.print(json); - } - } - } -} diff --git a/juick-api/src/main/java/com/juick/api/Messages.java b/juick-api/src/main/java/com/juick/api/Messages.java deleted file mode 100644 index 024a79c1..00000000 --- a/juick-api/src/main/java/com/juick/api/Messages.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.juick.api; - -import com.juick.Tag; -import com.juick.User; -import com.juick.json.JSONSerializer; -import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.StringUtils; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.stanza.model.Message; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * @author ugnich - */ -public class Messages { - private static final Logger logger = LoggerFactory.getLogger(Messages.class); - - JdbcTemplate sql; - - MessageSerializer messageSerializer = new MessageSerializer(); - - public Messages(JdbcTemplate sql) { - this.sql = sql; - } - - void feedMessages(HttpServletRequest request, HttpServletResponse response, List<com.juick.Message> msgs) throws IOException { - String requestScheme = request.getHeader("X-Scheme"); - if (requestScheme != null) { - if (Objects.equals(requestScheme, "https")) { - messageSerializer.setUriScheme(JSONSerializer.URIScheme.Secure); - } - } - String json = messageSerializer.serializeList(msgs); - Main.replyJSON(request, response, json); - } - - public void doGetHome(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); - List<Integer> mids = MessagesQueries.getMyFeed(sql, vuid, before_mid); - feedMessages(request, response, MessagesQueries.getMessages(sql, mids)); - } - - public void doGet(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); - String uname = request.getParameter("uname"); - String popular = request.getParameter("popular"); - String media = request.getParameter("media"); - String tag = request.getParameter("tag"); - List<Integer> mids = new ArrayList<>(); - if (!StringUtils.isEmpty(uname)) { - User user = UserQueries.getUserByName(sql, uname); - if (user != null) { - if (!StringUtils.isEmpty(media)) { - mids = MessagesQueries.getUserPhotos(sql, user.getUid(), 0, before_mid); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = TagQueries.getTag(sql, tag, false); - if (tagObject != null) { - mids = MessagesQueries.getUserTag(sql, user.getUid(), tagObject.TID, 0, before_mid); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } else { - mids = MessagesQueries.getUserBlog(sql, user.getUid(), 0, before_mid); - } - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } else { - if (!StringUtils.isEmpty(popular)) { - mids = MessagesQueries.getPopular(sql, vuid, before_mid); - } else if (!StringUtils.isEmpty(media)) { - mids = MessagesQueries.getPhotos(sql, vuid, before_mid); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = TagQueries.getTag(sql, tag, false); - if (tagObject != null) { - mids = MessagesQueries.getTag(sql, tagObject.TID, vuid, before_mid, 20); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } else { - mids = MessagesQueries.getAll(sql, vuid, before_mid); - } - } - feedMessages(request, response, MessagesQueries.getMessages(sql, mids)); - } - - public void doThreadGet(HttpServletRequest request, HttpServletResponse response, int vuid) throws IOException { - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - com.juick.Message msg = MessagesQueries.getMessage(sql, mid); - if (msg != null) { - if (!MessagesQueries.canViewThread(sql, mid, vuid)) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } else { - List<com.juick.Message> replies = MessagesQueries.getReplies(sql, mid); - replies.add(0, msg); - feedMessages(request, response, replies); - } - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - public void doGetRecommended(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); - - List<Integer> mids = MessagesQueries.getUserRecommendations(sql, vuid, before_mid); - if (mids != null && !mids.isEmpty()) { - List<com.juick.Message> msgs = MessagesQueries.getMessages(sql, mids); - if (msgs != null && !msgs.isEmpty()) { - String json = messageSerializer.serializeList(msgs); - Main.replyJSON(request, response, json); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - public void doSetPrivacy(HttpServletRequest request, - HttpServletResponse response, XmppSession xmpp, int vuid) - throws ServletException, IOException { - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - com.juick.User user = MessagesQueries.getMessageAuthor(sql, mid); - if (user != null && user.getUid() == vuid && MessagesQueries.setMessagePrivacy(sql, mid)) { - Main.replyJSON(request, response, "{\"status\":\"ok\"}"); - } else { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - - public void doSetPopular(HttpServletRequest request, - HttpServletResponse response, XmppSession xmpp) - throws ServletException, IOException { - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - int popular = NumberUtils.toInt(request.getParameter("popular"), 0); - - if (mid > 0) { - boolean ret = MessagesQueries.setMessagePopular(sql, mid, popular); - - if (ret && popular == 2) { - try { - com.juick.Message m = MessagesQueries.getMessage(sql, mid); - if (m != null) { - Message msg = new Message(); - msg.setFrom(Jid.of("juick@juick.com")); - msg.setTo(Jid.of("crosspost.juick.com")); - m.setUser(UserQueries.getUserByUID(sql, 11574).get()); - msg.addExtension(m); - - msg.setTo(Jid.of("twitter@crosspost.juick.com")); - xmpp.send(msg); - msg.setTo(Jid.of("fb@crosspost.juick.com")); - xmpp.send(msg); - msg.setTo(Jid.of("vk@crosspost.juick.com")); - xmpp.send(msg); - } else { - throw new Exception("Message not found"); - } - } catch (Exception e) { - logger.error("SETPOPULAR ERROR", e); - } - } - - Main.replyJSON(request, response, "{\"status\":\"ok\"}"); - } - } -} diff --git a/juick-api/src/main/java/com/juick/api/Notifications.java b/juick-api/src/main/java/com/juick/api/Notifications.java deleted file mode 100644 index dbd128b0..00000000 --- a/juick-api/src/main/java/com/juick/api/Notifications.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.juick.api; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.Message; -import com.juick.User; -import com.juick.server.MessagesQueries; -import com.juick.server.PushQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.server.UserQueries; -import com.juick.server.helpers.TokensList; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.jdbc.core.JdbcTemplate; -import spark.utils.IOUtils; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 24.10.2016. - */ -public class Notifications { - JdbcTemplate jdbc; - ObjectMapper mapper = new ObjectMapper(); - - public Notifications(JdbcTemplate jdbc) { - this.jdbc = jdbc; - } - - public void doGet(HttpServletRequest request, HttpServletResponse response, int vuid) throws IOException { - User visitor = UserQueries.getUserByUID(jdbc, vuid).orElse(new User()); - if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - String type = request.getParameter("type"); - int uid = NumberUtils.toInt(request.getParameter("uid"), 0); - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - if (uid > 0) { - switch (type) { - case "gcm": - List<String> tokens = PushQueries.getAndroidRegID(jdbc, uid); - Main.replyJSON(request, response, mapper.writeValueAsString(tokens)); - break; - case "apns": - List<String> apnsTokens = PushQueries.getAPNSToken(jdbc, uid); - Main.replyJSON(request, response, mapper.writeValueAsString(apnsTokens)); - break; - case "mpns": - List<String> mpnsTokens = PushQueries.getWinPhoneURL(jdbc, uid); - Main.replyJSON(request, response, mapper.writeValueAsString(mpnsTokens)); - break; - default: - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - - } else { - if (mid > 0) { - Message msg = MessagesQueries.getMessage(jdbc, mid); - if (msg != null) { - List<User> users; - if (msg.getRid() > 0) { - users = SubscriptionsQueries.getUsersSubscribedToComments(jdbc, mid, msg.getUser().getUid()); - } else { - users = SubscriptionsQueries.getSubscribedUsers(jdbc, msg.getUser().getUid(), mid); - } - - List<Integer> uids = users.stream().map(User::getUid).collect(Collectors.toList()); - - switch (type) { - case "gcm": - List<String> tokens = PushQueries.getAndroidTokens(jdbc, uids); - Main.replyJSON(request, response, mapper.writeValueAsString(tokens)); - break; - case "apns": - List<String> apnsTokens = PushQueries.getAPNSTokens(jdbc, uids); - Main.replyJSON(request, response, mapper.writeValueAsString(apnsTokens)); - break; - case "mpns": - List<String> mpnsTokens = PushQueries.getWindowsTokens(jdbc, uids); - Main.replyJSON(request, response, mapper.writeValueAsString(mpnsTokens)); - break; - default: - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - } else { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - } - - public void doDelete(HttpServletRequest request, HttpServletResponse response, int vuid) throws IOException { - User visitor = UserQueries.getUserByUID(jdbc, vuid).orElse(new User()); - if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); - TokensList list = mapper.readValue(IOUtils.toString(request.getInputStream()), TokensList.class); - list.getTokens().forEach(t -> PushQueries.deleteAPNSToken(jdbc, t)); - } -} diff --git a/juick-api/src/main/java/com/juick/api/Others.java b/juick-api/src/main/java/com/juick/api/Others.java deleted file mode 100644 index 446c03b5..00000000 --- a/juick-api/src/main/java/com/juick/api/Others.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.juick.api; - -import com.juick.User; -import com.juick.json.UserSerializer; -import com.juick.server.PMQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; - -/** - * - * @author ugnich - */ -public class Others { - - JdbcTemplate sql; - - UserSerializer userSerializer = new UserSerializer(); - - public Others(JdbcTemplate sql) { - this.sql = sql; - } - - public void doGetGroupsPMs(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int cnt = 5; - try { - String cntStr = request.getParameter("cnt"); - cnt = Integer.parseInt(cntStr); - if (cnt < 3) { - cnt = 3; - } - if (cnt > 10) { - cnt = 10; - } - } catch (Exception e) { - } - - List<User> lastconv = PMQueries.getPMLastConversationsUsers(sql, vuid, cnt); - if (lastconv != null && !lastconv.isEmpty()) { - String json = "{\"pms\":" + userSerializer.serializeList(lastconv) + "}"; - Main.replyJSON(request, response, json); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } -} diff --git a/juick-api/src/main/java/com/juick/api/PM.java b/juick-api/src/main/java/com/juick/api/PM.java deleted file mode 100644 index 1e06e67d..00000000 --- a/juick-api/src/main/java/com/juick/api/PM.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.juick.api; - -import com.juick.json.MessageSerializer; -import com.juick.server.PMQueries; -import com.juick.server.UserQueries; -import com.juick.util.UserUtils; -import org.springframework.jdbc.core.JdbcTemplate; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.stanza.model.Message; - -import java.io.IOException; -import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author ugnich - */ -public class PM { - - JdbcTemplate sql; - - MessageSerializer messageSerializer = new MessageSerializer(); - - public PM(JdbcTemplate sql) { - this.sql = sql; - } - - public void doGetPM(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - String uname = request.getParameter("uname"); - int uid = 0; - if (uname != null && uname.matches("^[a-zA-Z0-9\\-]{2,16}$")) { - uid = UserQueries.getUIDbyName(sql, uname); - } - - if (uid == 0) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - List<com.juick.Message> msgs = PMQueries.getPMMessages(sql, vuid, uid); - if (msgs != null && !msgs.isEmpty()) { - String json = messageSerializer.serializeList(msgs); - Main.replyJSON(request, response, json); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - public void doPostPM(HttpServletRequest request, - HttpServletResponse response, XmppSession xmpp, int vuid) - throws ServletException, IOException { - String uname = request.getParameter("uname"); - int uid = 0; - if (UserUtils.checkUserNameValid(uname)) { - uid = UserQueries.getUIDbyName(sql, uname); - } - - String body = request.getParameter("body"); - if (uid == 0 || body == null || body.length() < 1 || body.length() > 10240) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - if (UserQueries.isInBLAny(sql, uid, vuid)) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - if (PMQueries.createPM(sql, vuid, uid, body)) { - Message msg = new Message(); - msg.setFrom(Jid.of("juick@juick.com")); - msg.setTo(Jid.of(String.format("%d@push.juick.com", uid))); - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(UserQueries.getUserByUID(sql, vuid).get()); - jmsg.setText(body); - msg.addExtension(jmsg); - xmpp.send(msg); - - msg.setTo(Jid.of(String.format("%d@ws.juick.com", uid))); - xmpp.send(msg); - - Main.replyJSON(request, response, messageSerializer.serialize(jmsg).toString()); - - List<String> jids = UserQueries.getJIDsbyUID(sql, uid); - for (String jid: jids) { - Message mm = new Message(); - mm.setTo(Jid.of(jid)); - mm.setType(Message.Type.CHAT); - if (PMQueries.havePMinRoster(sql, 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); - } - xmpp.send(mm); - } - - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } -} diff --git a/juick-api/src/main/java/com/juick/api/Subscriptions.java b/juick-api/src/main/java/com/juick/api/Subscriptions.java deleted file mode 100644 index 126832a9..00000000 --- a/juick-api/src/main/java/com/juick/api/Subscriptions.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.juick.api; - -import com.juick.Message; -import com.juick.User; -import com.juick.json.UserSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.server.UserQueries; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; - -/** - * Created by vitalyster on 24.10.2016. - */ -public class Subscriptions { - JdbcTemplate jdbc; - UserSerializer userSerializer = new UserSerializer(); - - public Subscriptions(JdbcTemplate jdbc) { - this.jdbc = jdbc; - } - public void doGet(HttpServletRequest request, HttpServletResponse response, int vuid) throws IOException { - User visitor = UserQueries.getUserByUID(jdbc, vuid).orElse(new User()); - if ((visitor.getUid() == 0) && !(visitor.getName().equals("juick"))) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - int uid = NumberUtils.toInt(request.getParameter("uid"), 0); - int mid = NumberUtils.toInt(request.getParameter("mid"), 0); - if (uid > 0) { - List<User> users = SubscriptionsQueries.getSubscribedUsers(jdbc, uid, mid); - Main.replyJSON(request, response, userSerializer.serializeList(users)); - } else { - // thread - Message msg = MessagesQueries.getMessage(jdbc, mid); - if (msg != null) { - List<User> users = SubscriptionsQueries.getUsersSubscribedToComments(jdbc, mid, msg.getUser().getUid()); - Main.replyJSON(request, response, userSerializer.serializeList(users)); - } - } - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } -} diff --git a/juick-api/src/main/java/com/juick/api/TGBot.java b/juick-api/src/main/java/com/juick/api/TGBot.java index 9f573d34..73aaa2b9 100644 --- a/juick-api/src/main/java/com/juick/api/TGBot.java +++ b/juick-api/src/main/java/com/juick/api/TGBot.java @@ -1,34 +1,28 @@ package com.juick.api; -import com.juick.User; import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.UserQueries; -import com.juick.server.protocol.JuickProtocol; -import com.neovisionaries.ws.client.*; -import com.pengrad.telegrambot.BotUtils; +import com.juick.service.MessagesService; +import com.juick.service.TelegramService; +import com.neovisionaries.ws.client.WebSocket; +import com.neovisionaries.ws.client.WebSocketAdapter; +import com.neovisionaries.ws.client.WebSocketFactory; +import com.neovisionaries.ws.client.WebSocketFrame; import com.pengrad.telegrambot.Callback; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.TelegramBotAdapter; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.request.InlineKeyboardButton; import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.response.SendResponse; +import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import javax.servlet.http.HttpServletRequest; -import java.io.BufferedReader; +import javax.inject.Inject; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; -import java.util.UUID; import static com.juick.formatters.PlainTextFormatter.formatPost; import static com.juick.formatters.PlainTextFormatter.formatUrl; @@ -42,14 +36,18 @@ public class TGBot implements AutoCloseable { TelegramBot bot; WebSocket ws; MessageSerializer ms = new MessageSerializer(); - JdbcTemplate jdbc; - JuickProtocol protocol; + @Inject + TelegramService telegramService; + @Inject + MessagesService messagesService; - public TGBot(JdbcTemplate jdbc, String token) { - this.jdbc = jdbc; + + public TGBot(String token) { + if (StringUtils.isBlank(token)) { + return; + } bot = TelegramBotAdapter.build(token); - protocol = new JuickProtocol(jdbc, "https://juick.com/"); try { bot.setWebhook("https://api.juick.com/tlgmbtwbhk"); ws = new WebSocketFactory().createSocket("wss://ws.juick.com/"); @@ -75,18 +73,18 @@ public class TGBot implements AutoCloseable { if (jmsg.getRid() == 0) { String msg = formatPost(jmsg); - List<Long> users = getSubscribers(jmsg.getUser().getUid()); - List<Long> chats = getChats(); + List<Long> users = telegramService.getSubscribers(jmsg.getUser().getUid()); + List<Long> chats = telegramService.getChats(); // registered subscribed users users.forEach(c -> telegramNotify(c, msg, msgUrl)); // anonymous - chats.stream().filter(u -> getUser(u) == 0).forEach(c -> telegramNotify(c, msg, msgUrl)); + chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg, msgUrl)); } else { // get quote - com.juick.Message msg = MessagesQueries.getReply(jdbc, jmsg.getMid(), jmsg.getRid()); + com.juick.Message msg = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); String fmsg = formatPost(msg); - getSubscribersToComments(jmsg.getMid(), jmsg.getUser().getUid()).forEach(c -> telegramNotify(c, fmsg, msgUrl)); + telegramService.getSubscribersToComments(jmsg.getMid(), jmsg.getUser().getUid()).forEach(c -> telegramNotify(c, fmsg, msgUrl)); } } }); @@ -96,7 +94,7 @@ public class TGBot implements AutoCloseable { } } - private void telegramNotify(Long c, String msg, String msgUrl) { + public void telegramNotify(Long c, String msg, String msgUrl) { SendMessage telegramMessage = new SendMessage(c, msg); if (msgUrl != null) { telegramMessage.replyMarkup( @@ -119,60 +117,7 @@ public class TGBot implements AutoCloseable { }); } - List<Long> getChats() { - return jdbc.queryForList("SELECT chat_id FROM telegram_chats", Long.class); - } - - void addChat(Long id) { - jdbc.update("INSERT IGNORE INTO telegram_chats(chat_id) VALUES(?)", id); - } - - public void doPost(HttpServletRequest request) throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - try (BufferedReader reader = request.getReader()) { - Update update = BotUtils.parseUpdate(reader); - Message message = update.message(); - if (update.message() == null) { - message = update.editedMessage(); - if (message == null) { - logger.error("error parsing telegram update: " + update.toString()); - return; - } - } - logger.info(String.format("got telegram msg %s", message.toString())); - User user_from = UserQueries.getUserByUID(jdbc, getUser(message.chat().id())).orElse(new User()); - logger.info(String.format("Found juick user %d", user_from.getUid())); - - List<Long> chats = getChats(); - String username = message.from().username(); - if (username == null) { - username = message.from().firstName(); - } - if (!chats.contains(message.chat().id())) { - addChat(message.chat().id()); - logger.info("added chat with " + username); - createTelegramUser(message.from().id(), username); - telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); - } else { - if (user_from.getUid() == 0) { - telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); - } else { - String text = message.text(); - if (text != null) { - if (text.equalsIgnoreCase("/login")) { - String msg = String.format("Hi, %s!\nTap to log in", user_from.getName()); - String msgUrl = "http://juick.com/login?" + UserQueries.getHashByUID(jdbc, user_from.getUid()); - telegramNotify(message.from().id().longValue(), msg, msgUrl); - } /* else { - ProtocolReply reply = protocol.getReply(user_from, text); - telegramNotify(message.from().id().longValue(), reply.getDescription(), null); - }*/ - } - } - } - } - } - - private void telegramSignupNotify(Long telegramId, String hash) { + public void telegramSignupNotify(Long telegramId, String hash) { bot.execute(new SendMessage(telegramId, "You are subscribed to all Juick messages. " + "Create or link an existing Juick account to control " + @@ -194,30 +139,6 @@ public class TGBot implements AutoCloseable { }); } - private boolean createTelegramUser(long tgID, String tgName) { - return jdbc.update("INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", - tgID, tgName, UUID.randomUUID().toString()) > 0; - } - - private int getUser(long tgId) { - try { - return jdbc.queryForObject("SELECT id FROM users INNER JOIN telegram " + - "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - private List<Long> getSubscribers(int uid) { - return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_users " + - "ON (subscr_users.user_id=? AND telegram.user_id=subscr_users.suser_id)", Long.class, uid); - } - - private List<Long> getSubscribersToComments(int mid, int ignore_uid) { - return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_messages " + - "ON (telegram.user_id=subscr_messages.suser_id) WHERE message_id=? AND suser_id!=?", Long.class, mid, ignore_uid); - } - @Override public void close() throws Exception { try { diff --git a/juick-api/src/main/java/com/juick/api/Users.java b/juick-api/src/main/java/com/juick/api/Users.java deleted file mode 100644 index 04e53346..00000000 --- a/juick-api/src/main/java/com/juick/api/Users.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.juick.api; - -import com.juick.User; -import com.juick.json.UserSerializer; -import com.juick.server.UserQueries; -import com.juick.util.UserUtils; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -/** - * - * @author ugnich - */ -public class Users { - - JdbcTemplate sql; - - UserSerializer userSerializer = new UserSerializer(); - - public Users(JdbcTemplate sql) { - this.sql = sql; - } - - public void doGetUsers(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - List<com.juick.User> users = new ArrayList<>(); - - String punames[] = request.getParameterValues("uname"); - if (punames != null) { - ArrayList<String> unames = new ArrayList<>(Arrays.asList(punames)); - Iterator<String> i = unames.iterator(); - while (i.hasNext()) { - if (!i.next().matches("^[a-zA-Z0-9\\-]{2,16}$")) { - i.remove(); - } - } - if (!unames.isEmpty() && unames.size() < 20) { - users.addAll(UserQueries.getUsersByName(sql, unames)); - } - } - - String pjids[] = request.getParameterValues("jid"); - if (pjids != null) { - List<String> jids = new ArrayList<>(Arrays.asList(pjids)); - Iterator<String> ii = jids.iterator(); - while (ii.hasNext()) { - if (!ii.next().matches("^[a-zA-Z0-9\\-\\_\\@\\.]{6,64}$")) { - ii.remove(); - } - } - if (!jids.isEmpty() && jids.size() < 20) { - users.addAll(UserQueries.getUsersByJID(sql, jids)); - } - } - - if (!users.isEmpty()) { - String json = userSerializer.serializeList(users); - Main.replyJSON(request, response, json); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - public void doGetUserRead(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int uid = 0; - String uname = request.getParameter("uname"); - if (uname == null) { - uid = vuid; - } else { - if (UserUtils.checkUserNameValid(uname)) { - com.juick.User u = UserQueries.getUserByName(sql, uname); - if (u != null && u.getUid() > 0) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - List<Integer> uids = UserQueries.getUserRead(sql, uid); - if (uids.size() > 0) { - List<com.juick.User> users = UserQueries.getUsersByID(sql, uids); - if (users.size() > 0) { - String json = userSerializer.serializeList(users); - Main.replyJSON(request, response, json); - return; - } - } - } - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - - public void doGetUserReaders(HttpServletRequest request, - HttpServletResponse response, int vuid) - throws ServletException, IOException { - int uid = 0; - String uname = request.getParameter("uname"); - if (uname == null) { - uid = vuid; - } else { - if (UserUtils.checkUserNameValid(uname)) { - com.juick.User u = UserQueries.getUserByName(sql, uname); - if (u != null && u.getUid() > 0) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - List<User> users = UserQueries.getUserReaders(sql, uid); - String json = userSerializer.serializeList(users); - Main.replyJSON(request, response, json); - return; - } - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } -} diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java b/juick-api/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java new file mode 100644 index 00000000..8f1e269b --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiAppConfiguration.java @@ -0,0 +1,29 @@ +package com.juick.api.configuration; + +import com.juick.api.ApiServer; +import com.juick.api.TGBot; +import com.juick.configuration.DataConfiguration; +import org.springframework.context.annotation.*; +import org.springframework.core.env.Environment; + +import javax.inject.Inject; + +/** + * Created by aalexeev on 11/12/16. + */ +@Configuration +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class ApiAppConfiguration { + @Inject + private Environment env; + + @Bean + ApiServer apiServer() { + return new ApiServer(env); + } + @Bean + TGBot tgBot() { + return new TGBot(env.getProperty("telegram_token", "")); + } +} diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java b/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java new file mode 100644 index 00000000..f5ba4ff1 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiInitializer.java @@ -0,0 +1,39 @@ +package com.juick.api.configuration; + +import org.springframework.web.filter.CharacterEncodingFilter; +import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; + +import javax.servlet.Filter; + +/** + * Created by vt on 09/02/16. + */ +public class ApiInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + + @Override + protected Class<?>[] getRootConfigClasses() { + return new Class<?>[]{ApiAppConfiguration.class}; + } + + @Override + protected Class<?>[] getServletConfigClasses() { + return new Class<?>[]{ApiMvcConfiguration.class}; + } + + @Override + protected String[] getServletMappings() { + return new String[]{"/"}; + } + + @Override + protected Filter[] getServletFilters() { + CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); + characterEncodingFilter.setEncoding("UTF-8"); + return new Filter[]{characterEncodingFilter}; + } + + @Override + protected String getServletName() { + return "API dispatcher servlet"; + } +} diff --git a/juick-api/src/main/java/com/juick/api/configuration/ApiMvcConfiguration.java b/juick-api/src/main/java/com/juick/api/configuration/ApiMvcConfiguration.java new file mode 100644 index 00000000..7183ab1f --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/configuration/ApiMvcConfiguration.java @@ -0,0 +1,43 @@ +package com.juick.api.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.juick.api.ApiServer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.api"}) +public class ApiMvcConfiguration extends WebMvcConfigurationSupport { + + @Override + public RequestMappingHandlerMapping requestMappingHandlerMapping() { + RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); + mapping.setUseSuffixPatternMatch(false); + return mapping; + } + + @Override + protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + super.configureMessageConverters(converters); + } +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Messages.java b/juick-api/src/main/java/com/juick/api/controllers/Messages.java new file mode 100644 index 00000000..a6bd3f6d --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Messages.java @@ -0,0 +1,248 @@ +package com.juick.api.controllers; + +import com.juick.Tag; +import com.juick.User; +import com.juick.api.ApiServer; +import com.juick.api.util.HttpBadRequestException; +import com.juick.api.util.HttpForbiddenException; +import com.juick.api.util.HttpNotFoundException; +import com.juick.server.helpers.Status; +import com.juick.service.MessagesService; +import com.juick.service.TagService; +import com.juick.service.UserService; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import rocks.xmpp.addr.Jid; +import rocks.xmpp.core.stanza.model.Message; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * @author ugnich + */ +@Controller +@ResponseBody +public class Messages { + private static final Logger logger = LoggerFactory.getLogger(Messages.class); + + @Inject + private MessagesService messagesService; + @Inject + private UserService userService; + @Inject + private TagService tagService; + @Inject + ApiServer apiServer; + + // TODO: serialize image urls + + @RequestMapping(value = "/home", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<com.juick.Message> doGetHome(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); + List<Integer> mids = messagesService.getMyFeed(vuid, before_mid); + return messagesService.getMessages(mids); + } + + @RequestMapping(value = "/messages", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<com.juick.Message> doGet(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); + String uname = request.getParameter("uname"); + String popular = request.getParameter("popular"); + String media = request.getParameter("media"); + String tag = request.getParameter("tag"); + List<Integer> mids; + if (!StringUtils.isEmpty(uname)) { + User user = userService.getUserByName(uname); + if (user != null) { + if (!StringUtils.isEmpty(media)) { + mids = messagesService.getUserPhotos(user.getUid(), 0, before_mid); + } else if (!StringUtils.isEmpty(tag)) { + Tag tagObject = tagService.getTag(tag, false); + if (tagObject != null) { + mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before_mid); + } else { + throw new HttpNotFoundException(); + } + } else { + mids = messagesService.getUserBlog(user.getUid(), 0, before_mid); + } + } else { + throw new HttpNotFoundException(); + } + } else { + if (!StringUtils.isEmpty(popular)) { + mids = messagesService.getPopular(vuid, before_mid); + } else if (!StringUtils.isEmpty(media)) { + mids = messagesService.getPhotos(vuid, before_mid); + } else if (!StringUtils.isEmpty(tag)) { + Tag tagObject = tagService.getTag(tag, false); + if (tagObject != null) { + mids = messagesService.getTag(tagObject.TID, vuid, before_mid, 20); + } else { + throw new HttpNotFoundException(); + } + } else { + mids = messagesService.getAll(vuid, before_mid); + } + } + return messagesService.getMessages(mids); + } + + @RequestMapping(value = "/thread", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<com.juick.Message> doThreadGet(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + com.juick.Message msg = messagesService.getMessage(mid); + if (msg != null) { + if (!messagesService.canViewThread(mid, vuid)) { + throw new HttpForbiddenException(); + } else { + List<com.juick.Message> replies = messagesService.getReplies(mid); + replies.add(0, msg); + return replies; + } + } + throw new HttpNotFoundException(); + } + + @RequestMapping(value = "/messages/recommended", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<com.juick.Message> doGetRecommended(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int before_mid = NumberUtils.toInt(request.getParameter("before_mid"), 0); + + List<Integer> mids = messagesService.getUserRecommendations(vuid, before_mid); + if (mids != null && !mids.isEmpty()) { + List<com.juick.Message> msgs = messagesService.getMessages(mids); + if (msgs != null && !msgs.isEmpty()) { + return msgs; + } else { + throw new HttpForbiddenException(); + } + } + throw new HttpNotFoundException(); + } + + @RequestMapping(value = "/messages/set_privacy", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @ResponseBody + public Status doSetPrivacy(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + com.juick.User user = messagesService.getMessageAuthor(mid); + if (user != null && user.getUid() == vuid && messagesService.setMessagePrivacy(mid)) { + return new Status("ok"); + } else { + throw new HttpBadRequestException(); + } + } + + @RequestMapping(value = "/messages/set_popular", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Status doSetPopular(HttpServletRequest request) { + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + int popular = NumberUtils.toInt(request.getParameter("popular"), 0); + + if (mid > 0) { + boolean ret = messagesService.setMessagePopular(mid, popular); + + if (ret && popular == 2) { + try { + com.juick.Message m = messagesService.getMessage(mid); + if (m != null) { + Message msg = new Message(); + msg.setFrom(Jid.of("juick@juick.com")); + msg.setTo(Jid.of("crosspost.juick.com")); + m.setUser(userService.getUserByUID(11574).get()); + msg.addExtension(m); + + msg.setTo(Jid.of("twitter@crosspost.juick.com")); + apiServer.getXmpp().send(msg); + msg.setTo(Jid.of("fb@crosspost.juick.com")); + apiServer.getXmpp().send(msg); + msg.setTo(Jid.of("vk@crosspost.juick.com")); + apiServer.getXmpp().send(msg); + } else { + throw new Exception("Message not found"); + } + } catch (Exception e) { + logger.error("SETPOPULAR ERROR", e); + } + } + return new Status("ok"); + } + throw new HttpBadRequestException(); + } +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Notifications.java b/juick-api/src/main/java/com/juick/api/controllers/Notifications.java new file mode 100644 index 00000000..0d0d0965 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Notifications.java @@ -0,0 +1,138 @@ +package com.juick.api.controllers; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.Message; +import com.juick.User; +import com.juick.api.util.HttpBadRequestException; +import com.juick.api.util.HttpForbiddenException; +import com.juick.server.helpers.Status; +import com.juick.server.helpers.TokensList; +import com.juick.service.MessagesService; +import com.juick.service.PushQueriesService; +import com.juick.service.SubscriptionService; +import com.juick.service.UserService; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created by vitalyster on 24.10.2016. + */ +@Controller +@ResponseBody +public class Notifications { + + @Inject + PushQueriesService pushQueriesService; + @Inject + UserService userService; + @Inject + MessagesService messagesService; + @Inject + SubscriptionService subscriptionService; + + @RequestMapping(value = "/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<String> doGet(HttpServletRequest request) { + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + User visitor = userService.getUserByUID(vuid).orElse(new User()); + if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) { + throw new HttpForbiddenException(); + } + String type = request.getParameter("type"); + int uid = NumberUtils.toInt(request.getParameter("uid"), 0); + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + if (uid > 0) { + switch (type) { + case "gcm": + return pushQueriesService.getAndroidRegID(uid); + case "apns": + return pushQueriesService.getAPNSToken(uid); + case "mpns": + return pushQueriesService.getWinPhoneURL(uid); + default: + throw new HttpBadRequestException(); + } + + } else { + if (mid > 0) { + Message msg = messagesService.getMessage(mid); + if (msg != null) { + List<User> users; + if (msg.getRid() > 0) { + users = subscriptionService.getUsersSubscribedToComments(mid, msg.getUser().getUid()); + } else { + users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), mid); + } + + List<Integer> uids = users.stream().map(User::getUid).collect(Collectors.toList()); + + switch (type) { + case "gcm": + return pushQueriesService.getAndroidTokens(uids); + case "apns": + return pushQueriesService.getAPNSTokens(uids); + case "mpns": + return pushQueriesService.getWindowsTokens(uids); + default: + throw new HttpBadRequestException(); + } + } + } + } + throw new HttpBadRequestException(); + } + + @RequestMapping(value = "/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Status doDelete(HttpServletRequest request, @RequestBody String requestBody) throws IOException { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + User visitor = userService.getUserByUID(vuid).orElse(new User()); + if ((visitor.getUid() == 0) || !(visitor.getName().equals("juick"))) { + throw new HttpForbiddenException(); + } + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); + TokensList list = mapper.readValue(requestBody, TokensList.class); + list.getTokens().forEach(t -> pushQueriesService.deleteAPNSToken(t)); + return new Status("ok"); + } +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Others.java b/juick-api/src/main/java/com/juick/api/controllers/Others.java new file mode 100644 index 00000000..ad620444 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Others.java @@ -0,0 +1,70 @@ +package com.juick.api.controllers; + +import com.juick.User; +import com.juick.api.util.HttpForbiddenException; +import com.juick.api.util.HttpNotFoundException; +import com.juick.server.helpers.PrivateChats; +import com.juick.service.PMQueriesService; +import com.juick.service.UserService; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * + * @author ugnich + */ +@Controller +@ResponseBody +public class Others { + + @Inject + PMQueriesService pmQueriesService; + @Inject + UserService userService; + + @RequestMapping(value = "groups_pms", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public PrivateChats doGetGroupsPMs(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int cnt = 5; + try { + String cntStr = request.getParameter("cnt"); + cnt = Integer.parseInt(cntStr); + if (cnt < 3) { + cnt = 3; + } + if (cnt > 10) { + cnt = 10; + } + } catch (Exception e) { + } + + List<User> 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-api/src/main/java/com/juick/api/controllers/PM.java b/juick-api/src/main/java/com/juick/api/controllers/PM.java new file mode 100644 index 00000000..3d9893b1 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/PM.java @@ -0,0 +1,130 @@ +package com.juick.api.controllers; + +import com.juick.api.ApiServer; +import com.juick.api.util.HttpBadRequestException; +import com.juick.api.util.HttpForbiddenException; +import com.juick.service.PMQueriesService; +import com.juick.service.UserService; +import com.juick.util.UserUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import rocks.xmpp.addr.Jid; +import rocks.xmpp.core.stanza.model.Message; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * + * @author ugnich + */ +@Controller +@ResponseBody +public class PM { + + @Inject + UserService userService; + @Inject + PMQueriesService pmQueriesService; + @Inject + ApiServer apiServer; + + @RequestMapping(value = "/pm", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<com.juick.Message> doGetPM(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + String uname = request.getParameter("uname"); + 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(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + String uname = request.getParameter("uname"); + int uid = 0; + if (UserUtils.checkUserNameValid(uname)) { + uid = userService.getUIDbyName(uname); + } + + String body = request.getParameter("body"); + if (uid == 0 || body == null || body.length() < 1 || body.length() > 10240) { + throw new HttpBadRequestException(); + } + + if (userService.isInBLAny(uid, vuid)) { + throw new HttpForbiddenException(); + } + + if (pmQueriesService.createPM(vuid, uid, body)) { + Message msg = new Message(); + msg.setFrom(Jid.of("juick@juick.com")); + msg.setTo(Jid.of(String.format("%d@push.juick.com", uid))); + com.juick.Message jmsg = new com.juick.Message(); + jmsg.setUser(userService.getUserByUID(vuid).get()); + jmsg.setText(body); + msg.addExtension(jmsg); + apiServer.getXmpp().send(msg); + + msg.setTo(Jid.of(String.format("%d@ws.juick.com", uid))); + apiServer.getXmpp().send(msg); + + List<String> jids = userService.getJIDsbyUID(uid); + 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.getXmpp().send(mm); + } + return jmsg; + + } + throw new HttpBadRequestException(); + } +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Post.java b/juick-api/src/main/java/com/juick/api/controllers/Post.java new file mode 100644 index 00000000..fb1914e9 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Post.java @@ -0,0 +1,304 @@ +package com.juick.api.controllers; + +import com.juick.Tag; +import com.juick.api.ApiServer; +import com.juick.api.util.HttpBadRequestException; +import com.juick.api.util.HttpForbiddenException; +import com.juick.api.util.HttpNotFoundException; +import com.juick.api.util.HttpUtils; +import com.juick.service.MessagesService; +import com.juick.service.SubscriptionService; +import com.juick.service.TagService; +import com.juick.service.UserService; +import net.coobird.thumbnailator.Thumbnails; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import rocks.xmpp.addr.Jid; +import rocks.xmpp.core.session.XmppSession; +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.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by vt on 24/11/2016. + */ +@Controller +@ResponseBody +public class Post { + private static Logger logger = LoggerFactory.getLogger(ApiServer.class); + + @Inject + UserService userService; + @Inject + ApiServer apiServer; + @Inject + TagService tagService; + @Inject + MessagesService messagesService; + @Inject + SubscriptionService subscriptionService; + + @RequestMapping(value = "/post", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + protected com.juick.Message doPost(HttpServletRequest request) throws IOException { + String uri = request.getRequestURI(); + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + switch (uri) { + case "/post": + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + if (mid == 0) { + return doPostMessage(request, apiServer.getXmpp(), vuid); + } else { + return doPostComment(request, apiServer.getXmpp(), vuid); + } + default: + throw new HttpBadRequestException(); + } + } + + public com.juick.Message doPostMessage(HttpServletRequest request, XmppSession xmpp, int vuid) throws IOException { + String body = request.getParameter("body"); + if (body == null || body.length() < 1 || body.length() > 4096) { + throw new HttpBadRequestException(); + } + body = body.replace("\r", ""); + + String tagsStr = request.getParameter("tags"); + List<Tag> tags = new ArrayList<>(); + String tagsArr[] = new String[1]; + if (tagsStr != null && !tagsStr.isEmpty()) { + tagsArr = tagsStr.split("[ \\,]"); + for (int i = 0; i < tagsArr.length; i++) { + if (tagsArr[i].startsWith("*")) { + tagsArr[i] = tagsArr[i].substring(1); + } + if (tagsArr[i].length() > 64) { + tagsArr[i] = tagsArr[i].substring(0, 64); + } + } + tags = tagService.getTags(tagsArr, true); + while (tags.size() > 5) { + tags.remove(5); + } + } + + String attachmentFName = null; + try { + attachmentFName = HttpUtils.receiveMultiPartFile(request, "attach"); + } catch (Exception e) { + logger.error("MULTIPART ERROR", e); + throw new HttpBadRequestException(); + } + + String paramImg = request.getParameter("img"); + if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { + try { + URL imgUrl = new URL(paramImg); + attachmentFName = HttpUtils.downloadImage(imgUrl); + } catch (Exception e) { + logger.error("DOWNLOAD ERROR", e); + throw new HttpBadRequestException(); + } + } + + String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; + int mid = messagesService.createMessage(vuid, body, attachmentType, tags); + subscriptionService.subscribeMessage(mid, vuid); + com.juick.Message jmsg = messagesService.getMessage(mid); + if (xmpp != null) { + Message xmsg = new Message(); + xmsg.setFrom(Jid.of("juick@juick.com")); + xmsg.setType(Message.Type.CHAT); + xmsg.setThread("juick-" + mid); + + xmsg.addExtension(jmsg); + xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName())); + + if (attachmentFName != null) { + String fname = mid + "." + attachmentType; + String attachmentURL = "http://i.juick.com/photos-1024/" + fname; + + Path origName = Paths.get(apiServer.imgDir, "p", fname); + Files.move(Paths.get(apiServer.tmpDir, attachmentFName), origName); + Thumbnails.of(origName.toFile()).size(1024, 1024).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "photos-1024", fname).toFile()); + Thumbnails.of(origName.toFile()).size(512, 512).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "photos-512", fname).toFile()); + Thumbnails.of(origName.toFile()).size(160, 120).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "ps", fname).toFile()); + + body = attachmentURL + "\n" + body; + try { + OobX xoob = new OobX(new URI(attachmentURL)); + xmsg.addExtension(xoob); + } catch (URISyntaxException e) { + logger.error("invalid uri: " + attachmentURL, e); + } + } + + String tagsStr2 = ""; + for (String tag : tagsArr) { + tagsStr2 += " *" + tag; + } + xmsg.setBody("@" + jmsg.getUser().getName() + ":" + tagsStr2 + "\n" + body + "\n\n#" + mid + " http://juick.com/" + mid); + + xmsg.setTo(Jid.of("juick@s2s.juick.com")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("juick@ws.juick.com")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("juick@push.juick.com")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("twitter@crosspost.juick.com")); + xmpp.send(xmsg); + xmsg.setTo(Jid.of("fb@crosspost.juick.com")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("jubo@nologin.ru")); + xmpp.send(xmsg); + } else { + logger.error("XMPP unavailable"); + } + return jmsg; + } + + public com.juick.Message doPostComment(HttpServletRequest request, XmppSession xmpp, int vuid) + throws IOException { + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + if (mid == 0) { + throw new HttpBadRequestException(); + } + com.juick.Message msg = messagesService.getMessage(mid); + if (msg == null) { + throw new HttpNotFoundException(); + } + + int rid = NumberUtils.toInt(request.getParameter("rid"), 0); + com.juick.Message reply = null; + if (rid > 0) { + reply = messagesService.getReply(mid, rid); + if (reply == null) { + throw new HttpNotFoundException(); + } + } + + String body = request.getParameter("body"); + if (body == null || body.length() < 1 || body.length() > 4096) { + throw new HttpBadRequestException(); + } + body = body.replace("\r", ""); + + 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 = null; + try { + attachmentFName = HttpUtils.receiveMultiPartFile(request, "attach"); + } catch (Exception e) { + logger.error("MULTIPART ERROR", e); + throw new HttpBadRequestException(); + } + + String paramImg = request.getParameter("img"); + if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { + try { + attachmentFName = HttpUtils.downloadImage(new URL(paramImg)); + } catch (Exception e) { + logger.error("DOWNLOAD ERROR", e); + throw new HttpBadRequestException(); + } + } + + String attachmentType = attachmentFName != null ? 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); + + if (xmpp != null) { + 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 ? reply.getText() : msg.getText(); + if (quote.length() >= 50) { + quote = quote.substring(0, 47) + "..."; + } + + xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName())); + + if (attachmentFName != null) { + String fname = mid + "-" + ridnew + "." + attachmentType; + String attachmentURL = "http://i.juick.com/photos-1024/" + fname; + + Path origName = Paths.get(apiServer.imgDir, "p", fname); + Files.move(Paths.get(apiServer.tmpDir, attachmentFName), origName); + Thumbnails.of(origName.toFile()).size(1024, 1024).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "photos-1024", fname).toFile()); + Thumbnails.of(origName.toFile()).size(512, 512).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "photos-512", fname).toFile()); + Thumbnails.of(origName.toFile()).size(160, 120).outputQuality(0.9) + .toFile(Paths.get(apiServer.imgDir, "ps", fname).toFile()); + + body = attachmentURL + "\n" + body; + try { + xmsg.addExtension(new OobX(new URI(attachmentURL))); + } catch (URISyntaxException e) { + logger.error("invalid uri: " + 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")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("juick@ws.juick.com")); + xmpp.send(xmsg); + + xmsg.setTo(Jid.of("juick@push.juick.com")); + xmpp.send(xmsg); + } else { + logger.error("XMPP unavailable"); + } + return jmsg; + } +} diff --git a/juick-api/src/main/java/com/juick/api/SkypeEndpoint.java b/juick-api/src/main/java/com/juick/api/controllers/SkypeEndpoint.java index 74777305..2a5c6c1e 100644 --- a/juick-api/src/main/java/com/juick/api/SkypeEndpoint.java +++ b/juick-api/src/main/java/com/juick/api/controllers/SkypeEndpoint.java @@ -1,24 +1,30 @@ -package com.juick.api; +package com.juick.api.controllers; import org.msbotframework4j.builder.bot.AbstractBot; import org.msbotframework4j.core.model.Message; import org.msbotframework4j.logging.BotLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * Created by vitalyster on 18.07.2016. */ +@Controller public class SkypeEndpoint extends AbstractBot { private static final Logger logger = LoggerFactory.getLogger(SkypeEndpoint.class); @Override protected Message onMessage(Message request, BotLogger logger) { return null; } - public void doPost(HttpServletRequest request) throws IOException { - logger.info(request.toString()); + + @RequestMapping(value = "/skypebotendpoint", method = RequestMethod.POST) + public void doPost(@RequestBody String body) throws IOException { + logger.info(body); } } diff --git a/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java b/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java new file mode 100644 index 00000000..5426f853 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java @@ -0,0 +1,69 @@ +package com.juick.api.controllers; + +import com.juick.Message; +import com.juick.User; +import com.juick.api.util.HttpBadRequestException; +import com.juick.api.util.HttpForbiddenException; +import com.juick.service.MessagesService; +import com.juick.service.SubscriptionService; +import com.juick.service.UserService; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.List; + +/** + * Created by vitalyster on 24.10.2016. + */ +@Controller +@ResponseBody +public class Subscriptions { + @Inject + UserService userService; + @Inject + SubscriptionService subscriptionService; + @Inject + MessagesService messagesService; + + @RequestMapping(value = "/subscriptions", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<User> doGet(HttpServletRequest request) throws IOException { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + User visitor = userService.getUserByUID(vuid).orElse(new User()); + if ((visitor.getUid() == 0) && !(visitor.getName().equals("juick"))) { + throw new HttpForbiddenException(); + } + int uid = NumberUtils.toInt(request.getParameter("uid"), 0); + int mid = NumberUtils.toInt(request.getParameter("mid"), 0); + 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-api/src/main/java/com/juick/api/controllers/TelegramWebhook.java b/juick-api/src/main/java/com/juick/api/controllers/TelegramWebhook.java new file mode 100644 index 00000000..8b491167 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/TelegramWebhook.java @@ -0,0 +1,77 @@ +package com.juick.api.controllers; + +import com.juick.User; +import com.juick.api.TGBot; +import com.juick.service.TelegramService; +import com.juick.service.UserService; +import com.pengrad.telegrambot.BotUtils; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.Update; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.inject.Inject; +import java.util.List; + +/** + * Created by vt on 24/11/2016. + */ +@Controller +public class TelegramWebhook { + private static Logger logger = LoggerFactory.getLogger(TelegramWebhook.class); + @Inject + UserService usersService; + @Inject + TelegramService telegramService; + @Inject + TGBot tgBot; + + + @RequestMapping(value = "/tlgmbtwbhk", method = RequestMethod.POST) + public void processUpdate(@RequestBody String updateData) { + Update update = BotUtils.parseUpdate(updateData); + Message message = update.message(); + if (update.message() == null) { + message = update.editedMessage(); + if (message == null) { + logger.error("error parsing telegram update: " + update.toString()); + return; + } + } + logger.info(String.format("got telegram msg %s", message.toString())); + User user_from = usersService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(new User()); + logger.info(String.format("Found juick user %d", user_from.getUid())); + + List<Long> 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); + tgBot.telegramSignupNotify(message.from().id().longValue(), usersService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); + } else { + if (user_from.getUid() == 0) { + tgBot.telegramSignupNotify(message.from().id().longValue(), usersService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); + } else { + String text = message.text(); + if (text != null) { + if (text.equalsIgnoreCase("/login")) { + String msg = String.format("Hi, %s!\nTap to log in", user_from.getName()); + String msgUrl = "http://juick.com/login?" + usersService.getHashByUID(user_from.getUid()); + tgBot.telegramNotify(message.from().id().longValue(), msg, msgUrl); + } /* else { + ProtocolReply reply = protocol.getReply(user_from, text); + telegramNotify(message.from().id().longValue(), reply.getDescription(), null); + }*/ + } + } + } + } +} diff --git a/juick-api/src/main/java/com/juick/api/controllers/Users.java b/juick-api/src/main/java/com/juick/api/controllers/Users.java new file mode 100644 index 00000000..8b1bc6cd --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/controllers/Users.java @@ -0,0 +1,159 @@ +package com.juick.api.controllers; + +import com.juick.User; +import com.juick.api.util.HttpForbiddenException; +import com.juick.api.util.HttpNotFoundException; +import com.juick.service.UserService; +import com.juick.util.UserUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * + * @author ugnich + */ +@Controller +@ResponseBody +public class Users { + + @Inject + UserService userService; + + @RequestMapping(value = "/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<User> doGetUsers(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + List<com.juick.User> users = new ArrayList<>(); + + String punames[] = request.getParameterValues("uname"); + if (punames != null) { + ArrayList<String> unames = new ArrayList<>(Arrays.asList(punames)); + Iterator<String> i = unames.iterator(); + while (i.hasNext()) { + if (!i.next().matches("^[a-zA-Z0-9\\-]{2,16}$")) { + i.remove(); + } + } + if (!unames.isEmpty() && unames.size() < 20) { + users.addAll(userService.getUsersByName(unames)); + } + } + + String pjids[] = request.getParameterValues("jid"); + if (pjids != null) { + List<String> jids = new ArrayList<>(Arrays.asList(pjids)); + Iterator<String> ii = jids.iterator(); + while (ii.hasNext()) { + if (!ii.next().matches("^[a-zA-Z0-9\\-\\_\\@\\.]{6,64}$")) { + ii.remove(); + } + } + if (!jids.isEmpty() && jids.size() < 20) { + users.addAll(userService.getUsersByJID(jids)); + } + } + + if (!users.isEmpty()) { + return users; + } + throw new HttpNotFoundException(); + } + + @RequestMapping(value = "/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public List<User> doGetUserRead(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int uid = 0; + String uname = request.getParameter("uname"); + if (uname == null) { + uid = vuid; + } else { + if (UserUtils.checkUserNameValid(uname)) { + com.juick.User u = userService.getUserByName(uname); + if (u != null && u.getUid() > 0) { + uid = u.getUid(); + } + } + } + + if (uid > 0) { + List<Integer> uids = userService.getUserRead(uid); + if (uids.size() > 0) { + List<com.juick.User> 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<User> doGetUserReaders(HttpServletRequest request) { + // TODO: use spring-security + String auth = request.getHeader("Authorization"); + int vuid = userService.getUIDByHttpAuth(auth); + if (vuid == -1) { + throw new HttpForbiddenException(); + } + if (vuid == 0) { + String hash = request.getParameter("hash"); + if (hash != null && hash.length() == 16) { + vuid = userService.getUIDbyHash(hash); + } + } + if (vuid == 0) { + throw new HttpForbiddenException(); + } + int uid = 0; + String uname = request.getParameter("uname"); + if (uname == null) { + uid = vuid; + } else { + if (UserUtils.checkUserNameValid(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-api/src/main/java/com/juick/api/util/HttpBadRequestException.java b/juick-api/src/main/java/com/juick/api/util/HttpBadRequestException.java new file mode 100644 index 00000000..3a42e999 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/util/HttpBadRequestException.java @@ -0,0 +1,12 @@ +package com.juick.api.util; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class HttpBadRequestException extends RuntimeException { + +} diff --git a/juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java b/juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java new file mode 100644 index 00000000..d8f4ddf3 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java @@ -0,0 +1,12 @@ +package com.juick.api.util; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.FORBIDDEN) +public class HttpForbiddenException extends RuntimeException { + +} diff --git a/juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java b/juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java new file mode 100644 index 00000000..14f34cf0 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java @@ -0,0 +1,12 @@ +package com.juick.api.util; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class HttpNotFoundException extends RuntimeException { + +} diff --git a/juick-api/src/main/java/com/juick/api/Utils.java b/juick-api/src/main/java/com/juick/api/util/HttpUtils.java index c324a2df..f20e10a8 100644 --- a/juick-api/src/main/java/com/juick/api/Utils.java +++ b/juick-api/src/main/java/com/juick/api/util/HttpUtils.java @@ -15,10 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package com.juick.api; - -import com.juick.server.UserQueries; -import org.springframework.jdbc.core.JdbcTemplate; +package com.juick.api.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; @@ -26,34 +23,13 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; -import java.util.Base64; import java.util.UUID; /** * * @author Ugnich Anton */ -public class Utils { - - public static int getHttpAuthUID(JdbcTemplate sql, HttpServletRequest request) { - String auth = request.getHeader("Authorization"); - if (auth != null && auth.length() > 8 && auth.startsWith("Basic ")) { - Base64.Decoder dec = Base64.getDecoder(); - String loginpassw[] = new String(dec.decode(auth.substring(6))).split(":", 2); - if (loginpassw.length == 2 && loginpassw[0].length() > 1 && loginpassw[0].length() < 16 && loginpassw[0].matches("[a-zA-Z0-9\\-]+") && !loginpassw[1].isEmpty()) { - return UserQueries.checkPassword(sql, loginpassw[0], loginpassw[1]); - } - } - return 0; - } - - public static int getVisitorQueryStringUID(JdbcTemplate sql, HttpServletRequest request) { - String hash = request.getParameter("hash"); - if (hash != null && hash.length() == 16) { - return com.juick.server.UserQueries.getUIDbyHash(sql, hash); - } - return 0; - } +public class HttpUtils { public static String getPartFilename(Part part) { for (String cd : part.getHeader("content-disposition").split(";")) { if (cd.trim().startsWith("filename")) { @@ -68,7 +44,7 @@ public class Utils { Part filePart = request.getPart("attach"); if (filePart != null) { - String partname = Utils.getPartFilename(filePart); + String partname = HttpUtils.getPartFilename(filePart); if (partname != null && partname.length() > 0) { String attachmentType = partname.substring(partname.length() - 3).toLowerCase(); if (attachmentType.equals("jpg") || attachmentType.equals("peg") || attachmentType.equals("png")) { diff --git a/juick-api/src/main/webapp/WEB-INF/web.xml b/juick-api/src/main/webapp/WEB-INF/web.xml index 533a6882..7b33aefc 100644 --- a/juick-api/src/main/webapp/WEB-INF/web.xml +++ b/juick-api/src/main/webapp/WEB-INF/web.xml @@ -1,16 +1,3 @@ <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> - <servlet> - <servlet-name>Main</servlet-name> - <servlet-class>com.juick.api.Main</servlet-class> - </servlet> - <servlet-mapping> - <servlet-name>Main</servlet-name> - <url-pattern>/</url-pattern> - </servlet-mapping> - <session-config> - <session-timeout> - 30 - </session-timeout> - </session-config> </web-app> diff --git a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java b/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java index 25ce61fb..5019ef0c 100644 --- a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java +++ b/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java @@ -459,25 +459,37 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ @Transactional(readOnly = true) @Override public List<Integer> getMyFeed(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); + List<Integer> mids; + if (before > 0) { + mids = getJdbcTemplate().queryForList("SELECT message_id FROM messages " + + "INNER JOIN subscr_users ON (subscr_users.suser_id=? AND subscr_users.user_id=messages.user_id) " + + "WHERE message_id<? AND (privacy>=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + + "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", + Integer.class, uid, before, uid); + } else { + mids = getJdbcTemplate().queryForList("SELECT message_id FROM messages " + + "INNER JOIN subscr_users ON (subscr_users.suser_id=? " + + "AND subscr_users.user_id=messages.user_id) " + + "WHERE (privacy>=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + + "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", + Integer.class, uid, uid); + } - List<Integer> mids = getNamedParameterJdbcTemplate().queryForList( - "(SELECT message_id FROM messages " + - " INNER JOIN subscr_users ON (subscr_users.suser_id = :uid AND subscr_users.user_id = messages.user_id) " + - " WHERE " + - (before > 0 ? - " message_id < :before AND " : "") + - " (privacy >= 0 OR (privacy >= -2 AND privacy <= -1" + - " AND EXISTS (SELECT 1 FROM wl_users w WHERE w.wl_user_id = :uid and w.user_id = messages.user_id)))) " + - " UNION " + - " (SELECT message_id FROM messages WHERE user_id=:uid " + - (before > 0 ? - " AND message_id < :before " : "") + - " ORDER BY message_id ASC LIMIT 20", - sqlParameterSource, - Integer.class); + + if (before > 0) { + mids.addAll(getJdbcTemplate().queryForList("SELECT message_id FROM messages " + + "WHERE user_id=? AND message_id<? ORDER BY message_id DESC LIMIT 20", Integer.class, + uid, before)); + } else { + mids.addAll(getJdbcTemplate().queryForList("SELECT message_id FROM messages " + + "WHERE user_id=? ORDER BY message_id DESC LIMIT 20", Integer.class, uid)); + } + + mids.sort(Collections.reverseOrder()); + int remove = mids.size() - 20; + for (int i = 0; i < remove; i++) { + mids.remove(20); + } return mids; } diff --git a/juick-server/src/main/java/com/juick/service/TelegramService.java b/juick-server/src/main/java/com/juick/service/TelegramService.java new file mode 100644 index 00000000..d5aa4fa8 --- /dev/null +++ b/juick-server/src/main/java/com/juick/service/TelegramService.java @@ -0,0 +1,15 @@ +package com.juick.service; + +import java.util.List; + +/** + * Created by vt on 24/11/2016. + */ +public interface TelegramService { + void addChat(Long id); + List<Long> getChats(); + int getUser(long tgId); + boolean createTelegramUser(long tgID, String tgName); + List<Long> getSubscribers(int uid); + List<Long> getSubscribersToComments(int mid, int ignore_uid); +} diff --git a/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java b/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java new file mode 100644 index 00000000..d6fabc96 --- /dev/null +++ b/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java @@ -0,0 +1,58 @@ +package com.juick.service; + +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import javax.inject.Inject; +import java.util.List; +import java.util.UUID; + +/** + * Created by vt on 24/11/2016. + */ +@Repository +public class TelegramServiceImpl extends BaseJdbcService implements TelegramService { + @Inject + protected TelegramServiceImpl(JdbcTemplate jdbc) { + super(jdbc, null); + } + + @Override + public void addChat(Long id) { + getJdbcTemplate().update("INSERT IGNORE INTO telegram_chats(chat_id) VALUES(?)", id); + } + + @Override + public List<Long> getChats() { + return getJdbcTemplate().queryForList("SELECT chat_id FROM telegram_chats", Long.class); + } + + @Override + public int getUser(long tgId) { + try { + return getJdbcTemplate().queryForObject("SELECT id FROM users INNER JOIN telegram " + + "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + @Override + public boolean createTelegramUser(long tgID, String tgName) { + return getJdbcTemplate().update("INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", + tgID, tgName, UUID.randomUUID().toString()) > 0; + } + + @Override + public List<Long> getSubscribers(int uid) { + return getJdbcTemplate().queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_users " + + "ON (subscr_users.user_id=? AND telegram.user_id=subscr_users.suser_id)", Long.class, uid); + } + + @Override + public List<Long> getSubscribersToComments(int mid, int ignore_uid) { + return getJdbcTemplate().queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_messages " + + "ON (telegram.user_id=subscr_messages.suser_id) WHERE message_id=? AND suser_id!=?", Long.class, mid, ignore_uid); + } +} diff --git a/juick-server/src/main/java/com/juick/service/UserService.java b/juick-server/src/main/java/com/juick/service/UserService.java index bc7742cb..cfd5092a 100644 --- a/juick-server/src/main/java/com/juick/service/UserService.java +++ b/juick-server/src/main/java/com/juick/service/UserService.java @@ -48,6 +48,8 @@ public interface UserService { String getHashByUID(int uid); + int getUIDByHttpAuth(String header); + int checkPassword(String username, String password); boolean updatePassword(User user, String newPassword); diff --git a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java b/juick-server/src/main/java/com/juick/service/UserServiceImpl.java index 8bd3adef..a84f2fc3 100644 --- a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java +++ b/juick-server/src/main/java/com/juick/service/UserServiceImpl.java @@ -1,6 +1,7 @@ package com.juick.service; import com.juick.User; +import com.juick.server.UserQueries; import com.juick.server.helpers.Auth; import com.juick.server.helpers.EmailOpts; import com.juick.server.helpers.UserInfo; @@ -259,6 +260,18 @@ public class UserServiceImpl extends BaseJdbcService implements UserService { return list.get(0); } + @Override + public int getUIDByHttpAuth(String auth) { + if (auth != null && auth.length() > 8 && auth.startsWith("Basic ")) { + Base64.Decoder dec = Base64.getDecoder(); + String loginpassw[] = new String(dec.decode(auth.substring(6))).split(":", 2); + if (loginpassw.length == 2 && loginpassw[0].length() > 1 && loginpassw[0].length() < 16 && loginpassw[0].matches("[a-zA-Z0-9\\-]+") && !loginpassw[1].isEmpty()) { + return checkPassword(loginpassw[0], loginpassw[1]); + } + } + return 0; + } + @Transactional(readOnly = true) @Override public int checkPassword(final String username, final String password) { |