/*
* 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.www;
import com.juick.server.UserQueries;
import com.juick.xmpp.JID;
import com.juick.xmpp.Stream;
import com.juick.xmpp.StreamComponent;
import com.mitchellbosecke.pebble.error.PebbleException;
import net.jodah.failsafe.Execution;
import net.jodah.failsafe.RetryPolicy;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.xmlpull.v1.XmlPullParserException;
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.Socket;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
*
* @author Ugnich Anton
*/
@WebServlet(name = "Main", urlPatterns = {"/"})
@MultipartConfig(fileSizeThreshold = 1024 * 1024, maxRequestSize = 1024 * 1024 * 10)
public class Main extends HttpServlet implements Stream.StreamListener {
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();
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;
FacebookLogin loginFacebook = new FacebookLogin();
VKontakteLogin loginVK = new VKontakteLogin();
TwitterAuth twitterAuth;
SignUp signup = new SignUp();
Settings settings;
RSS rss = new RSS();
ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
Execution execution;
@Override
public void init() throws ServletException {
super.init();
try {
Properties conf = new Properties();
conf.load(getServletContext().getResourceAsStream("/WEB-INF/juick.conf"));
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"));
dataSource.setUrl(conf.getProperty("datasource_url"));
BasicDataSource dataSourceSearch = new BasicDataSource();
dataSourceSearch.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"));
dataSourceSearch.setUrl(sqlSearchConnStr);
sql = new JdbcTemplate(dataSource);
sqlSearch = new JdbcTemplate(dataSourceSearch);
setupXmppComponent(new JID(conf.getProperty("www_xmpp_jid", "www.juick.local")),
conf.getProperty("xmpp_password"), NumberUtils.toInt(conf.getProperty("xmpp_port", ""), 5347));
twitterAuth = new TwitterAuth(conf.getProperty("twitter_consumer_key"),
conf.getProperty("twitter_consumer_secret"));
String tmpDir = conf.getProperty("upload_tmp_dir", "/var/www/juick.com/i/tmp/");
Utils.setTmpDir(tmpDir);
String imgPath = conf.getProperty("img_path", "/var/www/juick.com/i/");
pagesNewMessage = new NewMessage(tmpDir, imgPath);
settings = new Settings(imgPath);
String sapeUser = conf.getProperty("sape_user", "");
if (!Objects.equals(sapeUser, "")) {
PageTemplates.sape = new Sape(sapeUser, "juick.com", 2000, 3600);
} else {
log("Sape is not initialized");
}
} catch (Exception e) {
log(null, e);
}
}
public void setupXmppComponent(final JID componentJid, final String password, final int port) {
@SuppressWarnings("unchecked") RetryPolicy retryPolicy = new RetryPolicy()
.withBackoff(1, 30, TimeUnit.SECONDS)
.withJitter(0.1)
.retryOn(IOException.class, XmlPullParserException.class);
execution = new Execution(retryPolicy);
executorService.submit(() -> {
while (!execution.isComplete()) {
try {
Socket socket = new Socket("localhost", port);
xmpp = new StreamComponent(componentJid, socket.getInputStream(), socket.getOutputStream(), password);
xmpp.addListener(Main.this);
xmpp.startParsing();
} catch (IOException e) {
log("XMPP router disconnected, reconnecting...");
}
}
});
}
@Override
public void onStreamFail(Exception e) {
log("XMPP STREAM FAIL:" + e);
execution.recordFailure(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.getUID() > 0) {
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.getUID() == 0) {
Utils.sendTemporaryRedirect(response, "/login");
} else {
switch (uri) {
case "/pm/inbox":
try {
pm.doGetInbox(sql, request, response, visitor);
} catch (PebbleException e) {
log("pebble exception", e);
response.sendError(500);
}
break;
case "/pm/sent":
try {
pm.doGetSent(sql, request, response, visitor);
} catch (PebbleException e) {
log("pebble exception", e);
response.sendError(500);
}
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")) {
try {
settings.doGet(sql, request, response);
} catch (PebbleException e) {
log("pebble exception", e);
response.sendError(500);
}
} 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(response);
} else if (uri.startsWith("/help/")) {
try {
help.doGetHelp(sql, request, response);
} catch (PebbleException e) {
log("pebble exception", e);
response.sendError(500);
} catch (URISyntaxException e) {
log("help exception", e);
response.sendError(500);
}
} 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();
switch (uri) {
case "/post": {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor.getUID() > 0 && !visitor.Banned) {
pagesNewMessage.doPostMessage(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
break;
}
case "/comment": {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor.getUID() > 0 && !visitor.Banned) {
pagesNewMessage.doPostComment(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
break;
}
case "/like": {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor.getUID() > 0 && !visitor.Banned) {
pagesNewMessage.doPostRecomm(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
break;
}
case "/pm/send": {
com.juick.User visitor = Utils.getVisitorUser(sql, request, response);
if (visitor.getUID() > 0 && !visitor.Banned) {
pm.doPostPM(sql, request, response, xmpp, visitor);
} else {
response.sendError(403);
}
break;
}
case "/login":
login.doPostLogin(sql, request, response);
break;
case "/signup":
signup.doPost(sql, request, response);
break;
case "/settings":
try {
settings.doPost(sql, request, response);
} catch (PebbleException e) {
log("pebble exception", e);
response.sendError(500);
}
break;
default:
response.sendError(405);
break;
}
}
}