mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-04 15:15:15 -04:00
merged with latest upstream trunk
This commit is contained in:
commit
0c1e6301b3
295 changed files with 17456 additions and 12859 deletions
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include <util/rsdir.h>
|
||||
#include "util/ContentTypes.h"
|
||||
|
||||
// for filestreamer
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
@ -274,12 +275,21 @@ public:
|
|||
{
|
||||
sendMessage(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Error: rsFiles is null. Retroshare is probably not yet started.");
|
||||
return MHD_YES;
|
||||
}
|
||||
if(url[0] == 0 || (mHash=RsFileHash(url+strlen(FILESTREAMER_ENTRY_PATH))).isNull())
|
||||
{
|
||||
sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: URL is not a valid file hash");
|
||||
return MHD_YES;
|
||||
}
|
||||
}
|
||||
std::string urls(url);
|
||||
urls = urls.substr(strlen(FILESTREAMER_ENTRY_PATH));
|
||||
size_t perpos = urls.find('/');
|
||||
if(perpos == std::string::npos){
|
||||
mHash = RsFileHash(urls);
|
||||
}else{
|
||||
mHash = RsFileHash(urls.substr(0, perpos));
|
||||
}
|
||||
if(urls.empty() || mHash.isNull())
|
||||
{
|
||||
sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: URL is not a valid file hash");
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
std::list<RsFileHash> dls;
|
||||
rsFiles->FileDownloads(dls);
|
||||
|
@ -293,8 +303,13 @@ public:
|
|||
struct MHD_Response* resp = MHD_create_response_from_callback(
|
||||
mSize, 1024*1024, &contentReadercallback, this, NULL);
|
||||
|
||||
// only mp3 at the moment
|
||||
MHD_add_response_header(resp, "Content-Type", "audio/mpeg3");
|
||||
// get content-type from extension
|
||||
std::string ext = "";
|
||||
unsigned int i = info.fname.rfind('.');
|
||||
if(i != std::string::npos)
|
||||
ext = info.fname.substr(i+1);
|
||||
MHD_add_response_header(resp, "Content-Type", ContentTypes::cTypeFromExt(ext).c_str());
|
||||
|
||||
secure_queue_response(connection, MHD_HTTP_OK, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
|
@ -635,27 +650,10 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
|
|||
{
|
||||
extension = filename[i] + extension;
|
||||
i--;
|
||||
}
|
||||
const char* type = 0;
|
||||
if(extension == "html")
|
||||
type = "text/html";
|
||||
else if(extension == "css")
|
||||
type = "text/css";
|
||||
else if(extension == "js")
|
||||
type = "text/javascript";
|
||||
else if(extension == "jsx") // react.js jsx files
|
||||
type = "text/jsx";
|
||||
else if(extension == "png")
|
||||
type = "image/png";
|
||||
else if(extension == "jpg" || extension == "jpeg")
|
||||
type = "image/jpeg";
|
||||
else if(extension == "gif")
|
||||
type = "image/gif";
|
||||
else
|
||||
type = "application/octet-stream";
|
||||
};
|
||||
|
||||
struct MHD_Response* resp = MHD_create_response_from_fd(s.st_size, fd);
|
||||
MHD_add_response_header(resp, "Content-Type", type);
|
||||
MHD_add_response_header(resp, "Content-Type", ContentTypes::cTypeFromExt(extension).c_str());
|
||||
secure_queue_response(connection, MHD_HTTP_OK, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
|
|
|
@ -42,6 +42,12 @@ StreamBase& operator << (StreamBase& left, ChatHandler::Msg& m)
|
|||
|
||||
bool compare_lobby_id(const ChatHandler::Lobby& l1, const ChatHandler::Lobby& l2)
|
||||
{
|
||||
if(l1.auto_subscribe && !l2.auto_subscribe) return true;
|
||||
if(!l1.auto_subscribe && l2.auto_subscribe) return false;
|
||||
if(l1.is_private && !l2.is_private) return true;
|
||||
if(!l1.is_private && l2.is_private) return false;
|
||||
if(l1.subscribed && !l2.subscribed) return true;
|
||||
if(!l1.subscribed && l2.subscribed) return false;
|
||||
return l1.id < l2.id;
|
||||
}
|
||||
|
||||
|
@ -65,13 +71,17 @@ StreamBase& operator <<(StreamBase& left, KeyValueReference<ChatLobbyId> kv)
|
|||
|
||||
StreamBase& operator << (StreamBase& left, ChatHandler::Lobby& l)
|
||||
{
|
||||
ChatId chatId(l.id);
|
||||
if (l.is_broadcast)
|
||||
chatId = ChatId::makeBroadcastId();
|
||||
left << makeKeyValueReference("id", l.id)
|
||||
<< makeKeyValue("chat_id", ChatId(l.id).toStdString())
|
||||
<< makeKeyValue("chat_id", chatId.toStdString())
|
||||
<< makeKeyValueReference("name",l.name)
|
||||
<< makeKeyValueReference("topic", l.topic)
|
||||
<< makeKeyValueReference("subscribed", l.subscribed)
|
||||
<< makeKeyValueReference("auto_subscribe", l.auto_subscribe)
|
||||
<< makeKeyValueReference("is_private", l.is_private)
|
||||
<< makeKeyValueReference("is_broadcast", l.is_broadcast)
|
||||
<< makeKeyValueReference("gxs_id", l.gxs_id);
|
||||
return left;
|
||||
}
|
||||
|
@ -157,11 +167,26 @@ void ChatHandler::tick()
|
|||
l.subscribed = true;
|
||||
l.auto_subscribe = info.lobby_flags & RS_CHAT_LOBBY_FLAGS_AUTO_SUBSCRIBE;
|
||||
l.is_private = !(info.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC);
|
||||
l.is_broadcast = false;
|
||||
l.gxs_id = info.gxs_id;
|
||||
lobbies.push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
ChatId id = ChatId::makeBroadcastId();
|
||||
{
|
||||
Lobby l;
|
||||
l.id = id.toLobbyId();
|
||||
l.name = "BroadCast";
|
||||
l.topic = "Retroshare broadcast chat: messages are sent to all connected friends.";
|
||||
l.subscribed = true;
|
||||
l.auto_subscribe = false;
|
||||
l.is_private = false;
|
||||
l.is_broadcast = true;
|
||||
l.gxs_id = id.toGxsId();
|
||||
lobbies.push_back(l);
|
||||
}
|
||||
|
||||
std::vector<VisibleChatLobbyRecord> unsubscribed_lobbies;
|
||||
mRsMsgs->getListOfNearbyChatLobbies(unsubscribed_lobbies);
|
||||
for(std::vector<VisibleChatLobbyRecord>::iterator vit = unsubscribed_lobbies.begin(); vit != unsubscribed_lobbies.end(); ++vit)
|
||||
|
@ -176,6 +201,7 @@ void ChatHandler::tick()
|
|||
l.subscribed = false;
|
||||
l.auto_subscribe = info.lobby_flags & RS_CHAT_LOBBY_FLAGS_AUTO_SUBSCRIBE;
|
||||
l.is_private = !(info.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC);
|
||||
l.is_broadcast = false;
|
||||
l.gxs_id = RsGxsId();
|
||||
lobbies.push_back(l);
|
||||
}
|
||||
|
@ -294,62 +320,7 @@ void ChatHandler::tick()
|
|||
|
||||
// remove html tags from chat message
|
||||
// extract links form href
|
||||
const std::string& in = msg.msg;
|
||||
std::string out;
|
||||
bool ignore = false;
|
||||
bool keep_link = false;
|
||||
std::string last_six_chars;
|
||||
Triple current_link;
|
||||
std::vector<Triple> links;
|
||||
for(unsigned int i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if(keep_link && in[i] == '"')
|
||||
{
|
||||
keep_link = false;
|
||||
current_link.second = out.size();
|
||||
}
|
||||
if(last_six_chars == "href=\"")
|
||||
{
|
||||
keep_link = true;
|
||||
current_link.first = out.size();
|
||||
}
|
||||
|
||||
// "rising edge" sets mode to ignore
|
||||
if(in[i] == '<')
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
if(!ignore || keep_link)
|
||||
out += in[i];
|
||||
// "falling edge" resets mode to keep
|
||||
if(in[i] == '>')
|
||||
ignore = false;
|
||||
|
||||
last_six_chars += in[i];
|
||||
if(last_six_chars.size() > 6)
|
||||
last_six_chars = last_six_chars.substr(1);
|
||||
std::string a = "</a>";
|
||||
if( current_link.first != -1
|
||||
&& last_six_chars.size() >= a.size()
|
||||
&& last_six_chars.substr(last_six_chars.size()-a.size()) == a)
|
||||
{
|
||||
// only allow these protocols
|
||||
// we don't want for example javascript:alert(0)
|
||||
std::string http = "http://";
|
||||
std::string https = "https://";
|
||||
std::string retroshare = "retroshare://";
|
||||
if( out.substr(current_link.first, http.size()) == http
|
||||
|| out.substr(current_link.first, https.size()) == https
|
||||
|| out.substr(current_link.first, retroshare.size()) == retroshare)
|
||||
{
|
||||
current_link.third = out.size();
|
||||
links.push_back(current_link);
|
||||
}
|
||||
current_link = Triple();
|
||||
}
|
||||
}
|
||||
m.msg = out;
|
||||
m.links = links;
|
||||
getPlainText(msg.msg, m.msg, m.links);
|
||||
m.recv_time = msg.recvTime;
|
||||
m.send_time = msg.sendTime;
|
||||
|
||||
|
@ -390,7 +361,77 @@ void ChatHandler::tick()
|
|||
}
|
||||
}
|
||||
|
||||
void ChatHandler::handleWildcard(Request &req, Response &resp)
|
||||
void ChatHandler::getPlainText(const std::string& in, std::string &out, std::vector<Triple> &links)
|
||||
{
|
||||
if (in.size() == 0)
|
||||
return;
|
||||
|
||||
if (in[0] != '<' || in[in.size() - 1] != '>')
|
||||
{
|
||||
// It's a plain text message without HTML
|
||||
out = in;
|
||||
return;
|
||||
}
|
||||
bool ignore = false;
|
||||
|
||||
bool keep_link = false;
|
||||
std::string last_six_chars;
|
||||
unsigned int tag_start_index = 0;
|
||||
Triple current_link;
|
||||
for(unsigned int i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if(keep_link && in[i] == '"')
|
||||
{
|
||||
keep_link = false;
|
||||
current_link.second = out.size();
|
||||
}
|
||||
if(last_six_chars == "href=\"")
|
||||
{
|
||||
keep_link = true;
|
||||
current_link.first = out.size();
|
||||
}
|
||||
|
||||
// "rising edge" sets mode to ignore
|
||||
if(in[i] == '<')
|
||||
{
|
||||
tag_start_index = i;
|
||||
ignore = true;
|
||||
}
|
||||
if(!ignore || keep_link)
|
||||
out += in[i];
|
||||
// "falling edge" resets mode to keep
|
||||
if(in[i] == '>') {
|
||||
// leave ignore mode on, if it's a style tag
|
||||
if (tag_start_index == 0 || tag_start_index + 6 > i || in.substr(tag_start_index, 6) != "<style")
|
||||
ignore = false;
|
||||
}
|
||||
|
||||
last_six_chars += in[i];
|
||||
if(last_six_chars.size() > 6)
|
||||
last_six_chars = last_six_chars.substr(1);
|
||||
std::string a = "</a>";
|
||||
if( current_link.first != -1
|
||||
&& last_six_chars.size() >= a.size()
|
||||
&& last_six_chars.substr(last_six_chars.size()-a.size()) == a)
|
||||
{
|
||||
// only allow these protocols
|
||||
// we don't want for example javascript:alert(0)
|
||||
std::string http = "http://";
|
||||
std::string https = "https://";
|
||||
std::string retroshare = "retroshare://";
|
||||
if( out.substr(current_link.first, http.size()) == http
|
||||
|| out.substr(current_link.first, https.size()) == https
|
||||
|| out.substr(current_link.first, retroshare.size()) == retroshare)
|
||||
{
|
||||
current_link.third = out.size();
|
||||
links.push_back(current_link);
|
||||
}
|
||||
current_link = Triple();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatHandler::handleWildcard(Request &/*req*/, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
resp.mDataStream.getStreamToMember();
|
||||
|
@ -447,7 +488,7 @@ void ChatHandler::handleSubscribeLobby(Request &req, Response &resp)
|
|||
resp.setFail("lobby join failed. (See console for more info)");
|
||||
}
|
||||
|
||||
void ChatHandler::handleUnsubscribeLobby(Request &req, Response &resp)
|
||||
void ChatHandler::handleUnsubscribeLobby(Request &req, Response &/*resp*/)
|
||||
{
|
||||
ChatLobbyId id = 0;
|
||||
req.mStream << makeKeyValueReference("id", id);
|
||||
|
@ -626,17 +667,17 @@ void ChatHandler::handleInfo(Request &req, Response &resp)
|
|||
resp.setOk();
|
||||
}
|
||||
|
||||
void ChatHandler::handleTypingLabel(Request &req, Response &resp)
|
||||
void ChatHandler::handleTypingLabel(Request &/*req*/, Response &/*resp*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ChatHandler::handleSendStatus(Request &req, Response &resp)
|
||||
void ChatHandler::handleSendStatus(Request &/*req*/, Response &/*resp*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ChatHandler::handleUnreadMsgs(Request &req, Response &resp)
|
||||
void ChatHandler::handleUnreadMsgs(Request &/*req*/, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
|
||||
|
|
|
@ -57,13 +57,14 @@ public:
|
|||
|
||||
class Lobby{
|
||||
public:
|
||||
Lobby(): id(0), subscribed(false), auto_subscribe(false), is_private(false){}
|
||||
Lobby(): id(0), subscribed(false), auto_subscribe(false), is_private(false), is_broadcast(false){}
|
||||
ChatLobbyId id;
|
||||
std::string name;
|
||||
std::string topic;
|
||||
bool subscribed;
|
||||
bool auto_subscribe;
|
||||
bool is_private;
|
||||
bool is_broadcast;
|
||||
|
||||
RsGxsId gxs_id;// for subscribed lobbies: the id we use to write messages
|
||||
|
||||
|
@ -75,6 +76,7 @@ public:
|
|||
&& subscribed == l.subscribed
|
||||
&& auto_subscribe == l.auto_subscribe
|
||||
&& is_private == l.is_private
|
||||
&& is_broadcast == l.is_broadcast
|
||||
&& gxs_id == l.gxs_id;
|
||||
}
|
||||
};
|
||||
|
@ -102,6 +104,8 @@ private:
|
|||
void handleSendStatus(Request& req, Response& resp);
|
||||
void handleUnreadMsgs(Request& req, Response& resp);
|
||||
|
||||
void getPlainText(const std::string& in, std::string &out, std::vector<Triple> &links);
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsNotify* mNotify;
|
||||
RsMsgs* mRsMsgs;
|
||||
|
|
|
@ -14,9 +14,9 @@ StreamBase& operator <<(StreamBase& left, KeyValueReference<uint32_t> ref)
|
|||
{
|
||||
digit = num % 10;
|
||||
num = num / 10;
|
||||
str += (char)(digit + '0');
|
||||
str = (char)(digit + '0') + str;
|
||||
}
|
||||
str += (char)(num + '0');
|
||||
str = (char)(num + '0') + str;
|
||||
left << makeKeyValueReference(ref.key, str);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -408,7 +408,7 @@ void RsControlModule::handleCreateLocation(Request &req, Response &resp)
|
|||
std::string err_string;
|
||||
if(!RsAccounts::GeneratePGPCertificate(pgp_name, "", pgp_password, pgp_id, 2048, err_string))
|
||||
{
|
||||
resp.setFail("could not cerate pgp key: "+err_string);
|
||||
resp.setFail("could not create pgp key: "+err_string);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib
|
||||
CONFIG += create_prl
|
||||
CONFIG -= qt
|
||||
TARGET = resapi
|
||||
TARGET_PRL = libresapi
|
||||
DESTDIR = lib
|
||||
|
||||
CONFIG += libmicrohttpd
|
||||
|
@ -22,10 +24,21 @@ unix {
|
|||
|
||||
win32{
|
||||
DEFINES *= WINDOWS_SYS
|
||||
INCLUDEPATH += $$PWD/../../../libs/include
|
||||
INCLUDEPATH += . $$INC_DIR
|
||||
}
|
||||
|
||||
libmicrohttpd{
|
||||
linux {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG *= libmicrohttpd
|
||||
} else {
|
||||
mac {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
LIBS *= /usr/local/lib/libmicrohttpd.a
|
||||
} else {
|
||||
LIBS *= -lmicrohttpd
|
||||
}
|
||||
}
|
||||
SOURCES += \
|
||||
api/ApiServerMHD.cpp
|
||||
|
||||
|
@ -51,7 +64,8 @@ SOURCES += \
|
|||
api/GetPluginInterfaces.cpp \
|
||||
api/ChatHandler.cpp \
|
||||
api/LivereloadHandler.cpp \
|
||||
api/TmpBlobStore.cpp
|
||||
api/TmpBlobStore.cpp \
|
||||
util/ContentTypes.cpp
|
||||
|
||||
HEADERS += \
|
||||
api/ApiServer.h \
|
||||
|
@ -74,4 +88,5 @@ HEADERS += \
|
|||
api/GetPluginInterfaces.h \
|
||||
api/ChatHandler.h \
|
||||
api/LivereloadHandler.h \
|
||||
api/TmpBlobStore.h
|
||||
api/TmpBlobStore.h \
|
||||
util/ContentTypes.h
|
||||
|
|
88
libresapi/src/util/ContentTypes.cpp
Normal file
88
libresapi/src/util/ContentTypes.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "ContentTypes.h"
|
||||
#include <fstream>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
RsMutex ContentTypes::ctmtx = RsMutex("CTMTX");
|
||||
std::map<std::string, std::string> ContentTypes::cache;
|
||||
bool ContentTypes::inited = false;
|
||||
|
||||
#ifdef WINDOWS_SYS
|
||||
//Next to the executable
|
||||
const char* ContentTypes::filename = ".\\mime.types";
|
||||
#else
|
||||
const char* ContentTypes::filename = "/etc/mime.types";
|
||||
#endif
|
||||
|
||||
std::string ContentTypes::cTypeFromExt(const std::string &extension)
|
||||
{
|
||||
if(extension.empty())
|
||||
return DEFAULTCT;
|
||||
|
||||
RsStackMutex mtx(ctmtx);
|
||||
|
||||
if(!inited)
|
||||
addBasic();
|
||||
|
||||
std::string extension2(extension); //lower case
|
||||
std::transform(extension2.begin(), extension2.end(), extension2.begin(),::tolower);
|
||||
|
||||
//looking into the cache
|
||||
std::map<std::string,std::string>::iterator it;
|
||||
it = cache.find(extension2);
|
||||
if (it != cache.end())
|
||||
{
|
||||
std::cout << "Mime " + it->second + " for extension ." + extension2 + " was found in cache" << std::endl;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//looking into mime.types
|
||||
std::string line;
|
||||
std::string ext;
|
||||
std::ifstream file(filename);
|
||||
while(getline(file, line))
|
||||
{
|
||||
if(line.empty() || line[0] == '#') continue;
|
||||
size_t i = line.find_first_of("\t ");
|
||||
size_t j;
|
||||
while(i != std::string::npos) //tokenize
|
||||
{
|
||||
j = i;
|
||||
i = line.find_first_of("\t ", i+1);
|
||||
if(i == std::string::npos)
|
||||
ext = line.substr(j+1);
|
||||
else
|
||||
ext = line.substr(j+1, i-j-1);
|
||||
|
||||
if(extension2 == ext)
|
||||
{
|
||||
std::string mime = line.substr(0, line.find_first_of("\t "));
|
||||
cache[extension2] = mime;
|
||||
std::cout << "Mime " + mime + " for extension ." + extension2 + " was found in mime.types" << std::endl;
|
||||
return mime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//nothing found
|
||||
std::cout << "Mime for " + extension2 + " was not found in " + filename + " falling back to " << DEFAULTCT << std::endl;
|
||||
cache[extension2] = DEFAULTCT;
|
||||
return DEFAULTCT;
|
||||
}
|
||||
|
||||
//Add some basic content-types before first use.
|
||||
//It keeps webui usable in the case of mime.types file not exists.
|
||||
void ContentTypes::addBasic()
|
||||
{
|
||||
inited = true;
|
||||
|
||||
cache["html"] = "text/html";
|
||||
cache["css"] = "text/css";
|
||||
cache["js"] = "text/javascript";
|
||||
cache["jsx"] = "text/jsx";
|
||||
cache["png"] = "image/png";
|
||||
cache["jpg"] = "image/jpeg";
|
||||
cache["jpeg"] = "image/jpeg";
|
||||
cache["gif"] = "image/gif";
|
||||
}
|
||||
|
23
libresapi/src/util/ContentTypes.h
Normal file
23
libresapi/src/util/ContentTypes.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef CONTENTTYPES_H
|
||||
#define CONTENTTYPES_H
|
||||
|
||||
#include <util/rsthreads.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#define DEFAULTCT "application/octet-stream"
|
||||
|
||||
class ContentTypes
|
||||
{
|
||||
public:
|
||||
static std::string cTypeFromExt(const std::string& extension);
|
||||
|
||||
private:
|
||||
static std::map<std::string, std::string> cache;
|
||||
static RsMutex ctmtx;
|
||||
static const char* filename;
|
||||
static bool inited;
|
||||
static void addBasic();
|
||||
};
|
||||
|
||||
#endif // CONTENTTYPES_H
|
|
@ -66,6 +66,10 @@ td{
|
|||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
|
|
|
@ -4,7 +4,15 @@ RS.start();
|
|||
|
||||
var api_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/api/v2/";
|
||||
var filestreamer_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/fstream/";
|
||||
var upload_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/upload/";
|
||||
var upload_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/upload/";
|
||||
|
||||
var extensions = {
|
||||
mp3: "audio",
|
||||
ogg: "audio",
|
||||
wav: "audio",
|
||||
webm: "video",
|
||||
mp4: "video"
|
||||
};
|
||||
|
||||
// livereload
|
||||
function start_live_reload()
|
||||
|
@ -558,7 +566,8 @@ var DownloadsWidget = React.createClass({
|
|||
widget.emit("play_file", {name: file.name, hash: file.hash})
|
||||
};
|
||||
var playBtn = <div></div>;
|
||||
if(file.name.slice(-3) === "mp3")
|
||||
var splits = file.name.split(".");
|
||||
if(splits[splits.length-1].toLowerCase() in extensions)
|
||||
playBtn = <div className="btn" onClick={playFn}>play</div>;
|
||||
|
||||
var ctrlBtn = <div></div>;
|
||||
|
@ -571,7 +580,7 @@ var DownloadsWidget = React.createClass({
|
|||
ctrlBtn = <div className="btn" onClick={pauseFn}>pause</div>;
|
||||
}
|
||||
return(<tr>
|
||||
<td>{this.props.data.name}</td>
|
||||
<td><a className="filelink" target="_blank" href={filestreamer_url + this.props.data.hash + "/" + encodeURIComponent(this.props.data.name)}>{this.props.data.name}</a></td>
|
||||
<td>{makeFriendlyUnit(this.props.data.size)}</td>
|
||||
<td><ProgressBar progress={this.props.data.transfered / this.props.data.size}/></td>
|
||||
<td>{makeFriendlyUnit(this.props.data.transfer_rate*1e3)}/s</td>
|
||||
|
@ -673,7 +682,7 @@ var SearchWidget = React.createClass({
|
|||
},
|
||||
});
|
||||
|
||||
var AudioPlayerWidget = React.createClass({
|
||||
var MediaPlayerWidget = React.createClass({
|
||||
mixins: [SignalSlotMixin],
|
||||
getInitialState: function(){
|
||||
return {file: undefined};
|
||||
|
@ -691,13 +700,28 @@ var AudioPlayerWidget = React.createClass({
|
|||
}
|
||||
else
|
||||
{
|
||||
return(
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<audio controls src={filestreamer_url+this.state.file.hash} type="audio/mpeg">
|
||||
</audio>
|
||||
</div>
|
||||
);
|
||||
var splits = this.state.file.name.split(".");
|
||||
var mediatype = extensions[splits[splits.length - 1].toLowerCase()];
|
||||
if (mediatype == "audio") {
|
||||
return (
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<audio controls autoPlay src={filestreamer_url+this.state.file.hash}>
|
||||
</audio>
|
||||
</div>
|
||||
);
|
||||
} else if (mediatype == "video") {
|
||||
return(
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<video height="300" controls autoPlay src={filestreamer_url+this.state.file.hash}>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return(<div></div>);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -1467,7 +1491,7 @@ var ChatWidget = React.createClass({
|
|||
<ChatInfoWidget id={this.props.id}/>
|
||||
{
|
||||
this.state.data.map(function(msg){
|
||||
return <div key={msg.id}>{msg.send_time} {msg.author_name}: {markup(msg.msg, msg.links)}</div>;
|
||||
return <div key={msg.id}>{new Date(msg.send_time*1000).toLocaleTimeString()} {msg.author_name}: {markup(msg.msg, msg.links)}</div>;
|
||||
})
|
||||
}
|
||||
<input type="text" ref="input" onKeyDown={
|
||||
|
@ -1702,7 +1726,7 @@ var MainWidget = React.createClass({
|
|||
}
|
||||
mainpage = <div>
|
||||
<UnreadChatMsgsCountWidget/>
|
||||
<AudioPlayerWidget/>
|
||||
<MediaPlayerWidget/>
|
||||
<IdentitySelectorWidget/>
|
||||
{mainpage}
|
||||
</div>;
|
||||
|
|
|
@ -66,6 +66,10 @@ td{
|
|||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
|
|
|
@ -4,7 +4,15 @@ RS.start();
|
|||
|
||||
var api_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/api/v2/";
|
||||
var filestreamer_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/fstream/";
|
||||
var upload_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/upload/";
|
||||
var upload_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/upload/";
|
||||
|
||||
var extensions = {
|
||||
mp3: "audio",
|
||||
ogg: "audio",
|
||||
wav: "audio",
|
||||
webm: "video",
|
||||
mp4: "video"
|
||||
};
|
||||
|
||||
// livereload
|
||||
function start_live_reload()
|
||||
|
@ -558,7 +566,8 @@ var DownloadsWidget = React.createClass({
|
|||
widget.emit("play_file", {name: file.name, hash: file.hash})
|
||||
};
|
||||
var playBtn = <div></div>;
|
||||
if(file.name.slice(-3) === "mp3")
|
||||
var splits = file.name.split(".");
|
||||
if(splits[splits.length-1].toLowerCase() in extensions)
|
||||
playBtn = <div className="btn" onClick={playFn}>play</div>;
|
||||
|
||||
var ctrlBtn = <div></div>;
|
||||
|
@ -571,7 +580,7 @@ var DownloadsWidget = React.createClass({
|
|||
ctrlBtn = <div className="btn" onClick={pauseFn}>pause</div>;
|
||||
}
|
||||
return(<tr>
|
||||
<td>{this.props.data.name}</td>
|
||||
<td><a className="filelink" target="_blank" href={filestreamer_url + this.props.data.hash + "/" + encodeURIComponent(this.props.data.name)}>{this.props.data.name}</a></td>
|
||||
<td>{makeFriendlyUnit(this.props.data.size)}</td>
|
||||
<td><ProgressBar progress={this.props.data.transfered / this.props.data.size}/></td>
|
||||
<td>{makeFriendlyUnit(this.props.data.transfer_rate*1e3)}/s</td>
|
||||
|
@ -673,7 +682,7 @@ var SearchWidget = React.createClass({
|
|||
},
|
||||
});
|
||||
|
||||
var AudioPlayerWidget = React.createClass({
|
||||
var MediaPlayerWidget = React.createClass({
|
||||
mixins: [SignalSlotMixin],
|
||||
getInitialState: function(){
|
||||
return {file: undefined};
|
||||
|
@ -691,13 +700,28 @@ var AudioPlayerWidget = React.createClass({
|
|||
}
|
||||
else
|
||||
{
|
||||
return(
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<audio controls src={filestreamer_url+this.state.file.hash} type="audio/mpeg">
|
||||
</audio>
|
||||
</div>
|
||||
);
|
||||
var splits = this.state.file.name.split(".");
|
||||
var mediatype = extensions[splits[splits.length - 1].toLowerCase()];
|
||||
if (mediatype == "audio") {
|
||||
return (
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<audio controls autoPlay src={filestreamer_url+this.state.file.hash}>
|
||||
</audio>
|
||||
</div>
|
||||
);
|
||||
} else if (mediatype == "video") {
|
||||
return(
|
||||
<div>
|
||||
<p>{this.state.file.name}</p>
|
||||
<video height="300" controls autoPlay src={filestreamer_url+this.state.file.hash}>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return(<div></div>);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -1467,7 +1491,7 @@ var ChatWidget = React.createClass({
|
|||
<ChatInfoWidget id={this.props.id}/>
|
||||
{
|
||||
this.state.data.map(function(msg){
|
||||
return <div key={msg.id}>{msg.send_time} {msg.author_name}: {markup(msg.msg, msg.links)}</div>;
|
||||
return <div key={msg.id}>{new Date(msg.send_time*1000).toLocaleTimeString()} {msg.author_name}: {markup(msg.msg, msg.links)}</div>;
|
||||
})
|
||||
}
|
||||
<input type="text" ref="input" onKeyDown={
|
||||
|
@ -1702,7 +1726,7 @@ var MainWidget = React.createClass({
|
|||
}
|
||||
mainpage = <div>
|
||||
<UnreadChatMsgsCountWidget/>
|
||||
<AudioPlayerWidget/>
|
||||
<MediaPlayerWidget/>
|
||||
<IdentitySelectorWidget/>
|
||||
{mainpage}
|
||||
</div>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue