/*
* Copyright (C) 2008-2017, Juick
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package com.juick.www.controllers;
import com.juick.Status;
import com.juick.Tag;
import com.juick.server.util.*;
import com.juick.service.*;
import com.juick.www.WebApp;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import rocks.xmpp.addr.Jid;
import rocks.xmpp.core.stanza.model.Message;
import rocks.xmpp.extensions.nick.model.Nickname;
import rocks.xmpp.extensions.oob.model.x.OobX;
import javax.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Ugnich Anton
*/
@Controller
public class NewMessage {
@Inject
private TagService tagService;
@Inject
private MessagesService messagesService;
@Inject
private UserService userService;
@Inject
private SubscriptionService subscriptionService;
@Inject
private CrosspostService crosspostService;
@Inject
private PMQueriesService pmQueriesService;
@Inject
private WebApp webApp;
@Value("${img_path:/var/www/juick.com/i/}")
private String imgDir;
@Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}")
private String tmpDir;
private static final Logger logger = LoggerFactory.getLogger(NewMessage.class);
@GetMapping("/post")
protected String postAction(@RequestParam(required = false) String body, ModelMap model) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
model.addAttribute("title", "Написать");
model.addAttribute("headers", "");
model.addAttribute("visitor", visitor);
if (body == null) {
body = StringUtils.EMPTY;
} else {
if (body.length() > 4096) {
body = body.substring(0, 4096);
}
body = StringEscapeUtils.escapeHtml4(body);
}
model.addAttribute("body", body);
model.addAttribute("visitor", visitor);
model.addAttribute("tags", tagService.getUserTagStats(visitor.getUid()).stream()
.sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList()));
return "views/post";
}
@PostMapping("/post")
public String postResult(@RequestParam(required = false) String img,
@RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body,
@RequestParam(required = false, name = "tags") String tagsStr,
@RequestParam(required = false) MultipartFile attach, ModelMap model) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
if ((StringUtils.isEmpty(body) || body.length() > 4096) && StringUtils.isEmpty(img) && attach == null) {
throw new HttpBadRequestException();
}
body = body.replace("\r", StringUtils.EMPTY);
List tags = webApp.parseTags(tagsStr);
String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
if (StringUtils.isBlank(attachmentFName) && StringUtils.isNotBlank(img)) {
try {
URL imgUrl = new URL(img);
attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
} catch (Exception e) {
logger.error("DOWNLOAD ERROR", e);
throw new HttpBadRequestException();
}
}
String attachmentType = StringUtils.isNotEmpty(attachmentFName) ? attachmentFName.substring(attachmentFName.length() - 3) : null;
int mid = messagesService.createMessage(visitor.getUid(), body, attachmentType, tags);
subscriptionService.subscribeMessage(mid, visitor.getUid());
Message xmsg = new Message();
xmsg.setFrom(Jid.of("juick@juick.com"));
xmsg.setType(Message.Type.CHAT);
xmsg.setThread("juick-" + mid);
com.juick.Message jmsg = messagesService.getMessage(mid);
xmsg.addExtension(jmsg);
xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName()));
if (StringUtils.isNotEmpty(attachmentFName)) {
String fname = mid + "." + attachmentType;
String attachmentURL = "http://i.juick.com/photos-1024/" + fname;
ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir);
body = attachmentURL + "\n" + body;
try {
xmsg.addExtension(new OobX(new URI(attachmentURL)));
} catch (URISyntaxException e) {
logger.warn("invalid uri: {} exception {}", attachmentURL, e);
}
}
if (webApp.getXmpp() != null) {
xmsg.setBody("@" + jmsg.getUser().getName() + ":" + jmsg.getTagsString() + "\n" + body + "\n\n#" + mid + " http://juick.com/" + mid);
xmsg.setTo(Jid.of("juick@s2s.juick.com"));
webApp.getXmpp().send(xmsg);
xmsg.setTo(Jid.of("juick@ws.juick.com"));
webApp.getXmpp().send(xmsg);
xmsg.setTo(Jid.of("juick@push.juick.com"));
webApp.getXmpp().send(xmsg);
xmsg.setTo(Jid.of("jubo@nologin.ru"));
webApp.getXmpp().send(xmsg);
} else {
logger.warn("XMPP unavailable");
}
//
model.addAttribute("title", "Сообщение опубликовано");
model.addAttribute("visitor", visitor);
model.addAttribute("user", visitor);
String hashtags = StringUtils.EMPTY;
String tagscomma = StringUtils.EMPTY;
for (int i = 0; i < jmsg.getTags().size(); i++) {
if (i > 0) {
hashtags += " ";
tagscomma += ",";
}
hashtags += "#" + jmsg.getTags().get(i);
tagscomma += jmsg.getTags().get(i);
}
model.addAttribute("url", "http://juick.com/" + mid);
if (!crosspostService.getTwitterToken(visitor.getUid()).isPresent()) {
String sharetwi = hashtags + " " + body;
if (sharetwi.length() > 115) {
sharetwi = sharetwi.substring(0, 114) + "…";
}
sharetwi += " http://juick.com/" + mid;
model.addAttribute("sharetwi", sharetwi);
}
if (!crosspostService.getFacebookToken(visitor.getUid()).isPresent()) {
model.addAttribute("facebook", 1);
}
model.addAttribute("tags", tagService.getUserTagStats(visitor.getUid()).stream()
.sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList()));
return "views/post_success";
}
@PostMapping("/comment")
public String doPostComment(
@RequestParam Integer mid,
@RequestParam(required = false, defaultValue = "0") Integer rid,
@RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body,
@RequestParam(required = false, defaultValue = StringUtils.EMPTY) String img,
@RequestParam(required = false) MultipartFile attach) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
com.juick.Message msg = messagesService.getMessage(mid);
if (msg == null) {
throw new HttpNotFoundException();
}
com.juick.Message reply = null;
if (rid > 0) {
reply = messagesService.getReply(mid, rid);
if (reply == null) {
throw new HttpNotFoundException();
}
}
if ((StringUtils.isEmpty(body) || body.length() > 4096) && StringUtils.isEmpty(img) && attach == null) {
throw new HttpBadRequestException();
}
body = body.replace("\r", StringUtils.EMPTY);
if ((msg.ReadOnly && msg.getUser().getUid() != visitor.getUid())
|| userService.isInBLAny(msg.getUser().getUid(), visitor.getUid())
|| (reply != null && userService.isInBLAny(reply.getUser().getUid(), visitor.getUid()))) {
throw new HttpForbiddenException();
}
String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
try {
URL imgUrl = new URL(img);
attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
} catch (Exception e) {
logger.error("DOWNLOAD ERROR", e);
throw new HttpBadRequestException();
}
}
String attachmentType = StringUtils.isNotEmpty(attachmentFName) ? attachmentFName.substring(attachmentFName.length() - 3) : null;
int ridnew = messagesService.createReply(mid, rid, visitor.getUid(), body, attachmentType);
subscriptionService.subscribeMessage(mid, visitor.getUid());
Message xmsg = new Message();
xmsg.setFrom(Jid.of("juick@juick.com"));
xmsg.setType(Message.Type.CHAT);
xmsg.setThread("juick-" + mid);
com.juick.Message jmsg = messagesService.getReply(mid, ridnew);
xmsg.addExtension(jmsg);
String quote = reply != null ? reply.getText() : msg.getText();
if (quote.length() >= 50) {
quote = quote.substring(0, 47) + "...";
}
xmsg.addExtension(new Nickname("@" + jmsg.getUser().getName()));
if (StringUtils.isNotEmpty(attachmentFName)) {
String fname = mid + "-" + ridnew + "." + attachmentType;
String attachmentURL = "http://i.juick.com/photos-1024/" + fname;
ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir);
body = attachmentURL + "\n" + body;
try {
xmsg.addExtension(new OobX(new URI(attachmentURL)));
} catch (URISyntaxException e) {
logger.warn("invalid uri: {}, exception {}", attachmentURL, e);
}
}
if (webApp.getXmpp() != null) {
xmsg.setBody("Reply by @" + jmsg.getUser().getName() + ":\n>" + quote + "\n" + body + "\n\n#" +
mid + "/" + ridnew + " http://juick.com/" + mid + "#" + ridnew);
xmsg.setTo(Jid.of("juick@s2s.juick.com"));
webApp.getXmpp().send(xmsg);
xmsg.setTo(Jid.of("juick@ws.juick.com"));
webApp.getXmpp().send(xmsg);
xmsg.setTo(Jid.of("juick@push.juick.com"));
webApp.getXmpp().send(xmsg);
} else {
logger.warn("XMPP unavailable");
}
return "redirect:/" + msg.getUser().getName() + "/" + mid + "#" + ridnew;
}
@PostMapping("/like")
@ResponseBody
public Status doPostRecomm(@RequestParam Integer mid) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
if (visitor.getUid() == 0) {
throw new HttpForbiddenException();
}
com.juick.Message msg = messagesService.getMessage(mid);
if (msg == null) {
throw new HttpNotFoundException();
}
if (msg.getUser().getUid() == visitor.getUid()) {
throw new HttpForbiddenException();
}
boolean res = messagesService.recommendMessage(mid, visitor.getUid());
if (res) {
if (webApp.getXmpp() != null) {
Message xmsg = new Message();
xmsg.setFrom(Jid.of("juick@juick.com"));
xmsg.setTo(Jid.of("recomm@s2s.juick.com"));
com.juick.Message jmsg = new com.juick.Message();
jmsg.setMid(mid);
jmsg.setUser(visitor);
xmsg.addExtension(jmsg);
webApp.getXmpp().send(xmsg);
} else {
logger.warn("XMPP unavailable");
}
return Status.OK;
} else {
throw new HttpBadRequestException();
}
}
@PostMapping("/pm/send")
public String doPostPM(@RequestParam(name = "uname", required = false) String unameParam,
@RequestParam String body) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
if (visitor.getUid() == 0 || visitor.isBanned()) {
throw new HttpForbiddenException();
}
String uname = unameParam;
if (uname.startsWith("@")) {
uname = uname.substring(1);
}
int uid = 0;
if (WebUtils.isUserName(uname)) {
uid = userService.getUIDbyName(uname);
}
if (uid == 0 || body.length() > 10240) {
throw new HttpBadRequestException();
}
if (userService.isInBLAny(uid, visitor.getUid())) {
throw new HttpForbiddenException();
}
if (pmQueriesService.createPM(visitor.getUid(), uid, body)) {
if (webApp.getXmpp() != null) {
Message msg = new Message();
msg.setFrom(Jid.of("juick@juick.com"));
msg.setTo(Jid.of(String.format("%d@push.juick.com", uid)));
com.juick.Message jmsg = new com.juick.Message();
jmsg.setUser(visitor);
jmsg.setText(body);
msg.addExtension(jmsg);
webApp.getXmpp().send(msg);
msg.setTo(Jid.of(String.format("%d@ws.juick.com", uid)));
webApp.getXmpp().send(msg);
List jids = userService.getJIDsbyUID(uid);
for (String jid : jids) {
Message mm = new Message();
mm.setTo(Jid.of(jid));
mm.setType(Message.Type.CHAT);
if (pmQueriesService.havePMinRoster(visitor.getUid(), jid)) {
mm.setFrom(Jid.of(jmsg.getUser().getName(), "juick.com", "Juick"));
mm.setBody(body);
} else {
mm.setFrom(Jid.of("juick", "juick.com", "Juick"));
mm.setBody("Private message from @" + jmsg.getUser().getName() + ":\n" + body);
}
webApp.getXmpp().send(mm);
}
} else {
logger.warn("XMPP unavailable");
}
return "redirect:/pm/sent";
} else {
throw new HttpBadRequestException();
}
}
@PostMapping("/post2")
public String doPostMessage(@RequestParam(name = "body") String bodyParam,
@RequestParam(required = false) String img,
@RequestParam(required = false) String referer,
@RequestParam(required = false) MultipartFile attach) throws IOException {
com.juick.User visitor = UserUtils.getCurrentUser();
if (visitor.getUid() == 0 || visitor.isBanned()) {
throw new HttpForbiddenException();
}
String body = bodyParam.replace("\r", StringUtils.EMPTY);
String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) {
try {
URL imgUrl = new URL(img);
attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
} catch (Exception e) {
logger.error("DOWNLOAD ERROR", e);
throw new HttpBadRequestException();
}
}
Message msg = new Message();
msg.setType(Message.Type.CHAT);
msg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "perl"));
msg.setTo(Jid.of("juick@juick.com/Juick"));
msg.setBody(body);
try {
if (StringUtils.isNotEmpty(attachmentFName)) {
String attachmentUrl = String.format("juick://%s", attachmentFName);
msg.addExtension(new OobX(new URI(attachmentUrl), "!!!!Juick!!"));
}
webApp.getXmpp().sendMessage(msg);
} catch (URISyntaxException e1) {
logger.warn("attachment error", e1);
}
if (StringUtils.isBlank(referer) || referer.substring(0, 21).equals("http://juick.com/post")
|| referer.substring(0, 22).equals("https://juick.com/post")) {
return "redirect:/?show=my";
}
return "redirect:" + referer;
}
}