package net.pms.remote;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import net.pms.PMS;
import net.pms.configuration.PmsConfiguration;
import net.pms.configuration.RendererConfiguration;
import net.pms.configuration.WebRender;
import net.pms.dlna.CodeEnter;
import net.pms.dlna.DLNAResource;
import net.pms.dlna.Playlist;
import net.pms.dlna.RootFolder;
import net.pms.dlna.virtual.VirtualVideoAction;
import net.pms.formats.Format;
import net.pms.util.UMSUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("restriction")
public class RemoteBrowseHandler implements HttpHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteBrowseHandler.class);
private RemoteWeb parent;
private static PmsConfiguration configuration = PMS.getConfiguration();
public RemoteBrowseHandler(RemoteWeb parent) {
this.parent = parent;
}
private String mkBrowsePage(String id, HttpExchange t) throws IOException {
LOGGER.debug("Make browse page " + id);
String user = RemoteUtil.userName(t);
RootFolder root = parent.getRoot(user, true, t);
String search = RemoteUtil.getQueryVars(t.getRequestURI().getQuery(), "str");
List<DLNAResource> res = root.getDLNAResources(id, true, 0, 0, root.getDefaultRenderer(), search);
boolean upnpAllowed = RemoteUtil.bumpAllowed(t);
boolean upnpControl = RendererConfiguration.hasConnectedControlPlayers();
if (!res.isEmpty() &&
res.get(0).getParent() != null &&
(res.get(0).getParent() instanceof CodeEnter)) {
// this is a code folder the search string is entered code
CodeEnter ce = (CodeEnter)res.get(0).getParent();
ce.setEnteredCode(search);
if(!ce.validCode(ce)) {
// invalid code throw error
throw new IOException("Auth error");
}
DLNAResource real = ce.getResource();
if (!real.isFolder()) {
// no folder -> redirect
Headers hdr = t.getResponseHeaders();
hdr.add("Location", "/play/" + real.getId());
RemoteUtil.respond(t, "", 302, "text/html");
// return null here to avoid multipl responses
return null;
}
else {
// redirect to ourself
Headers hdr = t.getResponseHeaders();
hdr.add("Location", "/browse/" + real.getResourceId());
RemoteUtil.respond(t, "", 302, "text/html");
return null;
}
}
if (StringUtils.isNotEmpty(search) && !(res instanceof CodeEnter)) {
UMSUtils.postSearch(res, search);
}
boolean hasFile = false;
ArrayList<String> folders = new ArrayList<>();
ArrayList<HashMap<String, String>> media = new ArrayList<>();
StringBuilder sb = new StringBuilder();
sb.setLength(0);
sb.append("<a href=\"javascript:history.back()\" title=\"").append(RemoteUtil.getMsgString("Web.10", t)).append("\">");
sb.append("<span>").append(RemoteUtil.getMsgString("Web.10", t)).append("</span>");
sb.append("</a>");
folders.add(sb.toString());
// Generate innerHtml snippets for folders and media items
for (DLNAResource r : res) {
String newId = r.getResourceId();
String idForWeb = URLEncoder.encode(newId, "UTF-8");
String thumb = "/thumb/" + idForWeb;
String name = StringEscapeUtils.escapeHtml(r.resumeName());
if (r instanceof VirtualVideoAction) {
// Let's take the VVA real early
sb.setLength(0);
HashMap<String, String> item = new HashMap<>();
sb.append("<a href=\"#\" onclick=\"umsAjax('/play/").append(idForWeb)
.append("', true);return false;\" title=\"").append(name).append("\">")
.append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">")
.append("</a>");
item.put("thumb", sb.toString());
sb.setLength(0);
sb.append("<a href=\"#\" onclick=\"umsAjax('/play/").append(idForWeb)
.append("', true);return false;\" title=\"").append(name).append("\">")
.append("<span class=\"caption\">").append(name).append("</span>")
.append("</a>");
item.put("caption", sb.toString());
item.put("bump", "<span class=\"floatRight\"></span>");
media.add(item);
hasFile = true;
continue;
}
if (r.isFolder()) {
sb.setLength(0);
// The resource is a folder
String p = "/browse/" + idForWeb;
boolean code = (r instanceof CodeEnter);
String txt = RemoteUtil.getMsgString("Web.8", t);
if (code) {
txt = RemoteUtil.getMsgString("Web.9", t);
}
if (r.getClass().getName().contains("SearchFolder") || code) {
// search folder add a prompt
// NOTE!!!
// Yes doing getClass.getname is REALLY BAD, but this
// is to make legacy plugins utilize this function as well
sb.append("<a href=\"javascript:void(0);\" onclick=\"searchFun('").append(p).append("','")
.append(txt).append("');\" title=\"").append(name).append("\">");
} else {
sb.append("<a href=\"").append(p).append("\" oncontextmenu=\"searchFun('").append(p)
.append("','").append(txt).append("');\" title=\"").append(name).append("\">");
}
sb.append("<span>").append(name).append("</span>");
sb.append("</a>");
folders.add(sb.toString());
} else {
// The resource is a media file
sb.setLength(0);
HashMap<String, String> item = new HashMap<>();
if (upnpAllowed) {
if (upnpControl) {
sb.append("<a class=\"bumpIcon\" href=\"javascript:bump.start('//")
.append(parent.getAddress()).append("','/play/").append(idForWeb).append("','")
.append(name.replace("'", "\\'")).append("')\" title=\"")
.append(RemoteUtil.getMsgString("Web.1", t)).append("\"></a>");
} else {
sb.append("<a class=\"bumpIcon icondisabled\" href=\"javascript:notify('warn','")
.append(RemoteUtil.getMsgString("Web.2", t))
.append("')\" title=\"").append(RemoteUtil.getMsgString("Web.3", t)).append("\"></a>");
}
if (r.getParent() instanceof Playlist) {
sb.append("\n<a class=\"playlist_del\" href=\"#\" onclick=\"umsAjax('/playlist/del/")
.append(idForWeb).append("', true);return false;\" title=\"")
.append(RemoteUtil.getMsgString("Web.4", t)).append("\"></a>");
} else {
sb.append("\n<a class=\"playlist_add\" href=\"#\" onclick=\"umsAjax('/playlist/add/")
.append(idForWeb).append("', false);return false;\" title=\"")
.append(RemoteUtil.getMsgString("Web.5", t)).append("\"></a>");
}
} else {
// ensure that we got a string
sb.append("");
}
item.put("bump", sb.toString());
sb.setLength(0);
if (WebRender.supports(r) || r.isResume() || r.getType() == Format.IMAGE) {
sb.append("<a href=\"/play/").append(idForWeb)
.append("\" title=\"").append(name).append("\">")
.append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">")
.append("</a>");
item.put("thumb", sb.toString());
sb.setLength(0);
sb.append("<a href=\"/play/").append(idForWeb)
.append("\" title=\"").append(name).append("\">")
.append("<span class=\"caption\">").append(name).append("</span>")
.append("</a>");
item.put("caption", sb.toString());
} else if (upnpControl && upnpAllowed) {
// Include it as a web-disabled item so it can be thrown via upnp
sb.append("<a class=\"webdisabled\" href=\"javascript:notify('warn','")
.append(RemoteUtil.getMsgString("Web.6", t)).append("')\"")
.append(" title=\"").append(name).append(' ').append(RemoteUtil.getMsgString("Web.7", t)).append("\">")
.append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">")
.append("</a>");
item.put("thumb", sb.toString());
sb.setLength(0);
sb.append("<span class=\"webdisabled caption\">").append(name).append("</span>");
item.put("caption", sb.toString());
}
media.add(item);
hasFile = true;
}
}
HashMap<String, Object> vars = new HashMap<>();
vars.put("name", id.equals("0") ? configuration.getServerDisplayName() :
StringEscapeUtils.escapeHtml(root.getDLNAResource(id, null).getDisplayName()));
vars.put("hasFile", hasFile);
vars.put("folders", folders);
vars.put("media", media);
if (configuration.useWebControl()) {
vars.put("push", true);
}
return parent.getResources().getTemplate("browse.html").execute(vars);
}
@Override
public void handle(HttpExchange t) throws IOException {
if (RemoteUtil.deny(t)) {
throw new IOException("Access denied");
}
String id = RemoteUtil.getId("browse/", t);
LOGGER.debug("Got a browse request found id " + id);
String response = mkBrowsePage(id, t);
LOGGER.trace("Browse page " + response);
RemoteUtil.respond(t, response, 200, "text/html");
}
}