/*
* Juick
* Copyright (C) 2008-2011, Ugnich Anton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package com.juick.http.www;
import com.juick.CrosspostComponent;
import com.juick.JuickComponent;
import com.juick.JuickNotificator;
import com.juick.PushComponent;
import com.juick.server.UserQueries;
import com.juick.xmpp.JID;
import com.juick.xmpp.Stream;
import com.juick.xmpp.StreamComponent;
import com.juick.xmpp.s2s.S2SComponent;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import ru.sape.Sape;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.Channels;
import java.nio.channels.CompletionHandler;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
* @author Ugnich Anton
*/
@WebServlet(name = "Main", urlPatterns = {"/"})
@MultipartConfig(fileSizeThreshold = 1024 * 1024, maxRequestSize = 1024 * 1024 * 10)
public class Main extends HttpServlet implements JuickNotificator, Stream.StreamListener {
static ExecutorService executorService;
JdbcTemplate sql;
JdbcTemplate sqlSearch;
Stream xmpp;
Home home = new Home();
Discover discover = new Discover();
PM pm = new PM();
Login login = new Login();
Help help = new Help();
User pagesUser = new User();
UserThread pagesUserThread = new UserThread();
NewMessage pagesNewMessage = new NewMessage();
FacebookLogin loginFacebook = new FacebookLogin();
VKontakteLogin loginVK = new VKontakteLogin();
TwitterAuth twitterAuth;
SignUp signup = new SignUp();
Settings settings = new Settings();
RSS rss = new RSS();
static List components = new ArrayList<>();
@Override
public void init() throws ServletException {
super.init();
try {
Properties conf = new Properties();
conf.load(getServletContext().getResourceAsStream("WEB-INF/juick.conf"));
executorService = Executors.newWorkStealingPool();
getServletContext().setAttribute("es", executorService);
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"));
dataSource.setUrl(conf.getProperty("datasource_url"));
sql = new JdbcTemplate(dataSource);
getServletContext().setAttribute("sql", sql);
DriverManagerDataSource searchDatasource = new DriverManagerDataSource();
searchDatasource.setDriverClassName("com.mysql.jdbc.Driver");
searchDatasource.setUrl("jdbc:mysql://127.0.0.1:9306?autoReconnect=true&useUnicode=yes&characterEncoding=utf8&maxAllowedPacket=512000");
sqlSearch = new JdbcTemplate(searchDatasource);
getServletContext().setAttribute("sqlSearch", sqlSearch);
setupXmppComponent(conf.getProperty("xmpp_password"));
twitterAuth = new TwitterAuth(conf.getProperty("twitter_consumer_key"),
conf.getProperty("twitter_consumer_secret"));
PageTemplates.sape = new Sape(conf.getProperty("sape_user"), "juick.com", 2000, 3600);
components.add(new S2SComponent(sql, executorService, conf));
components.add(new CrosspostComponent(sql, conf));
components.add(new PushComponent(sql, conf));
} catch (Exception e) {
log("www failed", e);
}
}
@Override
public void push(com.juick.xmpp.Message msg) {
for (JuickComponent c : components) {
c.messageReceived(msg);
}
}
public void setupXmppComponent(final String password) {
executorService.submit(() -> {
try {
AsynchronousSocketChannel socket = AsynchronousSocketChannel.open();
socket.connect(new InetSocketAddress("localhost", 5347), socket, new CompletionHandler() {
@Override
public void completed(Void result, AsynchronousSocketChannel attachment) {
xmpp = new StreamComponent(new JID("", "www.juick.com", ""), Channels.newInputStream(socket),
Channels.newOutputStream(socket), password);
xmpp.addListener(Main.this);
xmpp.startParsing();
}
@Override
public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
log("www xmpp failed");
}
});
} catch (IOException e) {
log("xmpp exception", e);
}
});
}
@Override
public void onStreamFail(Exception e) {log("XMPP STREAM FAIL", e);}
@Override
public void onStreamReady() {
log("XMPP STREAM READY");
}
/**
* Handles the HTTP GET
method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (request.getCharacterEncoding() == null) {
request.setCharacterEncoding("UTF-8");
}
String uri = request.getRequestURI();
if (uri.equals("/")) {
String tag = request.getParameter("tag");
if (tag != null) {
Utils.sendPermanentRedirect(response, "/tag/" + URLEncoder.encode(tag, "UTF-8"));
} else {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
home.doGet(sql, sqlSearch, request, response, visitor);
}
} else if (uri.equals("/post")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor != null) {
pagesNewMessage.doGetNewMessage(sql, request, response, visitor);
} else {
Utils.sendTemporaryRedirect(response, "/login");
}
} else if (uri.equals("/login")) {
if (request.getQueryString() == null) {
login.doGetLoginForm(sql, request, response);
} else {
login.doGetLogin(sql, request, response);
}
} else if (uri.startsWith("/pm/")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor == null) {
Utils.sendTemporaryRedirect(response, "/login");
} else {
switch (uri) {
case "/pm/inbox":
pm.doGetInbox(sql, request, response, visitor);
break;
case "/pm/sent":
pm.doGetSent(sql, request, response, visitor);
break;
default:
Errors.doGet404(sql, request, response);
break;
}
}
} else if (uri.startsWith("/rss/")) {
String uname = uri.substring(5);
int uid = UserQueries.getUIDbyName(sql, uname);
if (uid > 0) {
rss.doGet(sql, request, response, uid, uname);
} else {
response.sendError(404);
}
} else if (uri.equals("/logout")) {
login.doGetLogout(sql, request, response);
} else if (uri.equals("/settings")) {
settings.doGet(sql, request, response);
} else if (uri.equals("/_fblogin")) {
loginFacebook.doGet(sql, request, response);
} else if (uri.equals("/_vklogin")) {
loginVK.doGet(sql, request, response);
} else if (uri.startsWith("/_twitter")) {
twitterAuth.doGet(sql, request, response);
} else if (uri.equals("/signup")) {
signup.doGet(sql, request, response);
} else if (uri.equals("/help") || uri.equals("/help/")) {
help.doRedirectToHelpIndex(sql, request, response);
} else if (uri.startsWith("/help/")) {
help.doGetHelp(sql, request, response);
} else if (uri.startsWith("/tag/")) {
discover.doGet(sql, sqlSearch, request, response);
} else if (uri.matches("^/\\d+$")) {
String strID = request.getRequestURI().substring(1);
int mid = 0;
try {
mid = Integer.parseInt(strID);
} catch (NumberFormatException e) {
}
if (mid > 0) {
com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid);
if (author != null) {
Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid);
return;
}
}
Errors.doGet404(sql, request, response);
} else if (uri.matches("^/[^/]+$")) {
com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, request.getRequestURI().substring(1));
if (user != null) {
Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/");
} else {
Errors.doGet404(sql, request, response);
}
} else if (uri.matches("^/.+/.*")) {
String uriparts[] = uri.split("/");
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, sqlSearch, request, response, user);
} else if (uriparts[2].equals("tags")) {
pagesUser.doGetTags(sql, request, response, user);
} else if (uriparts[2].equals("friends")) {
pagesUser.doGetFriends(sql, request, response, user);
} else if (uriparts[2].equals("readers")) {
pagesUser.doGetReaders(sql, request, response, user);
} else {
int mid = 0;
try {
mid = Integer.parseInt(uriparts[2]);
} catch (NumberFormatException e) {
}
if (mid > 0) {
com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid);
if (author != null) {
if (!author.getUName().equals(user.getUName())) {
Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid);
} else {
pagesUserThread.doGetThread(sql, request, response, mid);
}
} else {
Errors.doGet404(sql, request, response);
}
} else {
Errors.doGet404(sql, request, response);
}
}
} else if (user != null && !user.Banned) {
Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/" + (uriparts.length > 2 ? uriparts[2] : ""));
} else {
Errors.doGet404(sql, request, response);
}
} else {
Errors.doGet404(sql, request, response);
}
}
/**
* Handles the HTTP POST
method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (request.getCharacterEncoding() == null) {
request.setCharacterEncoding("UTF-8");
}
String uri = request.getRequestURI();
if (uri.equals("/post")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor != null && !visitor.Banned) {
pagesNewMessage.doPostMessage(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
} else if (uri.equals("/comment")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor != null && !visitor.Banned) {
pagesNewMessage.doPostComment(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
} else if (uri.equals("/like")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor != null && !visitor.Banned) {
pagesNewMessage.doPostRecomm(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
} else if (uri.equals("/pm/send")) {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor != null && !visitor.Banned) {
pm.doPostPM(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
} else if (uri.equals("/login")) {
login.doPostLogin(sql, request, response);
} else if (uri.equals("/signup")) {
signup.doPost(sql, request, response);
} else if (uri.equals("/settings")) {
settings.doPost(sql, request, response);
} else {
response.sendError(405);
}
}
@Override
public void destroy() {
super.destroy();
executorService.shutdown();
// Now deregister JDBC drivers in this context's ClassLoader:
// Get the webapp's ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// Loop through all drivers
Enumeration 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 {
log(String.format("Deregistering JDBC driver %s", driver.toString()));
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
log(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
log(String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver));
}
}
}
}