diff options
Diffstat (limited to 'src/main/java/com')
31 files changed, 1175 insertions, 1033 deletions
diff --git a/src/main/java/com/juick/CrosspostComponent.java b/src/main/java/com/juick/CrosspostComponent.java index d5f13ab2..8b07583f 100644 --- a/src/main/java/com/juick/CrosspostComponent.java +++ b/src/main/java/com/juick/CrosspostComponent.java @@ -24,6 +24,9 @@ import com.juick.xmpp.Stream; import com.juick.xmpp.StreamComponent; import com.juick.xmpp.extensions.JuickMessage; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -35,11 +38,6 @@ import java.net.Socket; import java.net.URL; import java.net.URLEncoder; import java.security.Key; -import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Enumeration; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -61,7 +59,7 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea public final static String TWITTERURL = "https://api.twitter.com/1.1/statuses/update.json"; public final static String FBURL = "https://graph.facebook.com/me/feed"; public final static String VKURL = "https://api.vk.com/method/wall.post"; - Connection sql; + JdbcTemplate sql; Stream xmpp; String twitter_consumer_key; String twitter_consumer_secret; @@ -80,8 +78,7 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea twitter_consumer_key = conf.getProperty("twitter_consumer_key", ""); twitter_consumer_secret = conf.getProperty("twitter_consumer_secret", ""); - setupSql(conf.getProperty("mysql_host"), conf.getProperty("mysql_username"), - conf.getProperty("mysql_password", ""), conf.getProperty("mysql_database", "")); + setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); setupXmppComponent(conf.getProperty("xmpp_password", "")); } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); @@ -91,39 +88,15 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea @Override public void contextDestroyed(ServletContextEvent sce) { - // Now deregister JDBC drivers in this context's ClassLoader: - // Get the webapp's ClassLoader - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - // Loop through all drivers - Enumeration<Driver> drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - if (driver.getClass().getClassLoader() == cl) { - // This driver was registered by the webapp's ClassLoader, so deregister it: - try { - logger.info(String.format("Deregistering JDBC driver %s", driver.toString())); - DriverManager.deregisterDriver(driver); - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Error deregistering JDBC driver %s", driver), ex); - } - } else { - // driver was not registered by the webapp's ClassLoader and may be in use elsewhere - logger.log(Level.SEVERE, String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver)); - } - } executorService.shutdown(); logger.info("component destroyed"); } - public void setupSql(String host, String username, String password, String database) { - try { - Class.forName("com.mysql.jdbc.Driver"); - sql = DriverManager.getConnection( - String.format("jdbc:mysql://%s/%s?autoReconnect=true&user=%s&password=%s", - host, database, username, password)); - } catch (SQLException | ClassNotFoundException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } + public void setupSql(String driver, String url) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUrl(url); + sql = new JdbcTemplate(dataSource); } public void setupXmppComponent(String password) { @@ -161,8 +134,8 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea } public boolean facebookPost(com.juick.Message jmsg) { - String token = CrosspostQueries.getFacebookToken(sql, jmsg.getUser().getUID()); - if (token == null) { + String token = CrosspostQueries.getFacebookToken(sql, jmsg.getUser().getUID()).orElse(""); + if (token.isEmpty()) { return false; } @@ -198,8 +171,8 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea } public boolean vkontaktePost(com.juick.Message jmsg) { - String tokens[] = CrosspostQueries.getVKTokens(sql, jmsg.getUser().getUID()); - if (tokens == null || tokens.length != 2) { + Pair<String, String> tokens = CrosspostQueries.getVKTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); + if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { return false; } @@ -209,7 +182,7 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea boolean ret = false; try { - String body = "owner_id=" + tokens[0] + "&access_token=" + URLEncoder.encode(tokens[1], "UTF-8") + "&from_group=1&message=" + URLEncoder.encode(status, "UTF-8"); + String body = "owner_id=" + tokens.getLeft() + "&access_token=" + URLEncoder.encode(tokens.getRight(), "UTF-8") + "&from_group=1&message=" + URLEncoder.encode(status, "UTF-8"); HttpsURLConnection conn = (HttpsURLConnection) new URL(VKURL).openConnection(); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); @@ -235,12 +208,12 @@ public class CrosspostComponent implements ServletContextListener, Stream.Strea } public boolean twitterPost(com.juick.Message jmsg) { - String tokens[] = CrosspostQueries.getTwitterTokens(sql, jmsg.getUser().getUID()); - if (tokens == null || tokens.length != 2) { + Pair<String, String> tokens = CrosspostQueries.getTwitterTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); + if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { return false; } - String token = percentEncode(tokens[0]); - String token_secret = percentEncode(tokens[1]); + String token = percentEncode(tokens.getLeft()); + String token_secret = percentEncode(tokens.getRight()); logger.info("TWITTER: #" + jmsg.getMID()); diff --git a/src/main/java/com/juick/PushComponent.java b/src/main/java/com/juick/PushComponent.java index 386448f4..3bb7cdc5 100644 --- a/src/main/java/com/juick/PushComponent.java +++ b/src/main/java/com/juick/PushComponent.java @@ -22,8 +22,8 @@ import com.google.android.gcm.server.MulticastResult; import com.google.android.gcm.server.Result; import com.google.android.gcm.server.Sender; import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; import com.juick.server.PushQueries; +import com.juick.server.SubscriptionsQueries; import com.juick.xmpp.JID; import com.juick.xmpp.Message.MessageListener; import com.juick.xmpp.Stream; @@ -45,21 +45,23 @@ import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.apache.http.util.TextUtils; import org.json.JSONObject; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.io.IOException; import java.net.Socket; -import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * @@ -72,7 +74,7 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe private ExecutorService executorService; String wns_application_sip; String wns_client_secret; - Connection sql; + JdbcTemplate sql; Socket socket; Stream xmpp; Sender GCMSender; @@ -91,8 +93,7 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe wns_client_secret = conf.getProperty("wns_client_secret", ""); GCMSender = new Sender(conf.getProperty("gcm_key")); - setupSql(conf.getProperty("mysql_host"), conf.getProperty("mysql_username"), - conf.getProperty("mysql_password", ""), conf.getProperty("mysql_database", "")); + setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); setupXmppComponent(new JID("", conf.getProperty("push_jid"), ""), conf.getProperty("xmpp_host", "localhost"), Integer.parseInt(conf.getProperty("xmpp_port", "5347")), conf.getProperty("push_xmpp_password", "")); } catch (IOException e) { @@ -103,39 +104,15 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe @Override public void contextDestroyed(ServletContextEvent sce) { - // Now deregister JDBC drivers in this context's ClassLoader: - // Get the webapp's ClassLoader - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - // Loop through all drivers - Enumeration<Driver> drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - if (driver.getClass().getClassLoader() == cl) { - // This driver was registered by the webapp's ClassLoader, so deregister it: - try { - logger.info(String.format("Deregistering JDBC driver %s", driver.toString())); - DriverManager.deregisterDriver(driver); - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Error deregistering JDBC driver %s", driver), ex); - } - } else { - // driver was not registered by the webapp's ClassLoader and may be in use elsewhere - logger.log(Level.SEVERE, String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver)); - } - } executorService.shutdown(); logger.info("component destroyed"); } - public void setupSql(String host, String username, String password, String database) { - try { - Class.forName("com.mysql.jdbc.Driver"); - sql = DriverManager.getConnection( - String.format("jdbc:mysql://%s/%s?autoReconnect=true&user=%s&password=%s", - host, database, username, password)); - } catch (SQLException | ClassNotFoundException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } + public void setupSql(String driver, String url) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUrl(url); + sql = new JdbcTemplate(dataSource); } public void setupXmppComponent(JID jid, String host, int port, String password) { @@ -161,38 +138,32 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe @Override public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); - if (jmsg == null) { - return; - } - logger.info("Message to push: " + msg.toString()); - + JuickMessage jmsg = (JuickMessage)msg.getChild(JuickMessage.XMLNS); + List<User> subscribedUsers = new ArrayList<>(); boolean isPM = jmsg.getMID() == 0; boolean isReply = jmsg.getRID() > 0; - int senderID = 0, recipientID = 0; - if (isReply) { - senderID = jmsg.getUser().getUID(); - } + int pmTo = 0; if (isPM) { - // PM - try { - recipientID = Integer.parseInt(msg.to.Username); - } catch (NumberFormatException e) { - logger.info("Wrong PM recipient: " + msg.to.Username); - return; + pmTo = Integer.parseInt(msg.to.Username); + } else { + if (isReply) { + subscribedUsers = + SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); + } else { + // new message + subscribedUsers = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); } } /*** ANDROID ***/ - List<String> regids; + final List<String> regids = new ArrayList<>(); if (isPM) { - regids = new ArrayList<>(); - String targetId = PushQueries.getAndroidRegID(sql, recipientID); - if (targetId != null && !targetId.isEmpty()) { - regids.add(targetId); - } + PushQueries.getAndroidRegID(sql, pmTo).ifPresent(regids::add); } else { - regids = isReply ? PushQueries.getAndroidSubscribersToComments(sql, jmsg.getMID(), senderID) : PushQueries.getAndroidSubscribers(sql, senderID); + List<Integer> uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + regids.addAll(PushQueries.getAndroidTokens(sql, uids)); + } } if (!regids.isEmpty()) { @@ -216,15 +187,14 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe } /*** WinPhone ***/ - List<String> urls; + final List<String> urls = new ArrayList<>(); if (isPM) { - urls = new ArrayList<>(); - String targetURL = PushQueries.getWinPhoneURL(sql, recipientID); - if (!TextUtils.isEmpty(targetURL)) { - urls.add(targetURL); - } + PushQueries.getWinPhoneURL(sql, pmTo).ifPresent(urls::add); } else { - urls = isReply ? PushQueries.getWindowsSubscribersToComments(sql, jmsg.getMID(), senderID) :PushQueries.getWinPhoneSubscribers(sql, senderID); + List<Integer> uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + urls.addAll(PushQueries.getWindowsTokens(sql, uids)); + } } @@ -262,15 +232,14 @@ public class PushComponent implements ServletContextListener, Stream.StreamListe } /*** iOS ***/ - List<String> tokens; + final List<String> tokens = new ArrayList<>(); if (isPM) { - tokens = new ArrayList<>(); - String targetToken = PushQueries.getAPNSToken(sql, recipientID); - if (targetToken != null && !targetToken.isEmpty()) { - tokens.add(targetToken); - } + PushQueries.getAPNSToken(sql, pmTo).ifPresent(tokens::add); } else { - tokens = isReply ? PushQueries.getAppleSubscribersToComments(sql, jmsg.getMID(), senderID) : PushQueries.getAPNSSubscribers(sql, senderID); + List<Integer> uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + tokens.addAll(PushQueries.getAPNSTokens(sql, uids)); + } } if (!tokens.isEmpty()) { ApnsService service = APNS.newService().withCert("/etc/juick/ios.p12", "juick") diff --git a/src/main/java/com/juick/http/www/Discover.java b/src/main/java/com/juick/http/www/Discover.java index e7d85d8a..bdb86380 100644 --- a/src/main/java/com/juick/http/www/Discover.java +++ b/src/main/java/com/juick/http/www/Discover.java @@ -20,6 +20,8 @@ package com.juick.http.www; import com.juick.server.AdsQueries; import com.juick.server.MessagesQueries; import com.juick.server.TagQueries; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; @@ -37,7 +39,7 @@ import javax.servlet.http.HttpServletResponse; */ public class Discover { - protected void doGet(Connection sql, Connection sqlSearch, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); String paramTagStr = URLDecoder.decode(request.getRequestURI().substring(5), "UTF-8"); diff --git a/src/main/java/com/juick/http/www/Errors.java b/src/main/java/com/juick/http/www/Errors.java index f65b6201..0044c209 100644 --- a/src/main/java/com/juick/http/www/Errors.java +++ b/src/main/java/com/juick/http/www/Errors.java @@ -1,11 +1,12 @@ package com.juick.http.www; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Connection; +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.io.PrintWriter; /** * @@ -15,17 +16,16 @@ public class Errors { public static String tagsHTML = null; - public static void doGet404(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + public static void doGet404(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (tagsHTML == null) { - tagsHTML = PageTemplates.getPopularTags(sql, 80); + tagsHTML = PageTemplates.formatPopularTags(sql, 80); } response.setStatus(404); response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, "404 Страница не найдена", null); PageTemplates.pageNavigation(out, visitor, null); PageTemplates.pageHomeColumn(out, sql, visitor); @@ -37,8 +37,6 @@ public class Errors { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } } diff --git a/src/main/java/com/juick/http/www/FacebookLogin.java b/src/main/java/com/juick/http/www/FacebookLogin.java index fcb08d6c..cf444abc 100644 --- a/src/main/java/com/juick/http/www/FacebookLogin.java +++ b/src/main/java/com/juick/http/www/FacebookLogin.java @@ -18,20 +18,19 @@ package com.juick.http.www; import com.juick.server.UserQueries; +import org.json.JSONObject; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.json.JSONObject; /** * @@ -45,7 +44,7 @@ public class FacebookLogin { private static final String FACEBOOK_SECRET = "95813bfb6ab8f473410c50d4f971649e"; private static final String FACEBOOK_REDIRECT = "http://juick.com/_fblogin"; - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fbstate; String code = request.getParameter("code"); @@ -132,61 +131,22 @@ public class FacebookLogin { } } - private int getUIDbyFBID(Connection sql, long fbID) { - int uid = 0; - PreparedStatement stmt = null; - ResultSet rs = null; + private int getUIDbyFBID(JdbcTemplate sql, long fbID) { try { - stmt = sql.prepareStatement("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL"); - stmt.setLong(1, fbID); - rs = stmt.executeQuery(); - if (rs.first()) { - uid = rs.getInt(1); - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + return sql.queryForObject("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL", + Integer.class, fbID); + } catch (EmptyResultDataAccessException e) { + return 0; } - return uid; } - private boolean insertDB(Connection sql, long fbID, String loginhash, String token, String fbName, String fbLink) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("INSERT INTO facebook(fb_id,loginhash,access_token,fb_name,fb_link) VALUES (?,?,?,?,?)"); - stmt.setLong(1, fbID); - stmt.setString(2, loginhash); - stmt.setString(3, token); - stmt.setString(4, fbName); - stmt.setString(5, fbLink); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean insertDB(JdbcTemplate sql, long fbID, String loginhash, String token, String fbName, String fbLink) { + return sql.update("INSERT INTO facebook(fb_id,loginhash,access_token,fb_name,fb_link) VALUES (?,?,?,?,?)", + fbID, loginhash, token, fbName, fbLink) > 0; } - private boolean updateDB(Connection sql, long fbID, String token, String fbName, String fbLink) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?"); - stmt.setString(1, token); - stmt.setString(2, fbName); - stmt.setString(3, fbLink); - stmt.setLong(4, fbID); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean updateDB(JdbcTemplate sql, long fbID, String token, String fbName, String fbLink) { + return sql.update("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?", + token, fbName, fbLink, fbID) > 0; } } diff --git a/src/main/java/com/juick/http/www/Help.java b/src/main/java/com/juick/http/www/Help.java index 438fc3a9..503044e6 100644 --- a/src/main/java/com/juick/http/www/Help.java +++ b/src/main/java/com/juick/http/www/Help.java @@ -17,15 +17,12 @@ */ package com.juick.http.www; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Connection; +import org.springframework.jdbc.core.JdbcTemplate; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.*; /** * @@ -33,11 +30,11 @@ import javax.servlet.http.HttpServletResponse; */ public class Help { - protected void doRedirectToHelpIndex(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doRedirectToHelpIndex(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Utils.sendTemporaryRedirect(response, "/help/ru/"); } - protected void doGetHelp(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGetHelp(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); String path[] = request.getRequestURI().split("/"); @@ -64,8 +61,7 @@ public class Help { } response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, "Помощь", null); PageTemplates.pageNavigation(out, visitor, null); @@ -81,8 +77,6 @@ public class Help { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } diff --git a/src/main/java/com/juick/http/www/Home.java b/src/main/java/com/juick/http/www/Home.java index d60e871d..d5c1bb7b 100644 --- a/src/main/java/com/juick/http/www/Home.java +++ b/src/main/java/com/juick/http/www/Home.java @@ -19,6 +19,8 @@ package com.juick.http.www; import com.juick.server.AdsQueries; import com.juick.server.MessagesQueries; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; @@ -35,7 +37,7 @@ import javax.servlet.http.HttpServletResponse; */ public class Home { - protected void doGet(Connection sql, Connection sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { int paramBefore = 0; String paramBeforeStr = request.getParameter("before"); if (paramBeforeStr != null) { @@ -65,6 +67,7 @@ public class Home { title = "Микроблоги Juick: популярные записи"; mids = MessagesQueries.getPopular(sql, 0, paramBefore); } + } else if (paramShow.equals("top")) { Utils.sendPermanentRedirect(response, "/"); return; diff --git a/src/main/java/com/juick/http/www/Login.java b/src/main/java/com/juick/http/www/Login.java index 3e9c5e09..f12c7096 100644 --- a/src/main/java/com/juick/http/www/Login.java +++ b/src/main/java/com/juick/http/www/Login.java @@ -17,11 +17,10 @@ */ package com.juick.http.www; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -33,7 +32,7 @@ import javax.servlet.http.HttpServletResponse; */ public class Login { - protected void doGetLoginForm(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGetLoginForm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (visitor != null) { Utils.sendTemporaryRedirect(response, "/"); @@ -41,8 +40,7 @@ public class Login { } response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { out.println("<!DOCTYPE html>"); out.println("<html>"); out.println("<head>"); @@ -182,12 +180,10 @@ public class Login { out.println("</body>"); out.println("</html>"); - } finally { - out.close(); } } - protected void doGetLogin(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGetLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String hash = request.getQueryString(); if (hash.length() > 32) { response.sendError(400); @@ -204,7 +200,7 @@ public class Login { } } - protected void doPostLogin(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doPostLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null || password == null || username.length() > 32 || password.isEmpty()) { @@ -230,19 +226,10 @@ public class Login { } } - protected void doGetLogout(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGetLogout(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (visitor != null) { - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("DELETE FROM logins WHERE user_id=?"); - stmt.setInt(1, visitor.getUID()); - stmt.executeUpdate(); - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } + sql.update("DELETE FROM logins WHERE user_id=?", visitor.getUID()); } Cookie c = new Cookie("hash", "-"); diff --git a/src/main/java/com/juick/http/www/Main.java b/src/main/java/com/juick/http/www/Main.java index 7896c207..63bf1c1f 100644 --- a/src/main/java/com/juick/http/www/Main.java +++ b/src/main/java/com/juick/http/www/Main.java @@ -21,6 +21,8 @@ import com.juick.server.UserQueries; import com.juick.xmpp.JID; import com.juick.xmpp.Stream; import com.juick.xmpp.StreamComponent; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import ru.sape.Sape; import javax.servlet.ServletException; @@ -32,9 +34,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.Socket; import java.net.URLEncoder; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; import java.util.Properties; /** @@ -45,8 +44,8 @@ import java.util.Properties; @MultipartConfig(fileSizeThreshold = 1024 * 1024, maxRequestSize = 1024 * 1024 * 10) public class Main extends HttpServlet implements Stream.StreamListener { - Connection sql; - Connection sqlSearch; + JdbcTemplate sql; + JdbcTemplate sqlSearch; String sqlSearchConnStr = "jdbc:mysql://127.0.0.1:9306?autoReconnect=true&useUnicode=yes&characterEncoding=utf8&maxAllowedPacket=512000"; Stream xmpp; Home home = new Home(); @@ -72,9 +71,14 @@ public class Main extends HttpServlet implements Stream.StreamListener { Properties conf = new Properties(); conf.load(getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - Class.forName("com.mysql.jdbc.Driver"); - sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + conf.getProperty("mysql_username", "") + "&password=" + conf.getProperty("mysql_password", "")); - sqlSearch = null; // init this on search, timeout is too low + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(conf.getProperty("datasource_url")); + DriverManagerDataSource dataSourceSearch = new DriverManagerDataSource(); + dataSourceSearch.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSourceSearch.setUrl(sqlSearchConnStr); + sql = new JdbcTemplate(dataSource); + sqlSearch = new JdbcTemplate(dataSourceSearch); setupXmppComponent(conf.getProperty("xmpp_password")); twitterAuth = new TwitterAuth(conf.getProperty("twitter_consumer_key"), @@ -84,27 +88,7 @@ public class Main extends HttpServlet implements Stream.StreamListener { log(null, e); } } - public void closeSqlSearch() { - if (sqlSearch != null) { - try { - sqlSearch.close(); - sqlSearch = null; - } catch (SQLException e) { - log("An error on closing sql search connection", e); - } - } - }; - public Connection getSqlSearch() { - if (sqlSearch == null) { - try { - sqlSearch = DriverManager.getConnection(sqlSearchConnStr, "", ""); - } - catch (Exception e) { - log("Couldn't open sqlSearch connection",e); - } - } - return sqlSearch; - } + public void setupXmppComponent(final String password) { Thread thr = new Thread(() -> { try { @@ -120,33 +104,13 @@ public class Main extends HttpServlet implements Stream.StreamListener { } @Override - public void onStreamFail(Exception e) {log("XMPP STREAM FAIL", e);} + public void onStreamFail(Exception e) {log("XMPP STREAM FAIL:" + e);} @Override public void onStreamReady() { log("XMPP STREAM READY"); } - @Override - public void destroy() { - super.destroy(); - if (sql != null) { - try { - sql.close(); - sql = null; - } catch (SQLException e) { - log(null, e); - } - } - if (sqlSearch != null) { - try { - sqlSearch.close(); - sqlSearch = null; - } catch (SQLException e) { - log(null, e); - } - } - } /** * Handles the HTTP <code>GET</code> method. @@ -168,8 +132,7 @@ public class Main extends HttpServlet implements Stream.StreamListener { Utils.sendPermanentRedirect(response, "/tag/" + URLEncoder.encode(tag, "UTF-8")); } else { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - home.doGet(sql, getSqlSearch(), request, response, visitor); - closeSqlSearch(); + home.doGet(sql, sqlSearch, request, response, visitor); } } else if (uri.equals("/post")) { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); @@ -226,8 +189,7 @@ public class Main extends HttpServlet implements Stream.StreamListener { } else if (uri.startsWith("/help/")) { help.doGetHelp(sql, request, response); } else if (uri.startsWith("/tag/")) { - discover.doGet(sql, getSqlSearch(), request, response); - closeSqlSearch(); + discover.doGet(sql, sqlSearch, request, response); } else if (uri.matches("^/\\d+$")) { String strID = request.getRequestURI().substring(1); int mid = 0; @@ -255,8 +217,7 @@ public class Main extends HttpServlet implements Stream.StreamListener { com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, uriparts[1]); if (user != null && user.getUName().equals(uriparts[1]) && !user.Banned) { if (uriparts.length == 2) { // http://juick.com/username/ - pagesUser.doGetBlog(sql, getSqlSearch(), request, response, user); - closeSqlSearch(); + pagesUser.doGetBlog(sql, sqlSearch, request, response, user); } else if (uriparts[2].equals("tags")) { pagesUser.doGetTags(sql, request, response, user); } else if (uriparts[2].equals("friends")) { diff --git a/src/main/java/com/juick/http/www/NewMessage.java b/src/main/java/com/juick/http/www/NewMessage.java index 86725db7..642bd794 100644 --- a/src/main/java/com/juick/http/www/NewMessage.java +++ b/src/main/java/com/juick/http/www/NewMessage.java @@ -18,11 +18,7 @@ package com.juick.http.www; import com.juick.Tag; -import com.juick.server.CrosspostQueries; -import com.juick.server.MessagesQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; +import com.juick.server.*; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; @@ -30,17 +26,18 @@ import com.juick.xmpp.extensions.JuickMessage; import com.juick.xmpp.extensions.JuickUser; import com.juick.xmpp.extensions.Nickname; import com.juick.xmpp.extensions.XOOB; +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.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; -import java.sql.Connection; import java.util.ArrayList; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * @@ -48,10 +45,9 @@ import javax.servlet.http.HttpServletResponse; */ public class NewMessage { - protected void doGetNewMessage(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + protected void doGetNewMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, "Написать", "<script src=\"//maps.google.com/maps?file=api&v=2&sensor=false&key=ABQIAAAAVVtPtxkw4soCEHg44FsNChRB4OFYjAXt73He16Zkp6a_0tPs2RTU6i6UlcMs4QvPBYvIY8rWvcxqOg\" type=\"text/javascript\"></script>" + "<script src=\"//static.juick.com/mc.js\" type=\"text/javascript\" defer=\"defer\"></script>" + "<script src=\"//static.juick.com/maps.js?2010111500\" type=\"text/javascript\" defer=\"defer\"></script>" @@ -83,12 +79,10 @@ public class NewMessage { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } - void printUserTags(Connection sql, PrintWriter out, com.juick.User visitor) { + void printUserTags(JdbcTemplate sql, PrintWriter out, com.juick.User visitor) { List<Tag> tags = TagQueries.getUserTagsAll(sql, visitor.getUID()); if (tags.isEmpty()) { @@ -134,7 +128,7 @@ public class NewMessage { out.println("</p>"); } - public void doPostMessage(Connection sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + public void doPostMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { String body = request.getParameter("body"); if (body == null || body.length() < 1 || body.length() > 4096) { response.sendError(400); @@ -265,12 +259,12 @@ public class NewMessage { out.println("<section id=\"content\">"); out.println("<h1>Сообщение опубликовано</h1>"); out.println("<p>Поделитесь своим новым постом в социальных сетях:</p>"); - if (CrosspostQueries.getTwitterTokens(sql, visitor.getUID()) == null) { + if (CrosspostQueries.getTwitterTokens(sql, visitor.getUID()).isPresent()) { out.println("<p><a href=\"https://twitter.com/intent/tweet?text=" + URLEncoder.encode(sharetwi, "utf-8") + "\" onclick=\"return openSocialWindow(this)\" class=\"ico32-twi sharenew\">Отправить в Twitter</a></p>"); } out.println("<p><a href=\"http://www.livejournal.com/update.bml?subject=" + URLEncoder.encode(hashtags, "utf-8") + "&event=" + sharelj + "&prop_taglist=" + URLEncoder.encode(tagscomma, "utf-8") + "\" target=\"_blank\" class=\"ico32-lj sharenew\">Отправить в LiveJournal</a></p>"); out.println("<p><a href=\"https://vk.com/share.php?url=" + url + "\" onclick=\"return openSocialWindow(this)\" class=\"ico32-vk sharenew\">Отправить в ВКонтакте</a></p>"); - if (CrosspostQueries.getFacebookToken(sql, visitor.getUID()) == null) { + if (CrosspostQueries.getFacebookToken(sql, visitor.getUID()).isPresent()) { out.println("<p><a href=\"https://www.facebook.com/sharer/sharer.php?u=" + url + "\" onclick=\"return openSocialWindow(this)\" class=\"ico32-fb sharenew\">Отправить в Facebook</a></p>"); } out.println("<p><a href=\"https://plus.google.com/share?url=" + url + "\" onclick=\"return openSocialWindow(this)\" class=\"ico32-gp sharenew\">Отправить в Google+</a></p>"); @@ -282,7 +276,7 @@ public class NewMessage { } } - public void doPostComment(Connection sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + public void doPostComment(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { int mid = Utils.parseInt(request.getParameter("mid"), 0); if (mid == 0) { response.sendError(400); @@ -383,7 +377,7 @@ public class NewMessage { Utils.sendTemporaryRedirect(response, "/" + msg.getUser().getUName() + "/" + mid + "#" + ridnew); } - public void doPostRecomm(Connection sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + public void doPostRecomm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { int mid = Utils.parseInt(request.getParameter("mid"), 0); if (mid == 0) { response.sendError(400); diff --git a/src/main/java/com/juick/http/www/PM.java b/src/main/java/com/juick/http/www/PM.java index a9505bdb..932d1baf 100644 --- a/src/main/java/com/juick/http/www/PM.java +++ b/src/main/java/com/juick/http/www/PM.java @@ -23,14 +23,14 @@ import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; import com.juick.xmpp.extensions.JuickMessage; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Connection; -import java.util.ArrayList; -import java.util.List; +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.io.PrintWriter; +import java.util.List; /** * @@ -38,7 +38,7 @@ import javax.servlet.http.HttpServletResponse; */ public class PM { - protected void doGetInbox(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + protected void doGetInbox(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { /* int paramBefore = 0; String paramBeforeStr = request.getParameter("before"); @@ -54,8 +54,7 @@ public class PM { List<com.juick.Message> msgs = PMQueries.getLastPMInbox(sql, visitor.getUID()); response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, title, null); PageTemplates.pageNavigation(out, visitor, null); PageTemplates.pageHomeColumn(out, sql, visitor); @@ -64,8 +63,7 @@ public class PM { if (!msgs.isEmpty()) { out.println("<ul>"); - for (int i = msgs.size() - 1; i >= 0; i--) { - com.juick.Message msg = msgs.get(i); + for (com.juick.Message msg : msgs) { String txt = PageTemplates.formatMessage(msg.getText()); @@ -73,7 +71,7 @@ public class PM { out.println(" <div class=\"msg-avatar\"><a href=\"/" + msg.getUser().getUName() + "/\"><img src=\"//i.juick.com/a/" + msg.getUser().getUID() + ".png\" alt=\"" + msg.getUser().getUName() + "\"/></a></div>"); out.println(" <div class=\"msg-cont\">"); out.println(" <div class=\"msg-header\"><a href=\"/" + msg.getUser().getUName() + "/\">@" + msg.getUser().getUName() + "</a>:</div>"); - out.println(" <div class=\"msg-ts\"><a href=\"#\" onclick=\"return false\" title=\"" + msg.TimestampString + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.TimestampString) + "</a></div>"); + out.println(" <div class=\"msg-ts\"><a href=\"#\" onclick=\"return false\" title=\"" + PageTemplates.sdfSQL.format(msg.getDate()) + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>"); out.println(" <div class=\"msg-txt\">" + txt + "</div>"); out.println(" <form action=\"/pm/send\" method=\"POST\" enctype=\"multipart/form-data\"><input type=\"hidden\" name=\"uname\" value=\"" + msg.getUser().getUName() + "\"/>"); @@ -97,12 +95,10 @@ public class PM { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } - protected void doGetSent(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + protected void doGetSent(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { /* int paramBefore = 0; String paramBeforeStr = request.getParameter("before"); @@ -123,8 +119,7 @@ public class PM { } response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, title, null); PageTemplates.pageNavigation(out, visitor, null); PageTemplates.pageHomeColumn(out, sql, visitor); @@ -141,8 +136,7 @@ public class PM { if (!msgs.isEmpty()) { out.println("<ul>"); - for (int i = msgs.size() - 1; i >= 0; i--) { - com.juick.Message msg = msgs.get(i); + for (com.juick.Message msg : msgs) { String txt = PageTemplates.formatMessage(msg.getText()); @@ -150,7 +144,7 @@ public class PM { out.println(" <div class=\"msg-avatar\"><img src=\"//i.juick.com/a/" + visitor.getUID() + ".png\"/></div>"); out.println(" <div class=\"msg-cont\">"); out.println(" <div class=\"msg-header\">→ <a href=\"/" + msg.getUser().getUName() + "/\">@" + msg.getUser().getUName() + "</a>:</div>"); - out.println(" <div class=\"msg-ts\"><a href=\"#\" onclick=\"return false\" title=\"" + msg.TimestampString + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.TimestampString) + "</a></div>"); + out.println(" <div class=\"msg-ts\"><a href=\"#\" onclick=\"return false\" title=\"" + PageTemplates.sdfSQL.format(msg.getDate()) + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>"); out.println(" <div class=\"msg-txt\">" + txt + "</div>"); out.println(" </div>"); out.println(" </li>"); @@ -169,12 +163,10 @@ public class PM { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } - public void doPostPM(Connection sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + public void doPostPM(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { String uname = request.getParameter("uname"); if (uname.startsWith("@")) { uname = uname.substring(1); @@ -200,7 +192,7 @@ public class PM { msg.from = new JID("juick", "juick.com", null); msg.to = new JID(Integer.toString(uid), "push.juick.com", null); JuickMessage jmsg = new JuickMessage(); - jmsg.setUser(UserQueries.getUserByUID(sql, visitor.getUID())); + jmsg.setUser(visitor); jmsg.setText(body); msg.childs.add(jmsg); xmpp.send(msg); @@ -208,8 +200,8 @@ public class PM { msg.to.Host = "ws.juick.com"; xmpp.send(msg); - String jid = UserQueries.getJIDbyUID(sql, uid); - if (jid != null) { + List<String> jids = UserQueries.getJIDsbyUID(sql, uid); + for (String jid : jids) { Message mm = new Message(); mm.to = new JID(jid); mm.type = Message.Type.chat; diff --git a/src/main/java/com/juick/http/www/PageTemplates.java b/src/main/java/com/juick/http/www/PageTemplates.java index fedb11bf..7021ccba 100644 --- a/src/main/java/com/juick/http/www/PageTemplates.java +++ b/src/main/java/com/juick/http/www/PageTemplates.java @@ -20,6 +20,7 @@ package com.juick.http.www; import com.juick.Message; import com.juick.Tag; import com.juick.server.MessagesQueries; +import com.juick.server.TagQueries; import com.juick.server.UserQueries; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -35,7 +36,11 @@ import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StringUtils; import ru.sape.Sape; /** @@ -45,7 +50,7 @@ import ru.sape.Sape; public class PageTemplates { public static Sape sape = null; - private static final SimpleDateFormat sdfSQL = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + protected static final SimpleDateFormat sdfSQL = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static SimpleDateFormat sdfSimple = new SimpleDateFormat("d MMM"); private static SimpleDateFormat sdfFull = new SimpleDateFormat("d MMM yyyy"); private static String tagsHTML = null; @@ -105,13 +110,13 @@ public class PageTemplates { out.println("</header>"); } - public static void pageHomeColumn(PrintWriter out, Connection sql, com.juick.User visitor) { + public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor) { pageHomeColumn(out, sql, visitor, false); } - public static void pageHomeColumn(PrintWriter out, Connection sql, com.juick.User visitor, boolean showAdv) { + public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor, boolean showAdv) { if (tagsHTML == null) { - tagsHTML = PageTemplates.getPopularTags(sql, 80); + tagsHTML = PageTemplates.formatPopularTags(sql, 80); } out.println("<aside id=\"column\">"); @@ -126,64 +131,10 @@ public class PageTemplates { out.println("</aside>"); } - public static String getPopularTags(Connection sql, int cnt) { - String ret = ""; - - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement("SELECT name FROM tags WHERE top=1 ORDER BY name ASC"); - rs = stmt.executeQuery(); - rs.beforeFirst(); - while (rs.next()) { - if (!ret.isEmpty()) { - ret += " "; - } - try { - ret += "<a href=\"/tag/" + URLEncoder.encode(rs.getString(1), "UTF-8") + "\">" + Utils.encodeHTML(rs.getString(1)) + "</a>"; - } catch (UnsupportedEncodingException e) { - } - - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); - } - - return ret; - } - - public static void printContestRating(PrintWriter out, Connection sql) { - out.println("<hr/>"); - out.println("<!--noindex-->"); - out.println("<p style=\"font-size: 14pt\">Кто <a href=\"/help/ru/contest\">выиграет iPod</a>?</p>"); - out.println("<table width=\"100%\">"); - - int i = 0; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement("SELECT users.id,users.nick,COUNT(users_refs.user_id) AS cnt FROM users INNER JOIN users_refs ON users.id=users_refs.ref WHERE users.id>2 GROUP BY users_refs.ref ORDER BY cnt DESC LIMIT 10"); - rs = stmt.executeQuery(); - rs.beforeFirst(); - while (rs.next()) { - String uname = rs.getString(2); - if (i == 0) { - out.println(" <tr><td><b><a href=\"/" + uname + "/\">" + uname + "</a></b></td><td align=\"right\"><b>" + rs.getInt(3) + "</b></td></tr>"); - } else { - out.println(" <tr><td><a href=\"/" + uname + "/\">" + uname + "</a></td><td align=\"right\">" + rs.getInt(3) + "</td></tr>"); - } - i++; - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); - } - - out.println("</table>"); - out.println("<!--/noindex-->"); + public static String formatPopularTags(JdbcTemplate sql, int cnt) { + List<String> popularTags = TagQueries.getPopularTags(sql).stream() + .map(t -> "<a href=\"/tag/" + URLEncoder.encode(t) + "\">" + Utils.encodeHTML(t) + "</a>").collect(Collectors.toList()); + return StringUtils.collectionToDelimitedString(popularTags, " "); } public static void pageFooter(HttpServletRequest request, PrintWriter out, com.juick.User visitor, boolean sapeon) { @@ -275,7 +226,7 @@ public class PageTemplates { return ret; } - public static String formatDate(int minutes, String fulldate) { + public static String formatDate(int minutes, Date fulldate) { if (minutes < 1) { return "сейчас"; } else if (minutes < 60) { @@ -314,17 +265,16 @@ public class PageTemplates { } return days + " " + unit + " назад"; } else { - String ret = fulldate; + String ret = sdfFull.format(fulldate); synchronized (sdfSQL) { try { - Date pDate = sdfSQL.parse(fulldate); Calendar c = Calendar.getInstance(); int curyear = c.get(Calendar.YEAR); - c.setTime(pDate); + c.setTime(fulldate); if (c.get(Calendar.YEAR) == curyear) { - ret = sdfSimple.format(pDate); + ret = sdfSimple.format(fulldate); } else { - ret = sdfFull.format(pDate); + ret = sdfFull.format(fulldate); } } catch (Exception e) { System.err.println("PARSE EXCEPTION: " + fulldate); @@ -334,20 +284,11 @@ public class PageTemplates { } } - public static String formatJSLocalTime(String ts) { - String ret = ""; - synchronized (sdfSQL) { - try { - Date date = sdfSQL.parse(ts); - ret = "<script type=\"text/javascript\">" - + "var d=new Date(" + date.getTime() + ");" + public static String formatJSLocalTime(Date ts) { + return "<script type=\"text/javascript\">" + + "var d=new Date(" + ts.getTime() + ");" + "document.write((d.getDate()<10?'0':'')+d.getDate()+'.'+(d.getMonth()<9?'0':'')+(d.getMonth()+1)+'.'+d.getFullYear()+' '+(d.getHours()<10?'0':'')+d.getHours()+':'+(d.getMinutes()<10?'0':'')+d.getMinutes());" + "</script>"; - } catch (Exception e) { - System.err.println("PARSE EXCEPTION: " + ts); - } - } - return ret; } public static String formatReplies(int replies) { @@ -455,7 +396,7 @@ public class PageTemplates { return msg; } - public static void printMessages(PrintWriter out, Connection sql, com.juick.User user, List<Integer> mids, com.juick.User visitor, int YandexID, int ad_mid) { + public static void printMessages(PrintWriter out, JdbcTemplate sql, com.juick.User user, List<Integer> mids, com.juick.User visitor, int YandexID, int ad_mid) { List<com.juick.Message> msgs = MessagesQueries.getMessages(sql, mids); for (int i = 0; i < msgs.size(); i++) { @@ -501,7 +442,7 @@ public class PageTemplates { out.println("<article data-mid=\"" + msg.getMID() + "\">"); out.println(" <aside><a href=\"/" + msg.getUser().getUName() + "/\"><img src=\"//i.juick.com/a/" + msg.getUser().getUID() + ".png\" alt=\"" + msg.getUser().getUName() + "\"/></a></aside>"); out.println(" <header class=\"u\">@<a href=\"/" + msg.getUser().getUName() + "/\">" + msg.getUser().getUName() + "</a>:" + tagsStr + "</header>"); - out.println(" <header class=\"t\"><a href=\"/" + msg.getUser().getUName() + "/" + msg.getMID() + "\"><time datetime=\"" + msg.TimestampString + "Z\" title=\"" + msg.TimestampString + " GMT\">" + formatDate(msg.TimeAgo, msg.TimestampString) + "</time></a></header>"); + out.println(" <header class=\"t\"><a href=\"/" + msg.getUser().getUName() + "/" + msg.getMID() + "\"><time datetime=\"" + sdfSQL.format(msg.getDate()) + "Z\" title=\"" + sdfSQL.format(msg.getDate()) + " GMT\">" + formatDate(msg.TimeAgo, msg.getDate()) + "</time></a></header>"); if (msg.AttachmentType != null) { String fname = msg.getMID() + "." + msg.AttachmentType; out.println(" <p class=\"ir\"><a href=\"//i.juick.com/photos-512/" + fname + "\" onclick=\"return showPhotoDialog('" + fname + "')\"><img src=\"//i.juick.com/photos-512/" + fname + "\" alt=\"\"/></a></p>"); diff --git a/src/main/java/com/juick/http/www/RSS.java b/src/main/java/com/juick/http/www/RSS.java index 72893915..349743b5 100644 --- a/src/main/java/com/juick/http/www/RSS.java +++ b/src/main/java/com/juick/http/www/RSS.java @@ -19,16 +19,16 @@ package com.juick.http.www; import com.juick.Message; import com.juick.server.MessagesQueries; +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.io.PrintWriter; -import java.sql.Connection; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Iterator; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * @@ -36,10 +36,9 @@ import javax.servlet.http.HttpServletResponse; */ public class RSS { - private static final SimpleDateFormat sdfSQL = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat sdfRSS = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response, int uid, String uname) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int uid, String uname) throws ServletException, IOException { List<Integer> mids = MessagesQueries.getUserBlog(sql, uid, 0, 0); if (mids.isEmpty()) { response.sendError(404); @@ -73,14 +72,9 @@ public class RSS { out.println("]]></title>"); out.println("<description><![CDATA[" + PageTemplates.formatMessage(msg.getText()) + "]]></description>"); - synchronized (sdfSQL) { - try { - Date date = sdfSQL.parse(msg.TimestampString); - out.println("<pubDate>" + sdfRSS.format(date) + "</pubDate>"); - } catch (Exception e) { - System.err.println("PARSE EXCEPTION: " + msg.TimestampString); - } - } + Date date = msg.getDate(); + out.println("<pubDate>" + sdfRSS.format(date) + "</pubDate>"); + out.println("<comments>http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + "</comments>"); if (!msg.Tags.isEmpty()) { diff --git a/src/main/java/com/juick/http/www/Settings.java b/src/main/java/com/juick/http/www/Settings.java index de37bdd0..54ee0ee9 100644 --- a/src/main/java/com/juick/http/www/Settings.java +++ b/src/main/java/com/juick/http/www/Settings.java @@ -17,15 +17,14 @@ */ package com.juick.http.www; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; +import org.springframework.jdbc.core.JdbcTemplate; + import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; /** * @@ -33,12 +32,11 @@ import javax.servlet.http.HttpServletResponse; */ public class Settings { - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, "Логин", ""); PageTemplates.pageNavigation(out, visitor, null); @@ -56,12 +54,10 @@ public class Settings { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } - protected void doPost(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null || password == null || username.length() > 32 || password.isEmpty()) { @@ -79,16 +75,7 @@ public class Settings { if (uid > 0) { - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("DELETE FROM logins WHERE user_id=?"); - stmt.setInt(1, uid); - stmt.executeUpdate(); - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } + throw new IOException("Settings"); } String referer = request.getHeader("Referer"); diff --git a/src/main/java/com/juick/http/www/SignUp.java b/src/main/java/com/juick/http/www/SignUp.java index 64b62e48..1ee23386 100644 --- a/src/main/java/com/juick/http/www/SignUp.java +++ b/src/main/java/com/juick/http/www/SignUp.java @@ -18,16 +18,17 @@ package com.juick.http.www; import com.juick.server.UserQueries; -import java.io.IOException; -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; /** * @@ -35,7 +36,7 @@ import javax.servlet.http.HttpServletResponse; */ public class SignUp { - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); String type = request.getParameter("type"); @@ -61,8 +62,7 @@ public class SignUp { } response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { + try (PrintWriter out = response.getWriter()) { PageTemplates.pageHead(out, "Новый пользователь", null); PageTemplates.pageNavigation(out, visitor, null); @@ -110,12 +110,10 @@ public class SignUp { PageTemplates.pageFooter(request, out, visitor, false); PageTemplates.pageEnd(out); - } finally { - out.close(); } } - protected void doPost(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); int uid = 0; @@ -204,170 +202,57 @@ public class SignUp { response.sendRedirect("/"); } - private boolean setUserRef(Connection sql, int uid, int ref) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("INSERT INTO users_refs(user_id,ref) VALUES (?,?)"); - stmt.setInt(1, uid); - stmt.setInt(2, ref); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean setUserRef(JdbcTemplate sql, int uid, int ref) { + return sql.update("INSERT INTO users_refs(user_id,ref) VALUES (?,?)", uid, ref) > 0; } - private String getFacebookNameByHash(Connection sql, String hash) { - String ret = null; - - PreparedStatement stmt = null; - ResultSet rs = null; + private String getFacebookNameByHash(JdbcTemplate sql, String hash) { try { - stmt = sql.prepareStatement("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?"); - stmt.setString(1, hash); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = "<a href=\"" + rs.getString(2) + "\" rel=\"nofollow\">" + rs.getString(1) + "</a>"; - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + return sql.queryForObject("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?", String.class, hash); + } catch (EmptyResultDataAccessException e) { + return null; } - - return ret; } - private String getTelegramNameByHash(Connection sql, String hash) { - String ret = null; - - PreparedStatement stmt = null; - ResultSet rs = null; + private String getTelegramNameByHash(JdbcTemplate sql, String hash) { try { - stmt = sql.prepareStatement("SELECT tg_name FROM telegram WHERE loginhash=?"); - stmt.setString(1, hash); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = "<a href=\"https://telegram.me/" + rs.getString(1) + "\" rel=\"nofollow\">" + rs.getString(1) + "</a>"; - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + String name = sql.queryForObject("SELECT tg_name FROM telegram WHERE loginhash=?", String.class, hash); + return "<a href=\"https://telegram.me/" + name + "\" rel=\"nofollow\">" + name + "</a>"; + } catch (EmptyResultDataAccessException e) { + return null; } - - return ret; } - private boolean setFacebookUser(Connection sql, String hash, int uid) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?"); - stmt.setInt(1, uid); - stmt.setString(2, hash); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean setFacebookUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; } - private String getVKNameByHash(Connection sql, String hash) { - String ret = null; - - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement("SELECT vk_name,vk_link FROM vk WHERE loginhash=?"); - stmt.setString(1, hash); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = "<a href=\"http://vk.com/" + rs.getString(2) + "\" rel=\"nofollow\">" + rs.getString(1) + "</a>"; - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + private String getVKNameByHash(JdbcTemplate sql, String hash) { + List<Pair<String, String>> logins = sql.query("SELECT vk_name,vk_link FROM vk WHERE loginhash=?", + (rs, num) -> { + return Pair.of(rs.getString(1), rs.getString(2)); + }, hash); + if (logins.size() > 0) { + return "<a href=\"http://vk.com/" + logins.get(0).getRight() + "\" rel=\"nofollow\">" + logins.get(0).getLeft() + "</a>"; } - - return ret; + return null; } - private boolean setVKUser(Connection sql, String hash, int uid) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?"); - stmt.setInt(1, uid); - stmt.setString(2, hash); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean setVKUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; } - private boolean setTelegramUser(Connection sql, String hash, int uid) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?"); - stmt.setInt(1, uid); - stmt.setString(2, hash); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean setTelegramUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; } - private String getJIDByHash(Connection sql, String hash) { - String ret = null; - - PreparedStatement stmt = null; - ResultSet rs = null; + private String getJIDByHash(JdbcTemplate sql, String hash) { try { - stmt = sql.prepareStatement("SELECT jid FROM jids WHERE loginhash=?"); - stmt.setString(1, hash); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getString(1); - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + return sql.queryForObject("SELECT jid FROM jids WHERE loginhash=?", String.class, hash); + } catch (EmptyResultDataAccessException e) { + return null; } - - return ret; } - private boolean setJIDUser(Connection sql, String hash, int uid) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?"); - stmt.setInt(1, uid); - stmt.setString(2, hash); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean setJIDUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; } } diff --git a/src/main/java/com/juick/http/www/TwitterAuth.java b/src/main/java/com/juick/http/www/TwitterAuth.java index ff02c28b..bbad4d83 100644 --- a/src/main/java/com/juick/http/www/TwitterAuth.java +++ b/src/main/java/com/juick/http/www/TwitterAuth.java @@ -7,6 +7,7 @@ import com.github.scribejava.core.oauth.OAuth10aService; import com.github.scribejava.core.oauth.OAuthService; import com.juick.server.UserQueries; import org.json.JSONObject; +import org.springframework.jdbc.core.JdbcTemplate; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -29,7 +30,7 @@ public class TwitterAuth { this.consumerSecret = consumerSecret; } - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response) + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String hash = "", request_token = "", request_token_secret = ""; String verifier = request.getParameter("oauth_verifier"); diff --git a/src/main/java/com/juick/http/www/User.java b/src/main/java/com/juick/http/www/User.java index 84d1ad9a..0bdd910e 100644 --- a/src/main/java/com/juick/http/www/User.java +++ b/src/main/java/com/juick/http/www/User.java @@ -17,24 +17,22 @@ */ package com.juick.http.www; +import com.juick.Tag; import com.juick.server.MessagesQueries; import com.juick.server.TagQueries; import com.juick.server.UserQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * @@ -42,7 +40,7 @@ import javax.servlet.http.HttpServletResponse; */ public class User { - protected void doGetBlog(Connection sql, Connection sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + protected void doGetBlog(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); List<Integer> mids; @@ -158,7 +156,7 @@ public class User { } } - protected void doGetTags(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + protected void doGetTags(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (visitor == null) { @@ -181,7 +179,7 @@ public class User { } } - protected void doGetFriends(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + protected void doGetFriends(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (visitor == null) { @@ -198,25 +196,14 @@ public class User { out.println("<section id=\"content\">"); out.println("<table class=\"users\"><tr>"); - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement("SELECT users.id,users.nick FROM subscr_users INNER JOIN users ON subscr_users.user_id=users.id WHERE subscr_users.suser_id=? ORDER BY users.nick"); - stmt.setInt(1, user.getUID()); - rs = stmt.executeQuery(); - rs.beforeFirst(); - int cnt = 0; - while (rs.next()) { - if (cnt % 3 == 0 && cnt > 0) { - out.print("</tr><tr>"); - } - out.print("<td><a href=\"/" + rs.getString(2) + "/\"><img src=\"//i.juick.com/as/" + rs.getInt(1) + ".png\"/>" + rs.getString(2) + "</a></td>"); - cnt++; + List<com.juick.User> friends = UserQueries.getUserFriends(sql, user.getUID()); + for (int i = 0; i < friends.size(); i++) { + if (i % 3 == 0 && i > 0) { + out.print("</tr><tr>"); } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + out.print("<td><a href=\"/" + friends.get(i).getUName() + + "/\"><img src=\"//i.juick.com/as/" + friends.get(i).getUID() + ".png\"/>" + + friends.get(i).getUName() + "</a></td>"); } out.println("</tr></table>"); @@ -227,7 +214,7 @@ public class User { } } - protected void doGetReaders(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + protected void doGetReaders(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (visitor == null) { @@ -244,25 +231,14 @@ public class User { out.println("<section id=\"content\">"); out.println("<table class=\"users\"><tr>"); - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement("SELECT users.id,users.nick FROM subscr_users INNER JOIN users ON subscr_users.suser_id=users.id WHERE subscr_users.user_id=? ORDER BY users.nick"); - stmt.setInt(1, user.getUID()); - rs = stmt.executeQuery(); - rs.beforeFirst(); - int cnt = 0; - while (rs.next()) { - if (cnt % 3 == 0 && cnt > 0) { - out.print("</tr><tr>"); - } - out.print("<td><a href=\"/" + rs.getString(2) + "/\"><img src=\"//i.juick.com/as/" + rs.getInt(1) + ".png\"/>" + rs.getString(2) + "</a></td>"); - cnt++; + List<com.juick.User> readers = UserQueries.getUserReaders(sql, user.getUID()); + for (int i = 0; i < readers.size(); i++) { + if (i % 3 == 0 && i > 0) { + out.print("</tr><tr>"); } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + out.print("<td><a href=\"/" + readers.get(i).getUName() + + "/\"><img src=\"//i.juick.com/as/" + readers.get(i).getUID() + ".png\"/>" + + readers.get(i).getUName() + "</a></td>"); } out.println("</tr></table>"); @@ -285,7 +261,7 @@ public class User { } } - public static void pageUserColumn(PrintWriter out, Connection sql, com.juick.User user, com.juick.User visitor) { + public static void pageUserColumn(PrintWriter out, JdbcTemplate sql, com.juick.User user, com.juick.User visitor) { out.println("<aside id=\"column\">"); out.println(" <div id=\"ctitle\"><a href=\"./\"><img src=\"//i.juick.com/as/" + user.getUID() + ".png\" alt=\"\"/>" + user.getUName() + "</a></div>"); if (visitor != null && visitor.getUID() > 0 && visitor.getUID() != user.getUID()) { @@ -342,56 +318,22 @@ public class User { out.println("</aside>"); } - public static String pageUserTags(Connection sql, com.juick.User user, com.juick.User visitor, int cnt) { - com.juick.Tag tags[] = null; - - int maxUsageCnt = 0; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - if (cnt > 0) { - stmt = sql.prepareStatement("SELECT tags.name AS name,COUNT(DISTINCT messages_tags.message_id) AS cnt FROM (messages INNER JOIN messages_tags ON (messages.message_id=messages_tags.message_id)) INNER JOIN tags ON messages_tags.tag_id=tags.tag_id WHERE messages.user_id=? GROUP BY messages_tags.tag_id ORDER BY cnt DESC LIMIT ?", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - stmt.setInt(1, user.getUID()); - stmt.setInt(2, cnt); - } else { - stmt = sql.prepareStatement("SELECT tags.name AS name,COUNT(DISTINCT messages_tags.message_id) AS cnt FROM (messages INNER JOIN messages_tags ON (messages.message_id=messages_tags.message_id)) INNER JOIN tags ON messages_tags.tag_id=tags.tag_id WHERE messages.user_id=? GROUP BY messages_tags.tag_id ORDER BY cnt DESC", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - stmt.setInt(1, user.getUID()); - } - rs = stmt.executeQuery(); - rs.last(); - tags = new com.juick.Tag[rs.getRow()]; - rs.beforeFirst(); - cnt = 0; - while (rs.next()) { - tags[cnt] = new com.juick.Tag(); - tags[cnt].Name = rs.getString(1); - tags[cnt].UsageCnt = rs.getInt(2); - if (tags[cnt].UsageCnt > maxUsageCnt) { - maxUsageCnt = tags[cnt].UsageCnt; - } - cnt++; - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); - } - - if (tags != null && cnt > 0) { - Arrays.sort(tags, 0, cnt); - } - + public static String pageUserTags(JdbcTemplate sql, com.juick.User user, com.juick.User visitor, int cnt) { + List<Tag> tags = TagQueries.getUserTagsAll(sql, user.getUID()); + int maxUsageCnt = tags.stream().map(t -> t.UsageCnt).max(Integer::max).orElse(0); String ret = ""; - for (int i = 0; i < cnt; i++) { - String tag = Utils.encodeHTML(tags[i].Name); + int count = Math.min(tags.size(), cnt); + for (int i = 0; i < count; i++) { + String tag = Utils.encodeHTML(tags.get(i).Name); try { - tag = "<a href=\"./?tag=" + URLEncoder.encode(tags[i].Name, "UTF-8") + "\" title=\"" + tags[i].UsageCnt + "\" rel=\"nofollow\">" + tag + "</a>"; + tag = "<a href=\"./?tag=" + URLEncoder.encode(tags.get(i).Name, "UTF-8") + "\" title=\"" + + tags.get(i).UsageCnt + "\" rel=\"nofollow\">" + tag + "</a>"; } catch (UnsupportedEncodingException e) { } - if (tags[i].UsageCnt > maxUsageCnt / 3 * 2) { + if (tags.get(i).UsageCnt > maxUsageCnt / 3 * 2) { ret += "<big>" + tag + "</big> "; - } else if (tags[i].UsageCnt > maxUsageCnt / 3) { + } else if (tags.get(i).UsageCnt > maxUsageCnt / 3) { ret += "<small>" + tag + "</small> "; } else { ret += tag + " "; diff --git a/src/main/java/com/juick/http/www/UserThread.java b/src/main/java/com/juick/http/www/UserThread.java index 4ee290ab..638e3a3b 100644 --- a/src/main/java/com/juick/http/www/UserThread.java +++ b/src/main/java/com/juick/http/www/UserThread.java @@ -21,9 +21,10 @@ import com.juick.Message; import com.juick.Tag; import com.juick.server.MessagesQueries; import com.juick.server.UserQueries; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.io.PrintWriter; -import java.sql.Connection; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; @@ -36,7 +37,7 @@ import javax.servlet.http.HttpServletResponse; */ public class UserThread { - protected void doGetThread(Connection sql, HttpServletRequest request, HttpServletResponse response, int MID) throws ServletException, IOException { + protected void doGetThread(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int MID) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request, response); if (!MessagesQueries.canViewThread(sql, MID, visitor != null ? visitor.getUID() : 0)) { @@ -98,7 +99,7 @@ public class UserThread { } } - public static com.juick.Message printMessage(PrintWriter out, Connection sql, com.juick.Message msg, com.juick.User visitor) { + public static com.juick.Message printMessage(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor) { msg.VisitorCanComment = visitor != null; List<Tag> tags = MessagesQueries.getMessageTags(sql, msg.getMID()); @@ -128,7 +129,7 @@ public class UserThread { out.println(" <div class=\"msg-cont\">"); out.println(" <div class=\"msg-menu\"><a href=\"#\" onclick=\"showMessageLinksDialog(" + msg.getMID() + "); return false\"></a></div>"); out.println(" <div class=\"msg-header\"><a href=\"/" + msg.getUser().getUName() + "/\">@" + msg.getUser().getUName() + "</a>:" + tagsStr + "</div>"); - out.println(" <div class=\"msg-ts\">" + PageTemplates.formatJSLocalTime(msg.TimestampString) + "</div>"); + out.println(" <div class=\"msg-ts\">" + PageTemplates.formatJSLocalTime(msg.getDate()) + "</div>"); out.println(" <div class=\"msg-txt\">" + txt + "</div>"); if (msg.AttachmentType != null) { @@ -189,7 +190,7 @@ public class UserThread { return msg; } - public static void printReplies(PrintWriter out, Connection sql, com.juick.Message msg, com.juick.User visitor, boolean listview) { + public static void printReplies(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor, boolean listview) { List<com.juick.Message> replies = MessagesQueries.getReplies(sql, msg.getMID()); List<Integer> blUIDs = new ArrayList<Integer>(); @@ -297,7 +298,7 @@ public class UserThread { } else { out.println(" <div class=\"msg-header\">[удалено]:</div>"); } - out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMID() + "#" + msg.getRID() + "\" title=\"" + msg.TimestampString + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.TimestampString) + "</a></div>"); + out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMID() + "#" + msg.getRID() + "\" title=\"" + PageTemplates.sdfSQL.format(msg.getDate()) + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>"); out.println(" <div class=\"msg-txt\">" + PageTemplates.formatMessage(msg.getText()) + "</div>"); if (msg.AttachmentType != null) { out.println(" <div class=\"msg-media\"><a href=\"//i.juick.com/p/" + msg.getMID() + "-" + msg.getRID() + "." + msg.AttachmentType + "\"><img src=\"//i.juick.com/photos-512/" + msg.getMID() + "-" + msg.getRID() + "." + msg.AttachmentType + "\" alt=\"\"/></a></div>"); @@ -341,7 +342,7 @@ public class UserThread { } else { out.println(" <div class=\"msg-header\">[удалено]:</div>"); } - out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMID() + "#" + msg.getRID() + "\" title=\"" + msg.TimestampString + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.TimestampString) + "</a></div>"); + out.println(" <div class=\"msg-ts\"><a href=\"/" + msg.getMID() + "#" + msg.getRID() + "\" title=\"" + PageTemplates.sdfSQL.format(msg.getDate()) + " GMT\">" + PageTemplates.formatDate(msg.TimeAgo, msg.getDate()) + "</a></div>"); out.println(" <div class=\"msg-txt\">" + PageTemplates.formatMessage(msg.getText()) + "</div>"); if (msg.AttachmentType != null) { out.println(" <div class=\"msg-media\"><a href=\"//i.juick.com/p/" + msg.getMID() + "-" + msg.getRID() + "." + msg.AttachmentType + "\"><img src=\"//i.juick.com/photos-512/" + msg.getMID() + "-" + msg.getRID() + "." + msg.AttachmentType + "\" alt=\"\"/></a></div>"); diff --git a/src/main/java/com/juick/http/www/Utils.java b/src/main/java/com/juick/http/www/Utils.java index 931e2c21..ab721020 100644 --- a/src/main/java/com/juick/http/www/Utils.java +++ b/src/main/java/com/juick/http/www/Utils.java @@ -17,6 +17,8 @@ */ package com.juick.http.www; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; @@ -77,7 +79,7 @@ public class Utils { return attachmentFName; } - public static com.juick.User getVisitorUser(Connection sql, HttpServletRequest request, HttpServletResponse response) { + public static com.juick.User getVisitorUser(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) { String hash = getCookie(request, "hash"); if (hash != null) { com.juick.User visitor = com.juick.server.UserQueries.getUserByHash(sql, hash); diff --git a/src/main/java/com/juick/http/www/VKontakteLogin.java b/src/main/java/com/juick/http/www/VKontakteLogin.java index 5f26fef1..8fad3b7a 100644 --- a/src/main/java/com/juick/http/www/VKontakteLogin.java +++ b/src/main/java/com/juick/http/www/VKontakteLogin.java @@ -18,19 +18,18 @@ package com.juick.http.www; import com.juick.server.UserQueries; -import java.io.IOException; -import java.net.URLEncoder; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.UUID; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.json.JSONException; -import org.json.JSONObject; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.UUID; /** * @@ -42,7 +41,7 @@ public class VKontakteLogin { private static final String VK_SECRET = "z2afNI8jA5lIpZ2jsTm1"; private static final String VK_REDIRECT = "http://juick.com/_vklogin"; - protected void doGet(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String code = request.getParameter("code"); if (code == null || code.equals("")) { response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); @@ -114,42 +113,16 @@ public class VKontakteLogin { } } - private int getUIDbyVKID(Connection sql, long vkID) { - int uid = 0; - PreparedStatement stmt = null; - ResultSet rs = null; + private int getUIDbyVKID(JdbcTemplate sql, long vkID) { try { - stmt = sql.prepareStatement("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL"); - stmt.setLong(1, vkID); - rs = stmt.executeQuery(); - if (rs.first()) { - uid = rs.getInt(1); - } - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(rs, stmt); + return sql.queryForObject("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL", Integer.class, vkID); + } catch (EmptyResultDataAccessException e) { + return 0; } - return uid; } - private boolean insertDB(Connection sql, long vkID, String loginhash, String token, String vkName, String vkLink) { - boolean ret = false; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)"); - stmt.setLong(1, vkID); - stmt.setString(2, loginhash); - stmt.setString(3, token); - stmt.setString(4, vkName); - stmt.setString(5, vkLink); - stmt.executeUpdate(); - ret = true; - } catch (SQLException e) { - System.err.println(e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; + private boolean insertDB(JdbcTemplate sql, long vkID, String loginhash, String token, String vkName, String vkLink) { + return sql.update("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)", + vkID, loginhash, token, vkName, vkLink) > 0; } } diff --git a/src/main/java/com/juick/server/protocol/JuickProtocol.java b/src/main/java/com/juick/server/protocol/JuickProtocol.java new file mode 100644 index 00000000..1a2e5333 --- /dev/null +++ b/src/main/java/com/juick/server/protocol/JuickProtocol.java @@ -0,0 +1,375 @@ +package com.juick.server.protocol; + +import com.juick.*; +import com.juick.json.MessageSerializer; +import com.juick.server.*; +import com.juick.server.protocol.annotation.UserCommand; +import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Created by oxpa on 22.03.16. + */ + +public class JuickProtocol { + MessageSerializer json = new MessageSerializer(); + JdbcTemplate sql; + String baseUri; + + public JuickProtocol(JdbcTemplate sql, String baseUri) { + this.sql = sql; + this.baseUri = baseUri; + } + + /** + * find command by pattern and invoke + * @param user who send command + * @param userInput given by user + * @return command result + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws NoSuchMethodException + */ + public ProtocolReply getReply(User user, String userInput) throws InvocationTargetException, + IllegalAccessException, NoSuchMethodException { + Optional<Method> cmd = Arrays.asList(getClass().getDeclaredMethods()).stream() + .filter(m -> m.isAnnotationPresent(UserCommand.class)) + .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), + m.getAnnotation(UserCommand.class).patternFlags()).matcher(userInput).matches()) + .findFirst(); + if (!cmd.isPresent()) { + // default command - post as new message + return postMessage(user, userInput); + } else { + Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), + cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(userInput); + List<String> groups = new ArrayList<>(); + while (matcher.find()) { + for (int i = 1; i <= matcher.groupCount(); i++) { + groups.add(matcher.group(i)); + } + } + return (ProtocolReply) getClass().getMethod(cmd.get().getName(), User.class, String[].class) + .invoke(this, user, groups.toArray(new String[groups.size()])); + } + } + + public ProtocolReply postMessage(User user, String input) { + List<Tag> tags = TagQueries.fromString(sql, input, false); + String body = input.substring(TagQueries.toString(tags).length()); + int mid = MessagesQueries.createMessage(sql, user.getUID(), body, null, tags); + //app.events().publishEvent(new JuickMessageEvent(app.messages().getMessage(mid))); + return new ProtocolReply("New message posted.\n#" + mid + " " + baseUri + mid, + Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getMessage(sql, mid))))); + } + + @UserCommand(pattern = "^#(\\++)$", help = "#+ - Show last Juick messages (#++ - second page, ...)") + public ProtocolReply commandLast(User user, String... arguments) { + // number of + is the page count + int page = arguments[0].length(); + List<Integer> mids = MessagesQueries.getAll(sql, user.getUID(), page); + List<Message> messages = MessagesQueries.getMessages(sql, mids); + // TODO: message toString + return new ProtocolReply("Last messages: \n" + String.join("\n", messages.stream().map(Object::toString) + .collect(Collectors.toList())), Optional.of(json.serializeList(messages))); + } + + @UserCommand(pattern = "^\\s*bl\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "BL - Show your blacklist") + public ProtocolReply commandBL(User user_from, String... arguments) { + List<User> blusers; + List<String> bltags; + + blusers = UserQueries.getUserBLUsers(sql, user_from.getUID()); + bltags = TagQueries.getUserBLTags(sql, user_from.getUID()); + + + String txt = ""; + if (bltags.size() > 0) { + for (String bltag : bltags) { + txt += "*" + bltag + "\n"; + } + + if (blusers.size() > 0) { + txt += "\n"; + } + } + if (blusers.size() > 0) { + for (User bluser : blusers) { + txt += "@" + bluser.getUName() + "\n"; + } + } + if (txt.isEmpty()) { + txt = "You don't have any users or tags in your blacklist."; + } + return new ProtocolReply(txt, Optional.empty()); + } + + @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", + help = "@username+ - Show user's info and last 10 messages (@username++ - second page, ..)") + public ProtocolReply commandUser(User user, String... arguments) { + User blogUser = UserQueries.getUserByName(sql, arguments[0]); + int page = arguments[1].length(); + if (blogUser.getUID() > 0) { + List<Integer> mids = MessagesQueries.getUserBlog(sql, blogUser.getUID(), 0, page); + List<Message> messages = MessagesQueries.getMessages(sql, mids); + return new ProtocolReply(String.format("Last messages from @%s:\n%s", arguments[0], + String.join("\n", messages.stream() + .map(Object::toString).collect(Collectors.toList()))), + Optional.of(json.serializeList(messages))); + } + return new ProtocolReply("User not found", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*d\\s*\\#([0-9]+)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "D #12345 - delete the message") + public ProtocolReply commandDel(User user, String... args) { + try { + int mid = Integer.parseInt(args[0]); + if (MessagesQueries.deleteMessage(sql, user.getUID(), mid)) { + return new ProtocolReply(String.format("Message %s deleted", mid), Optional.empty()); + } + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*login\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "LOGIN - log in to Juick website") + public ProtocolReply commandLogin(User user, String... arguments) { + return new ProtocolReply(baseUri + "?" + UserQueries.getHashByUID(sql, user.getUID()), + Optional.empty()); + } + + @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") + public ProtocolReply commandMyFeed(User user, String... arguments) { + // number of # is the page count + int page = arguments[0].length(); + List<Integer> mids = MessagesQueries.getMyFeed(sql, user.getUID(), page); + List<Message> messages = MessagesQueries.getMessages(sql, mids); + // TODO: add instructions for empty feed + return new ProtocolReply("Your feed: \n" + String.join("\n", + messages.stream().map(Object::toString).collect(Collectors.toList())), + Optional.of(json.serializeList(messages))); + } + + @UserCommand(pattern = "^\\s*(on|off)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "ON/OFF - Enable/disable subscriptions delivery") + public ProtocolReply commandOnOff(User user, String[] input) { + UserQueries.ActiveStatus newStatus; + String retValUpdated; + if (input[0].toLowerCase().equals("on")) { + newStatus = UserQueries.ActiveStatus.Active; + retValUpdated = "Notifications are activated for " + user.getJID(); + } else { + newStatus = UserQueries.ActiveStatus.Inactive; + retValUpdated = "Notifications are disabled for " + user.getJID(); + } + + if (UserQueries.setActiveStatusForJID(sql, user.getJID(), newStatus)) { + return new ProtocolReply(retValUpdated, Optional.empty()); + } else { + return new ProtocolReply(String.format("Subscriptions status for %s was not changed", user.getJID()), + Optional.empty()); + } + } + + @UserCommand(pattern = "^\\s*ping\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "PING - returns you a PONG") + public ProtocolReply commandPing(User user, String[] input) { + return new ProtocolReply("PONG", Optional.empty()); + } + + @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") + public ProtocolReply commandPM(User user_from, String... arguments) { + String user_to = arguments[0]; + String body = arguments[1]; + int ret = 0; + + int uid_to = 0; + String jid_to = null; + boolean haveInRoster = false; + + if (user_to.indexOf('@') > 0) { + uid_to = UserQueries.getUIDbyJID(sql, user_to); + } else { + uid_to = UserQueries.getUIDbyName(sql, user_to); + } + + if (uid_to > 0) { + if (!UserQueries.isInBLAny(sql, uid_to, user_from.getUID())) { + if (PMQueries.createPM(sql, user_from.getUID(), uid_to, body)) { + //jid_to = UserQueries.getJIDsbyUID(sql, uid_to); + if (jid_to != null) { + haveInRoster = PMQueries.havePMinRoster(sql, user_from.getUID(), jid_to); + } + ret = 200; + } else { + ret = 500; + } + } else { + ret = 403; + } + } else { + ret = 404; + } + + + if (ret == 200) { + JuickMessage jmsg = new JuickMessage(); + jmsg.setUser(user_from); + jmsg.setText(body); + // TODO: add PM payload + //app.events().publishEvent(new JuickMessageEvent(jmsg)); + /* TODO: move to XMPP component + if (jid_to != null) { + Message mm = new Message(); + mm.to = new JID(jid_to); + mm.type = Message.Type.chat; + if (haveInRoster) { + mm.from = new JID(user_from.getUName(), getDomain(), "Juick"); + mm.body = body; + } else { + mm.from = new JID("juick", getDomain(), "Juick"); + mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; + } + return Collections.singletonList(mm); + } + */ + } + if (ret == 200) { + return new ProtocolReply("Private message sent", Optional.empty()); + } else { + return new ProtocolReply("Error " + ret, Optional.empty()); + } + } + + @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") + public ProtocolReply commandShow(User user, String... arguments) { + boolean showReplies = arguments[1].length() > 0; + int mid; + try { + mid = Integer.parseInt(arguments[0]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + Message msg = MessagesQueries.getMessage(sql, mid); + if (showReplies) { + List<Message> replies = MessagesQueries.getReplies(sql, mid); + replies.add(0, msg); + return new ProtocolReply(String.join("\n", + replies.stream().map(Object::toString).collect(Collectors.toList())), + Optional.of(json.serializeList(replies))); + } + return new ProtocolReply(msg.toString(), Optional.of(json.serializeList(Collections.singletonList(msg)))); + } + @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)", + help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") + public ProtocolReply EditOrReply(User user, String... args) { + int mid; + try { + mid = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + int rid; + try { + rid = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + rid = 0; + } + String txt = args[5]; + List<Tag> messageTags = TagQueries.fromString(sql, txt, true); + if (messageTags.size() > 0) { + if (user.getUID() != MessagesQueries.getMessageAuthor(sql, mid).getUID()) { + return new ProtocolReply("It is not your message", Optional.empty()); + } + TagQueries.updateTags(sql, mid, messageTags); + return new ProtocolReply("Tags are updated", Optional.empty()); + } else { + int newrid = MessagesQueries.createReply(sql, mid, rid, user.getUID(), txt, null); + return new ProtocolReply("Reply posted.\n#" + mid + "/" + newrid + " " + + baseUri + mid + "/" + newrid, + Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getReply(sql, mid, newrid))))); + } + } + + @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments", + patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeMessage(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + int mid; + try { + mid = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + if (subscribe) { + if (SubscriptionsQueries.subscribeMessage(sql, mid, user.getUID())) { + return new ProtocolReply("Subscribed", Optional.empty()); + } + } else { + if (SubscriptionsQueries.unSubscribeMessage(sql, mid, user.getUID())) { + return new ProtocolReply("Unsubscribed from #" + mid, Optional.empty()); + } + return new ProtocolReply("You was not subscribed to #" + mid, Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @user - subscribe to user's posts", + patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeUser(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + User toUser = UserQueries.getUserByName(sql, args[1]); + if (toUser.getUID() > 0) { + if (subscribe) { + if (SubscriptionsQueries.subscribeUser(sql, user, toUser)) { + return new ProtocolReply("Subscribed", Optional.empty()); + // TODO: notification + // TODO: already subscribed case + } + } else { + if (SubscriptionsQueries.unSubscribeUser(sql, user, toUser)) { + return new ProtocolReply("Unsubscribed from @" + toUser.getUName(), Optional.empty()); + } + return new ProtocolReply("You was not subscribed to @" + toUser.getUName(), Optional.empty()); + } + } + return new ProtocolReply("Error", Optional.empty()); + } + @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + + "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeTag(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + Tag tag = TagQueries.getTag(sql, args[1], true); + if (subscribe) { + if (SubscriptionsQueries.subscribeTag(sql, user, tag)) { + return new ProtocolReply("Subscribed", Optional.empty()); + } + } else { + if (SubscriptionsQueries.unSubscribeTag(sql, user, tag)) { + return new ProtocolReply("Unsubscribed from " + tag.Name, Optional.empty()); + } + return new ProtocolReply("You was not subscribed to " + tag.Name, Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*help\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "HELP - returns this help message") + public ProtocolReply commandHelp(User user, String[] input) { + List<String> commandsHelp = Arrays.asList(getClass().getDeclaredMethods()).stream() + .filter(m -> m.isAnnotationPresent(UserCommand.class)) + .map(m -> m.getAnnotation(UserCommand.class).help()) + .collect(Collectors.toList()); + return new ProtocolReply(String.join("\n", commandsHelp), Optional.empty()); + } +} diff --git a/src/main/java/com/juick/server/protocol/ProtocolReply.java b/src/main/java/com/juick/server/protocol/ProtocolReply.java new file mode 100644 index 00000000..d9d36a5d --- /dev/null +++ b/src/main/java/com/juick/server/protocol/ProtocolReply.java @@ -0,0 +1,23 @@ +package com.juick.server.protocol; + +import java.util.Optional; + +/** + * Created by vitalyster on 08.04.2016. + */ +public class ProtocolReply { + + private Optional<String> json; + private String description; + + public ProtocolReply(String text, Optional<String> json) { + this.description = text; + this.json = json; + } + public String getDescription() { + return description; + } + public Optional<String> getJson() { + return json; + } +} diff --git a/src/main/java/com/juick/server/protocol/annotation/UserCommand.java b/src/main/java/com/juick/server/protocol/annotation/UserCommand.java new file mode 100644 index 00000000..af7c4924 --- /dev/null +++ b/src/main/java/com/juick/server/protocol/annotation/UserCommand.java @@ -0,0 +1,31 @@ +package com.juick.server.protocol.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by oxpa on 22.03.16. + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserCommand { + /** + * + * @return a command pattern + */ + String pattern() default ""; + + /** + * + * @return pattern flags + */ + int patternFlags() default 0; + + /** + * + * @return a string used in HELP command output. Basically, only 1 string + */ + String help() default ""; +} diff --git a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java index 53dd6deb..885b2375 100644 --- a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java +++ b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java @@ -20,6 +20,10 @@ package com.juick.xmpp.extensions; import com.juick.xmpp.utils.XmlUtils; import com.juick.xmpp.*; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -31,12 +35,17 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { public final static String XMLNS = "http://juick.com/message"; public final static String TagName = "juick"; + private SimpleDateFormat df; public JuickMessage() { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); } public JuickMessage(com.juick.Message msg) { super(msg); + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); } @Override @@ -45,7 +54,7 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { } @Override - public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException { + public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException { JuickMessage jmsg = new JuickMessage(); final String sMID = parser.getAttributeValue(null, "mid"); @@ -72,7 +81,10 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { if (sReadOnly != null) { jmsg.ReadOnly = true; } - jmsg.TimestampString = parser.getAttributeValue(null, "ts"); + String ts = parser.getAttributeValue(null, "timestamp"); + if (ts != null) { + jmsg.setDate(df.parse(ts)); + } jmsg.AttachmentType = parser.getAttributeValue(null, "attach"); while (parser.next() == XmlPullParser.START_TAG) { @@ -112,8 +124,8 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { if (ReadOnly) { ret += " readonly=\"1\""; } - if (TimestampString != null) { - ret += " ts=\"" + TimestampString + "\""; + if (getDate() != null) { + ret += " ts=\"" + df.format(getDate()) + "\""; } if (AttachmentType != null) { ret += " attach=\"" + AttachmentType + "\""; diff --git a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java b/src/main/java/com/juick/xmpp/s2s/CleaningUp.java index 48771580..14d97ed8 100644 --- a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java +++ b/src/main/java/com/juick/xmpp/s2s/CleaningUp.java @@ -10,19 +10,24 @@ import java.util.Iterator; * @author ugnich */ public class CleaningUp implements Runnable { + XMPPComponent xmpp; + + public CleaningUp(XMPPComponent xmpp) { + this.xmpp = xmpp; + } @Override public void run() { while (true) { try { - PrintWriter statsFile = new PrintWriter(XMPPComponent.STATSFILE, "UTF-8"); + PrintWriter statsFile = new PrintWriter(xmpp.STATSFILE, "UTF-8"); statsFile.write("<html><body><h2>Threads: " + Thread.activeCount() + "</h2>"); - statsFile.write("<h2>Out (" + XMPPComponent.outConnections.size() + ")</h2><table border=1><tr><th>to</th><th>sid</th><th>inactive</th><th>out packets</th><th>out bytes</th></tr>"); + statsFile.write("<h2>Out (" + xmpp.outConnections.size() + ")</h2><table border=1><tr><th>to</th><th>sid</th><th>inactive</th><th>out packets</th><th>out bytes</th></tr>"); long now = System.currentTimeMillis(); - synchronized (XMPPComponent.outConnections) { - for (Iterator<ConnectionOut> i = XMPPComponent.outConnections.iterator(); i.hasNext();) { + synchronized (xmpp.outConnections) { + for (Iterator<ConnectionOut> i = xmpp.outConnections.iterator(); i.hasNext();) { ConnectionOut c = i.next(); int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0); if (inactive > 900) { @@ -40,10 +45,10 @@ public class CleaningUp implements Runnable { } } - statsFile.write("</table><h2>In (" + XMPPComponent.inConnections.size() + ")</h2><table border=1><tr><th>from</th><th>sid</th><th>inactive</th><th>in packets</th></tr>"); + statsFile.write("</table><h2>In (" + xmpp.inConnections.size() + ")</h2><table border=1><tr><th>from</th><th>sid</th><th>inactive</th><th>in packets</th></tr>"); - synchronized (XMPPComponent.inConnections) { - for (Iterator<ConnectionIn> i = XMPPComponent.inConnections.iterator(); i.hasNext();) { + synchronized (xmpp.inConnections) { + for (Iterator<ConnectionIn> i = xmpp.inConnections.iterator(); i.hasNext();) { ConnectionIn c = i.next(); int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0); if (inactive > 900) { @@ -73,10 +78,10 @@ public class CleaningUp implements Runnable { } } - statsFile.write("</table><h2>Cache (" + XMPPComponent.outCache.size() + ")</h2><table border=1><tr><th>host</th><th>live</th><th>size</th></tr>"); + statsFile.write("</table><h2>Cache (" + xmpp.outCache.size() + ")</h2><table border=1><tr><th>host</th><th>live</th><th>size</th></tr>"); - synchronized (XMPPComponent.outCache) { - for (Iterator<CacheEntry> i = XMPPComponent.outCache.iterator(); i.hasNext();) { + synchronized (xmpp.outCache) { + for (Iterator<CacheEntry> i = xmpp.outCache.iterator(); i.hasNext();) { CacheEntry c = i.next(); int inactive = (int) ((double) (now - c.tsCreated) / 1000.0); if (inactive > 600) { diff --git a/src/main/java/com/juick/xmpp/s2s/Connection.java b/src/main/java/com/juick/xmpp/s2s/Connection.java index c1bbf22e..eae6efaa 100644 --- a/src/main/java/com/juick/xmpp/s2s/Connection.java +++ b/src/main/java/com/juick/xmpp/s2s/Connection.java @@ -30,6 +30,7 @@ public class Connection { public long tsLocalData = 0; public long bytesLocal = 0; public long packetsLocal = 0; + XMPPComponent xmpp; Socket socket; public static final String NS_DB = "jabber:server:dialback"; public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; @@ -52,17 +53,18 @@ public class Connection { }; - public Connection() throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException { + public Connection(XMPPComponent xmpp) throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException { + this.xmpp = xmpp; tsCreated = System.currentTimeMillis(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); KeyStore ks = KeyStore.getInstance("JKS"); - try (InputStream ksIs = new FileInputStream(XMPPComponent.keystore)) { - ks.load(ksIs, XMPPComponent.keystorePassword.toCharArray()); + try (InputStream ksIs = new FileInputStream(xmpp.keystore)) { + ks.load(ksIs, xmpp.keystorePassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); - kmf.init(ks, XMPPComponent.keystorePassword.toCharArray()); + kmf.init(ks, xmpp.keystorePassword.toCharArray()); sc = SSLContext.getInstance("TLSv1.2"); sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java b/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java index 93e761a0..345caea9 100644 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java +++ b/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java @@ -31,9 +31,11 @@ public class ConnectionIn extends Connection implements Runnable { final public List<String> from = new ArrayList<>(); public long tsRemoteData = 0; public long packetsRemote = 0; + JuickBot bot; - public ConnectionIn(Socket socket) throws Exception { - super(); + public ConnectionIn(XMPPComponent xmpp, JuickBot bot, Socket socket) throws Exception { + super(xmpp); + this.bot = bot; this.socket = socket; streamID = UUID.randomUUID().toString(); restartParser(); @@ -69,19 +71,19 @@ public class ConnectionIn extends Connection implements Runnable { String dfrom = parser.getAttributeValue(null, "from"); String to = parser.getAttributeValue(null, "to"); LOGGER.info("STREAM FROM " + dfrom + " TO " + to + " " + streamID + " ASKING FOR DIALBACK"); - if (dfrom.endsWith(XMPPComponent.HOSTNAME) && (dfrom.equals(XMPPComponent.HOSTNAME) || dfrom.endsWith("." + XMPPComponent.HOSTNAME))) { + if (dfrom.endsWith(xmpp.HOSTNAME) && (dfrom.equals(xmpp.HOSTNAME) || dfrom.endsWith("." + xmpp.HOSTNAME))) { break; } - if (to != null && to.equals(XMPPComponent.HOSTNAME)) { + if (to != null && to.equals(xmpp.HOSTNAME)) { String dbKey = XmlUtils.getTagText(parser); updateTsRemoteData(); - ConnectionOut c = XMPPComponent.getConnectionOut(dfrom, false); + ConnectionOut c = xmpp.getConnectionOut(dfrom, false); if (c != null) { c.sendDialbackVerify(streamID, dbKey); } else { - c = new ConnectionOut(dfrom, streamID, dbKey); - XMPPComponent.executorService.submit(c); + c = new ConnectionOut(xmpp, dfrom, streamID, dbKey); + xmpp.executorService.submit(c); } } else { throw new HostUnknownException("STREAM FROM " + dfrom + " " + streamID + " INVALID TO " + to); @@ -107,15 +109,15 @@ public class ConnectionIn extends Connection implements Runnable { } else if (tag.equals("presence") && checkFromTo(parser)) { Presence p = Presence.parse(parser, null); if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { - JuickBot.incomingPresence(p); + bot.incomingPresence(p); } } else if (tag.equals("message") && checkFromTo(parser)) { updateTsRemoteData(); - Message msg = Message.parse(parser, XMPPComponent.childParsers); + Message msg = Message.parse(parser, xmpp.childParsers); if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { LOGGER.info("STREAM " + streamID + ": " + msg.toString()); - if (!JuickBot.incomingMessage(msg)) { - XMPPComponent.connRouter.router.send(msg.toString()); + if (!bot.incomingMessage(msg)) { + xmpp.router.send(msg.toString()); } } } else if (tag.equals("iq") && checkFromTo(parser)) { @@ -124,7 +126,7 @@ public class ConnectionIn extends Connection implements Runnable { String xml = XmlUtils.parseToString(parser, true); if (type == null || !type.equals(Iq.Type.error)) { LOGGER.info("STREAM " + streamID + ": " + xml); - XMPPComponent.connRouter.router.send(xml); + xmpp.router.send(xml); } } else if (!isSecured() && tag.equals("starttls")) { LOGGER.info("STREAM " + streamID + " SECURING"); @@ -140,7 +142,7 @@ public class ConnectionIn extends Connection implements Runnable { } catch (SSLException sex) { LOGGER.warning("STREAM " + streamID + " SSL ERROR"); sendStanza("<failed xmlns\"" + NS_TLS + "\" />"); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { @@ -150,17 +152,17 @@ public class ConnectionIn extends Connection implements Runnable { } } LOGGER.warning("STREAM " + streamID + " FINISHED"); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } catch (EOFException ex) { LOGGER.info(String.format("STREAM %s CLOSED (dirty)", streamID)); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } catch (HostUnknownException e) { LOGGER.warning(e.getMessage()); } catch (Exception e) { LOGGER.log(Level.WARNING, "STREAM " + streamID + " ERROR", e); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } } @@ -172,10 +174,10 @@ public class ConnectionIn extends Connection implements Runnable { void sendOpenStream(String from, boolean xmppversionnew) throws IOException { String openStream = "<?xml version='1.0'?><stream:stream xmlns='jabber:server' " + "xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - XMPPComponent.HOSTNAME + "' id='" + streamID + "' version='1.0'>"; + xmpp.HOSTNAME + "' id='" + streamID + "' version='1.0'>"; if (xmppversionnew) { openStream += "<stream:features>"; - if (!isSecured() && !XMPPComponent.brokenSSLhosts.contains(from)) { + if (!isSecured() && !xmpp.brokenSSLhosts.contains(from)) { openStream += "<starttls xmlns=\"" + NS_TLS + "\"><optional/></starttls>"; } openStream += "</stream:features>"; @@ -185,7 +187,7 @@ public class ConnectionIn extends Connection implements Runnable { public void sendDialbackResult(String sfrom, String type) { try { - sendStanza("<db:result from='" + XMPPComponent.HOSTNAME + "' to='" + sfrom + "' type='" + type + "'/>"); + sendStanza("<db:result from='" + xmpp.HOSTNAME + "' to='" + sfrom + "' type='" + type + "'/>"); if (type.equals("valid")) { from.add(sfrom); LOGGER.info("STREAM FROM " + sfrom + " " + streamID + " READY"); @@ -200,7 +202,7 @@ public class ConnectionIn extends Connection implements Runnable { String cto = parser.getAttributeValue(null, "to"); if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { JID jidto = new JID(cto); - if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(XMPPComponent.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { + if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(xmpp.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { JID jidfrom = new JID(cfrom); int size = from.size(); for (int i = 0; i < size; i++) { diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java b/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java deleted file mode 100644 index 569d3db0..00000000 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.juick.xmpp.s2s; - -import java.net.ServerSocket; -import java.net.Socket; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author vt - */ -public class ConnectionListener implements Runnable { - - private static final Logger logger = Logger.getLogger(ConnectionListener.class.getName()); - - @Override - public void run() { - try { - final ServerSocket listener = new ServerSocket(5269); - logger.info("s2s listener ready"); - while (true) { - try { - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(socket); - XMPPComponent.addConnectionIn(client); - XMPPComponent.executorService.submit(client); - } catch (Exception e) { - logger.log(Level.SEVERE, "s2s error", e); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "s2s listener exception", e); - } - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java b/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java index d3a10406..fede701e 100644 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java +++ b/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java @@ -17,6 +17,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.util.UUID; import java.util.logging.Level; /** @@ -29,30 +30,31 @@ public class ConnectionOut extends Connection implements Runnable { String checkSID = null; String dbKey = null; - public ConnectionOut(String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { - super(); + public ConnectionOut(XMPPComponent xmpp, String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { + super(xmpp); to = hostname; } - public ConnectionOut(String hostname, String checkSID, String dbKey) throws Exception { - super(); + public ConnectionOut(XMPPComponent xmpp, String hostname, String checkSID, String dbKey) throws Exception { + super(xmpp); to = hostname; this.checkSID = checkSID; this.dbKey = dbKey; + streamID = UUID.randomUUID().toString(); } void sendOpenStream() throws IOException { sendStanza("<?xml version='1.0'?><stream:stream xmlns='jabber:server' id='" + streamID + "' xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - XMPPComponent.HOSTNAME + "' to='" + to + "' version='1.0'>"); + xmpp.HOSTNAME + "' to='" + to + "' version='1.0'>"); } void processDialback() throws Exception { if (checkSID != null) { sendDialbackVerify(checkSID, dbKey); } - sendStanza("<db:result from='" + XMPPComponent.HOSTNAME + "' to='" + to + "'>" + - generateDialbackKey(to, XMPPComponent.HOSTNAME, streamID) + "</db:result>"); + sendStanza("<db:result from='" + xmpp.HOSTNAME + "' to='" + to + "'>" + + generateDialbackKey(to, xmpp.HOSTNAME, streamID) + "</db:result>"); } @Override @@ -76,7 +78,7 @@ public class ConnectionOut extends Connection implements Runnable { } LOGGER.info("STREAM TO " + to + " " + streamID + " OPEN"); - XMPPComponent.addConnectionOut(ConnectionOut.this); + xmpp.addConnectionOut(ConnectionOut.this); boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; if (!xmppversionnew) { processDialback(); @@ -95,7 +97,7 @@ public class ConnectionOut extends Connection implements Runnable { streamReady = true; LOGGER.info("STREAM TO " + to + " " + streamID + " READY"); - String cache = XMPPComponent.getFromCache(to); + String cache = xmpp.getFromCache(to); if (cache != null) { LOGGER.info("STREAM TO " + to + " " + streamID + " SENDING CACHE"); sendStanza(cache); @@ -110,7 +112,7 @@ public class ConnectionOut extends Connection implements Runnable { String type = parser.getAttributeValue(null, "type"); String sid = parser.getAttributeValue(null, "id"); if (from != null && from.equals(to) && sid != null && !sid.isEmpty() && type != null) { - ConnectionIn c = XMPPComponent.getConnectionIn(sid); + ConnectionIn c = xmpp.getConnectionIn(sid); if (c != null) { c.sendDialbackResult(from, type); } @@ -118,7 +120,7 @@ public class ConnectionOut extends Connection implements Runnable { XmlUtils.skip(parser); } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { StreamFeatures features = StreamFeatures.parse(parser); - if (!isSecured() && features.STARTTLS >= 0 && !XMPPComponent.brokenSSLhosts.contains(to)) { + if (!isSecured() && features.STARTTLS >= 0 && !xmpp.brokenSSLhosts.contains(to)) { System.out.println("STREAM TO " + to + " " + streamID + " SECURING"); sendStanza("<starttls xmlns=\"" + NS_TLS + "\" />"); } else { @@ -136,7 +138,7 @@ public class ConnectionOut extends Connection implements Runnable { } catch (SSLException sex) { LOGGER.log(Level.SEVERE, String.format("s2s ssl error: %s %s", to, streamID), sex); sendStanza("<failed xmlns\"" + NS_TLS + "\" />"); - XMPPComponent.removeConnectionOut(this); + xmpp.removeConnectionOut(this); closeConnection(); } } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { @@ -147,22 +149,22 @@ public class ConnectionOut extends Connection implements Runnable { } LOGGER.warning("STREAM TO " + to + " " + streamID + " FINISHED"); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } catch (EOFException eofex) { LOGGER.info(String.format("STREAM %s %s CLOSED (dirty)", to, streamID)); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } catch (Exception e) { LOGGER.log(Level.SEVERE, String.format("s2s out exception: %s %s", to, streamID), e); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } } public void sendDialbackVerify(String sid, String key) { try { - sendStanza("<db:verify from='" + XMPPComponent.HOSTNAME + "' to='" + to + "' id='" + sid + "'>" + key + "</db:verify>"); + sendStanza("<db:verify from='" + xmpp.HOSTNAME + "' to='" + to + "' id='" + sid + "'>" + key + "</db:verify>"); } catch (IOException e) { LOGGER.log(Level.WARNING, "STREAM TO " + to + " " + streamID + " ERROR", e); } diff --git a/src/main/java/com/juick/xmpp/s2s/JuickBot.java b/src/main/java/com/juick/xmpp/s2s/JuickBot.java index 25c75dfe..f0b71689 100644 --- a/src/main/java/com/juick/xmpp/s2s/JuickBot.java +++ b/src/main/java/com/juick/xmpp/s2s/JuickBot.java @@ -19,8 +19,13 @@ import java.util.regex.Pattern; * @author ugnich */ public class JuickBot { + XMPPComponent xmpp; + public JuickBot(XMPPComponent xmpp, JID JuickJID) { + this.xmpp = xmpp; + this.JuickJID = JuickJID; + } - public static final JID JuickJID = new JID("juick", "juick.com", "Juick"); + public final JID JuickJID; private static final String HELPTEXT = "@username text - Send private message\n" + "*tagname Blah-blah-blah - Post a message with tag 'tagname'\n" @@ -55,7 +60,7 @@ public class JuickBot { + "\n" + "Read more: http://juick.com/help/"; - public static boolean incomingPresence(Presence p) throws Exception { + public boolean incomingPresence(Presence p) throws Exception { final String username = p.to.Username.toLowerCase(); final boolean toJuick = username.equals("juick"); @@ -64,14 +69,12 @@ public class JuickBot { reply.from = new JID(p.to.Username, p.to.Host, null); reply.to = new JID(p.from.Username, p.from.Host, null); reply.type = Presence.Type.unsubscribe; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } else if (p.type.equals(Presence.Type.probe)) { int uid_to = 0; if (!toJuick) { - synchronized (XMPPComponent.sqlSync) { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - } + uid_to = UserQueries.getUIDbyName(xmpp.sql, username); } if (toJuick || uid_to > 0) { @@ -80,12 +83,12 @@ public class JuickBot { reply.from.Resource = "Juick"; reply.to = p.from; reply.priority = 10; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } else { Presence reply = new Presence(p.to, p.from, Presence.Type.error); reply.id = p.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } return true; @@ -94,50 +97,46 @@ public class JuickBot { if (toJuick) { canSubscribe = true; } else { - synchronized (XMPPComponent.sqlSync) { - int uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - if (uid_to > 0) { - PMQueries.addPMinRoster(XMPPComponent.sql, uid_to, p.from.Bare()); - canSubscribe = true; - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.addPMinRoster(xmpp.sql, uid_to, p.from.Bare()); + canSubscribe = true; } } if (canSubscribe) { Presence reply = new Presence(p.to, p.from, Presence.Type.subscribed); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); reply.from.Resource = "Juick"; reply.priority = 10; reply.type = null; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } else { Presence reply = new Presence(p.to, p.from, Presence.Type.error); reply.id = p.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } } else if (p.type.equals(Presence.Type.unsubscribe)) { if (!toJuick) { - synchronized (XMPPComponent.sqlSync) { - int uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - if (uid_to > 0) { - PMQueries.removePMinRoster(XMPPComponent.sql, uid_to, p.from.Bare()); - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.removePMinRoster(xmpp.sql, uid_to, p.from.Bare()); } } Presence reply = new Presence(p.to, p.from, Presence.Type.unsubscribed); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } return false; } - public static boolean incomingMessage(Message msg) throws Exception { + public boolean incomingMessage(Message msg) throws Exception { if (msg.body == null || msg.body.isEmpty()) { return true; } @@ -146,11 +145,9 @@ public class JuickBot { User user_from = null; String signuphash = ""; - synchronized (XMPPComponent.sqlSync) { - user_from = UserQueries.getUserByJID(XMPPComponent.sql, msg.from.Bare()); - if (user_from == null) { - signuphash = UserQueries.getSignUpHashByJID(XMPPComponent.sql, msg.from.Bare()); - } + user_from = UserQueries.getUserByJID(xmpp.sql, msg.from.Bare()); + if (user_from == null) { + signuphash = UserQueries.getSignUpHashByJID(xmpp.sql, msg.from.Bare()); } if (user_from == null) { @@ -160,7 +157,7 @@ public class JuickBot { } else { reply.body = "Внимание, системное сообщение!\nВаш JabberID не обнаружен в списке доверенных. Для того, чтобы отправить сообщение пользователю " + username + "@juick.com, пожалуйста зарегистрируйте свой JabberID в системе: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nWarning, system message!\nYour JabberID is not found in our server's white list. To send a message to " + username + "@juick.com, please sign up: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nIf you already have an account on Juick, you will be proposed to attach this JabberID to your existing account."; } - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } @@ -168,24 +165,19 @@ public class JuickBot { return incomingMessageJuick(user_from, msg); } - int uid_to = 0; - synchronized (XMPPComponent.sqlSync) { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); if (uid_to == 0) { Message reply = new Message(msg.to, msg.from, Message.Type.error); reply.id = msg.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } boolean success = false; - synchronized (XMPPComponent.sqlSync) { - if (!UserQueries.isInBLAny(XMPPComponent.sql, uid_to, user_from.getUID())) { - success = PMQueries.createPM(XMPPComponent.sql, user_from.getUID(), uid_to, msg.body); - } + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + success = PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, msg.body); } if (success) { @@ -193,29 +185,22 @@ public class JuickBot { m.from = new JID("juick", "juick.com", null); m.to = new JID(Integer.toString(uid_to), "push.juick.com", null); JuickMessage jmsg = new JuickMessage(); - synchronized (XMPPComponent.sqlSync) { - jmsg.setUser(UserQueries.getUserByUID(XMPPComponent.sql, user_from.getUID())); - } + jmsg.setUser(user_from); jmsg.setText(msg.body); m.childs.add(jmsg); - XMPPComponent.connRouter.router.send(m.toString()); + xmpp.router.send(m.toString()); m.to.Host = "ws.juick.com"; - XMPPComponent.connRouter.router.send(m.toString()); + xmpp.router.send(m.toString()); - String jid; + List<String> jids; boolean inroster = false; - synchronized (XMPPComponent.sqlSync) { - jid = UserQueries.getJIDbyUID(XMPPComponent.sql, uid_to); - if (jid != null) { - inroster = PMQueries.havePMinRoster(XMPPComponent.sql, user_from.getUID(), jid); - } - } - - if (jid != null) { + jids = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + for (String jid : jids) { Message mm = new Message(); mm.to = new JID(jid); mm.type = Message.Type.chat; + inroster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); if (inroster) { mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); mm.body = msg.body; @@ -223,21 +208,20 @@ public class JuickBot { mm.from = new JID("juick", "juick.com", "Juick"); mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + msg.body; } - XMPPComponent.sendOut(mm); + xmpp.sendOut(mm); } - } else { Message reply = new Message(msg.to, msg.from, Message.Type.error); reply.id = msg.id; reply.addChild(new Error(Error.Type.cancel, "not-allowed")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } return true; } private static Pattern regexPM = Pattern.compile("^\\@(\\S+)\\s+([\\s\\S]+)$"); - public static boolean incomingMessageJuick(User user_from, Message msg) throws Exception { + public boolean incomingMessageJuick(User user_from, Message msg) throws Exception { String command = msg.body.trim(); int commandlen = command.length(); @@ -274,59 +258,54 @@ public class JuickBot { return false; } - private static void commandPing(Message m) throws Exception { + private void commandPing(Message m) throws Exception { Presence p = new Presence(JuickJID, m.from); p.priority = 10; - XMPPComponent.sendOut(p); + xmpp.sendOut(p); Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = "PONG"; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandHelp(Message m) throws Exception { + private void commandHelp(Message m) throws Exception { Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = HELPTEXT; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandLogin(Message m, User user_from) throws Exception { + private void commandLogin(Message m, User user_from) throws Exception { Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(XMPPComponent.sql, user_from.getUID()); - XMPPComponent.sendOut(reply); + reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(xmpp.sql, user_from.getUID()); + xmpp.sendOut(reply); } - private static void commandPM(Message m, User user_from, String user_to, String body) throws Exception { + private void commandPM(Message m, User user_from, String user_to, String body) throws Exception { int ret = 0; int uid_to = 0; - String jid_to = null; + List<String> jids_to = null; boolean haveInRoster = false; - synchronized (XMPPComponent.sqlSync) { - if (user_to.indexOf('@') > 0) { - uid_to = UserQueries.getUIDbyJID(XMPPComponent.sql, user_to); - } else { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, user_to); - } + if (user_to.indexOf('@') > 0) { + uid_to = UserQueries.getUIDbyJID(xmpp.sql, user_to); + } else { + uid_to = UserQueries.getUIDbyName(xmpp.sql, user_to); + } - if (uid_to > 0) { - if (!UserQueries.isInBLAny(XMPPComponent.sql, uid_to, user_from.getUID())) { - if (PMQueries.createPM(XMPPComponent.sql, user_from.getUID(), uid_to, body)) { - jid_to = UserQueries.getJIDbyUID(XMPPComponent.sql, uid_to); - if (jid_to != null) { - haveInRoster = PMQueries.havePMinRoster(XMPPComponent.sql, user_from.getUID(), jid_to); - } - ret = 200; - } else { - ret = 500; - } + if (uid_to > 0) { + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + if (PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, body)) { + jids_to = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + ret = 200; } else { - ret = 403; + ret = 500; } } else { - ret = 404; + ret = 403; } + } else { + ret = 404; } if (ret == 200) { @@ -337,15 +316,16 @@ public class JuickBot { jmsg.setUser(user_from); jmsg.setText(body); msg.childs.add(jmsg); - XMPPComponent.connRouter.router.send(msg.toString()); + xmpp.router.send(msg.toString()); msg.to.Host = "ws.juick.com"; - XMPPComponent.connRouter.router.send(msg.toString()); + xmpp.router.send(msg.toString()); - if (jid_to != null) { + for (String jid : jids_to) { Message mm = new Message(); - mm.to = new JID(jid_to); + mm.to = new JID(jid); mm.type = Message.Type.chat; + haveInRoster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); if (haveInRoster) { mm.from = new JID(user_from.getUName(), "juick.com", "Juick"); mm.body = body; @@ -353,7 +333,7 @@ public class JuickBot { mm.from = new JID("juick", "juick.com", "Juick"); mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; } - XMPPComponent.sendOut(mm); + xmpp.sendOut(mm); } } @@ -365,22 +345,17 @@ public class JuickBot { reply.type = Message.Type.error; reply.body = "Error " + ret; } - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandBLShow(Message m, User user_from) throws Exception { - List<User> blusers; - List<String> bltags; - - synchronized (XMPPComponent.sqlSync) { - blusers = UserQueries.getUserBLUsers(XMPPComponent.sql, user_from.getUID()); - bltags = TagQueries.getUserBLTags(XMPPComponent.sql, user_from.getUID()); - } + private void commandBLShow(Message m, User user_from) throws Exception { + List<User> blusers = UserQueries.getUserBLUsers(xmpp.sql, user_from.getUID()); + List<String> bltags = TagQueries.getUserBLTags(xmpp.sql, user_from.getUID()); String txt = ""; if (bltags.size() > 0) { - for (int i = 0; i < bltags.size(); i++) { - txt += "*" + bltags.get(i) + "\n"; + for (String bltag : bltags) { + txt += "*" + bltag + "\n"; } if (blusers.size() > 0) { @@ -388,8 +363,8 @@ public class JuickBot { } } if (blusers.size() > 0) { - for (int i = 0; i < blusers.size(); i++) { - txt += "@" + blusers.get(i).getUName() + "\n"; + for (User bluser : blusers) { + txt += "@" + bluser.getUName() + "\n"; } } if (txt.isEmpty()) { @@ -398,6 +373,6 @@ public class JuickBot { Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = txt; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } } diff --git a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java index 13df5fd0..d0b231e2 100644 --- a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java +++ b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java @@ -1,21 +1,27 @@ package com.juick.xmpp.s2s; -import com.juick.xmpp.Stanza; -import com.juick.xmpp.StanzaChild; +import com.juick.User; +import com.juick.server.MessagesQueries; +import com.juick.server.SubscriptionsQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.*; import com.juick.xmpp.extensions.JuickMessage; +import com.juick.xmpp.extensions.Nickname; +import com.juick.xmpp.extensions.XOOB; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.xmlpull.v1.XmlPullParserException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -26,50 +32,51 @@ import java.util.logging.Logger; * * @author ugnich */ -public class XMPPComponent implements ServletContextListener { - - private static final Logger LOGGER = Logger.getLogger(XMPPComponent.class.getName()); - - public static final ExecutorService executorService = Executors.newCachedThreadPool(); - - public static String HOSTNAME = null; - public static String STATSFILE = null; - public static String keystore; - public static String keystorePassword; - public static List<String> brokenSSLhosts; - public static ConnectionRouter connRouter; - static final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>()); - static final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>()); - static final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>()); - static final Integer sqlSync = 0; - static java.sql.Connection sql; - final public static HashMap<String, StanzaChild> childParsers = new HashMap<>(); - - public static void addConnectionIn(ConnectionIn c) { +public class XMPPComponent implements ServletContextListener, Stream.StreamListener, + Message.MessageListener, Iq.IqListener, Presence.PresenceListener { + + private static final Logger logger = Logger.getLogger(XMPPComponent.class.getName()); + + public final ExecutorService executorService = Executors.newCachedThreadPool(); + StreamComponent router; + JuickBot bot; + + public String HOSTNAME, componentName; + public String STATSFILE = null; + public String keystore; + public String keystorePassword; + public List<String> brokenSSLhosts; + final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>()); + final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>()); + final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>()); + JdbcTemplate sql; + final public HashMap<String, StanzaChild> childParsers = new HashMap<>(); + + public void addConnectionIn(ConnectionIn c) { synchronized (inConnections) { inConnections.add(c); } } - public static void addConnectionOut(ConnectionOut c) { + public void addConnectionOut(ConnectionOut c) { synchronized (outConnections) { outConnections.add(c); } } - public static void removeConnectionIn(ConnectionIn c) { + public void removeConnectionIn(ConnectionIn c) { synchronized (inConnections) { inConnections.remove(c); } } - public static void removeConnectionOut(ConnectionOut c) { + public void removeConnectionOut(ConnectionOut c) { synchronized (outConnections) { outConnections.remove(c); } } - public static String getFromCache(String hostname) { + public String getFromCache(String hostname) { CacheEntry ret = null; synchronized (outCache) { for (Iterator<CacheEntry> i = outCache.iterator(); i.hasNext();) { @@ -84,7 +91,7 @@ public class XMPPComponent implements ServletContextListener { return (ret != null) ? ret.xml : null; } - public static ConnectionOut getConnectionOut(String hostname, boolean needReady) { + public ConnectionOut getConnectionOut(String hostname, boolean needReady) { synchronized (outConnections) { for (ConnectionOut c : outConnections) { if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) { @@ -95,7 +102,7 @@ public class XMPPComponent implements ServletContextListener { return null; } - public static ConnectionIn getConnectionIn(String streamID) { + public ConnectionIn getConnectionIn(String streamID) { synchronized (inConnections) { for (ConnectionIn c : inConnections) { if (c.streamID != null && c.streamID.equals(streamID)) { @@ -106,11 +113,11 @@ public class XMPPComponent implements ServletContextListener { return null; } - public static void sendOut(Stanza s) { + public void sendOut(Stanza s) { sendOut(s.to.Host, s.toString()); } - public static void sendOut(String hostname, String xml) { + public void sendOut(String hostname, String xml) { boolean haveAnyConn = false; ConnectionOut connOut = null; @@ -131,7 +138,7 @@ public class XMPPComponent implements ServletContextListener { try { connOut.sendStanza(xml); } catch (IOException e) { - LOGGER.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); + logger.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); } return; } @@ -152,12 +159,11 @@ public class XMPPComponent implements ServletContextListener { } if (!haveAnyConn) { - ConnectionOut connectionOut = null; try { - connectionOut = new ConnectionOut(hostname); - XMPPComponent.executorService.submit(connectionOut); + ConnectionOut connectionOut = new ConnectionOut(this, hostname); + executorService.submit(connectionOut); } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) { - LOGGER.log(Level.SEVERE, "s2s out error", e); + logger.log(Level.SEVERE, "s2s out error", e); } } } @@ -165,45 +171,74 @@ public class XMPPComponent implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { - LOGGER.info("component initialized"); - executorService.submit(() -> { - Properties conf = new Properties(); - try { - conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - HOSTNAME = conf.getProperty("hostname"); - String componentName = conf.getProperty("componentname"); - STATSFILE = conf.getProperty("statsfile"); - keystore = conf.getProperty("keystore"); - keystorePassword = conf.getProperty("keystore_password"); - brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); - Class.forName("com.mysql.jdbc.Driver"); - sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + - conf.getProperty("mysql_username", "") + "&password=" + conf.getProperty("mysql_password", "")); - - childParsers.put(JuickMessage.XMLNS, new JuickMessage()); - executorService.submit(() -> connRouter = new ConnectionRouter(componentName, conf.getProperty("xmpp_password"))); - executorService.submit(new ConnectionListener()); - executorService.submit(new CleaningUp()); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "XMPPComponent error", e); - } - }); + logger.info("component initialized"); + Properties conf = new Properties(); + try { + conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + HOSTNAME = conf.getProperty("hostname"); + componentName = conf.getProperty("componentname"); + JID Jid = new JID(conf.getProperty("xmppbot_jid")); + STATSFILE = conf.getProperty("statsfile"); + keystore = conf.getProperty("keystore"); + keystorePassword = conf.getProperty("keystore_password"); + brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(conf.getProperty("datasource_url")); + sql = new JdbcTemplate(dataSource); + bot = new JuickBot(this, Jid); + + childParsers.put(JuickMessage.XMLNS, new JuickMessage()); + + executorService.submit(() -> { + Socket routerSocket = null; + try { + routerSocket = new Socket("localhost", 5347); + router = new StreamComponent(new JID("s2s"), routerSocket.getInputStream(), routerSocket.getOutputStream(), conf.getProperty("xmpp_password")); + router.addChildParser(new JuickMessage()); + router.addListener((Stream.StreamListener) this); + router.addListener((Message.MessageListener) this); + router.addListener((Iq.IqListener) this); + router.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, "router error", e); + } + }); + executorService.submit(() -> { + final ServerSocket listener = new ServerSocket(5269); + logger.info("s2s listener ready"); + while (true) { + try { + Socket socket = listener.accept(); + ConnectionIn client = new ConnectionIn(this, bot, socket); + addConnectionIn(client); + executorService.submit(client); + } catch (Exception e) { + logger.log(Level.SEVERE, "s2s error", e); + } + } + }); + executorService.submit(new CleaningUp(this)); + + } catch (Exception e) { + logger.log(Level.SEVERE, "XMPPComponent error", e); + } } @Override public void contextDestroyed(ServletContextEvent sce) { - synchronized (XMPPComponent.outConnections) { - for (Iterator<ConnectionOut> i = XMPPComponent.outConnections.iterator(); i.hasNext();) { + synchronized (outConnections) { + for (Iterator<ConnectionOut> i = outConnections.iterator(); i.hasNext();) { ConnectionOut c = i.next(); c.closeConnection(); i.remove(); } } - synchronized (XMPPComponent.inConnections) { - for (Iterator<ConnectionIn> i = XMPPComponent.inConnections.iterator(); i.hasNext();) { + synchronized (inConnections) { + for (Iterator<ConnectionIn> i = inConnections.iterator(); i.hasNext();) { ConnectionIn c = i.next(); c.closeConnection(); i.remove(); @@ -211,42 +246,198 @@ public class XMPPComponent implements ServletContextListener { } try { - XMPPComponent.connRouter.closeConnection(); + closeRouterConnection(); } catch (IOException e) { - LOGGER.log(Level.WARNING, "router warning", e); + logger.log(Level.WARNING, "router warning", e); } + executorService.shutdown(); + logger.info("component destroyed"); + } + public void closeRouterConnection() throws IOException { + router.logoff(); + } - synchronized (XMPPComponent.sqlSync) { - if (XMPPComponent.sql != null) { - try { - XMPPComponent.sql.close(); - XMPPComponent.sql = null; - } catch (SQLException e) { - LOGGER.warning("SQL ERROR: " + e); + public void sendJuickMessage(JuickMessage jmsg) { + List<String> jids = new ArrayList<>(); + + if (jmsg.FriendsOnly) { + jids = SubscriptionsQueries.getJIDSubscribedToUser(sql, jmsg.getUser().getUID(), jmsg.FriendsOnly); + } else { + List<User> users = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + jids.add(jid); } } } - // Now deregister JDBC drivers in this context's ClassLoader: - // Get the webapp's ClassLoader - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - // Loop through all drivers - Enumeration<Driver> drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - if (driver.getClass().getClassLoader() == cl) { - // This driver was registered by the webapp's ClassLoader, so deregister it: - try { - LOGGER.info(String.format("Deregistering JDBC driver %s", driver.toString())); - DriverManager.deregisterDriver(driver); - } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, String.format("Error deregistering JDBC driver %s", driver), ex); - } + + String txt = "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID() + " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (String jid : jids) { + msg.to = new JID(jid); + sendOut(msg); + } + } + + public void sendJuickComment(JuickMessage jmsg) { + List<User> users; + String replyQuote; + String replyTo; + + users = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); + com.juick.Message replyMessage = jmsg.ReplyTo > 0 ? MessagesQueries.getReply(sql, jmsg.getMID(), jmsg.ReplyTo) + : MessagesQueries.getMessage(sql, jmsg.getMID()); + replyTo = replyMessage.getUser().getUName(); + replyQuote = getReplyQuote(replyMessage); + + String txt = "Reply by @" + jmsg.getUser().getUName() + ":\n" + replyQuote + "\n@" + replyTo + " "; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMID() + "/" + jmsg.getRID() + " http://juick.com/" + jmsg.getMID() + "#" + jmsg.getRID(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.addChild(jmsg); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + private String getReplyQuote(com.juick.Message q) { + String quote = q.getText(); + if (quote.length() > 50) { + quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; + } else if (quote.length() > 0) { + quote = ">" + quote.replace('\n', ' ') + "\n"; + } + return quote; + } + + public void sendJuickRecommendation(JuickMessage recomm) { + List<User> users; + JuickMessage jmsg; + jmsg = new JuickMessage(MessagesQueries.getMessage(sql, recomm.getMID())); + users = SubscriptionsQueries.getUsersSubscribedToUserRecommendations(sql, + recomm.getUser().getUID(), recomm.getMID(), jmsg.getUser().getUID()); + + String txt = "Recommended by @" + recomm.getUser().getUName() + ":\n"; + txt += "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID(); + if (jmsg.Replies > 0) { + if (jmsg.Replies % 10 == 1 && jmsg.Replies % 100 != 11) { + txt += " (" + jmsg.Replies + " reply)"; } else { - // driver was not registered by the webapp's ClassLoader and may be in use elsewhere - LOGGER.log(Level.SEVERE, String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver)); + txt += " (" + jmsg.Replies + " replies)"; } } - executorService.shutdown(); - LOGGER.info("component destroyed"); + txt += " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + @Override + public boolean onIq(Iq iq) { + JID jid = iq.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (IQ): " + iq.toString()); + sendOut(iq); + } + return false; + } + + @Override + public void onMessage(Message xmsg) { + logger.info("STREAM ROUTER (PROCESS): " + xmsg.toString()); + JuickMessage jmsg = (JuickMessage) xmsg.getChild(JuickMessage.XMLNS); + JID jid = xmsg.to; + if (jid.Host.equals(componentName)) { + if (jmsg != null) { + if (jid.Username != null && jid.Username.equals("recomm")) { + sendJuickRecommendation(jmsg); + } else { + if (jmsg.getRID() > 0) { + sendJuickComment(jmsg); + } else if (jmsg.getMID() > 0) { + sendJuickMessage(jmsg); + } + } + } + } else { + sendOut(xmsg); + } + } + + @Override + public void onPresence(Presence presence) { + JID jid = presence.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (PRESENCE): " + presence.toString()); + sendOut(presence); + } + } + + @Override + public void onStreamReady() { + logger.info("STREAM ROUTER (READY)"); + } + + @Override + public void onStreamFail(Exception ex) { + logger.log(Level.SEVERE, "STREAM ROUTER (FAIL)", ex); } } |