Merge branch 'development' into LTS_development

# Conflicts:
#	src/input/input.cpp
This commit is contained in:
Thulinma 2016-09-22 09:50:09 +02:00
commit 44fd455c8e
9 changed files with 341 additions and 111 deletions

View file

@ -4,6 +4,82 @@
#include "http_parser.h" #include "http_parser.h"
#include "encode.h" #include "encode.h"
#include "timing.h" #include "timing.h"
#include "defines.h"
/// Helper function to check if the given c-string is numeric or not
static bool is_numeric(const char * str){
while (str != 0){
if (str[0] < 48 || str[0] > 57){return false;}
++str;
}
return true;
}
///Constructor that does the actual parsing
HTTP::URL::URL(const std::string & url){
//first detect protocol at the start, if any
size_t proto_sep = url.find("://");
if (proto_sep != std::string::npos){
protocol = url.substr(0, proto_sep);
proto_sep += 3;
}else{
proto_sep = 0;
}
//proto_sep now points to the start of the host, guaranteed
//continue by finding the path, if any
size_t first_slash = url.find('/', proto_sep);
if (first_slash != std::string::npos){
path = url.substr(first_slash+1);
}
//host and port are now definitely between proto_sep and first_slash
//we check for [ at the start because we may have an IPv6 address as host
if (url[proto_sep] == '['){
//IPv6 address - find matching brace
size_t closing_brace = url.find(']', proto_sep);
//check if it exists at all
if (closing_brace == std::string::npos || closing_brace > first_slash){
//assume host ends at first slash if there is no closing brace before it
closing_brace = first_slash;
}
host = url.substr(proto_sep+1, closing_brace-(proto_sep+1));
//continue by finding port, if any
size_t colon = url.rfind(':', first_slash);
if (colon == std::string::npos || colon <= closing_brace){
//no port. Assume 80
port = "80";
}else{
//we have a port number, read it
port = url.substr(colon+1, first_slash-(colon+1));
}
}else{
//"normal" host - first find port, if any
size_t colon = url.rfind(':', first_slash);
if (colon == std::string::npos || colon < proto_sep){
//no port. Assume 80
port = "80";
host = url.substr(proto_sep, first_slash-proto_sep);
}else{
//we have a port number, read it
port = url.substr(colon+1, first_slash-(colon+1));
host = url.substr(proto_sep, colon-proto_sep);
}
}
//if the host is numeric, assume it is a port, instead
if (is_numeric(host.c_str())){
port = host;
host = "";
}
EXTREME_MSG("URL host: %s", host.c_str());
EXTREME_MSG("URL protocol: %s", protocol.c_str());
EXTREME_MSG("URL port: %s", port.c_str());
EXTREME_MSG("URL path: %s", path.c_str());
}
///Returns the port in numeric format
uint32_t HTTP::URL::getPort() const{
if (!port.size()){return 80;}
return atoi(port.c_str());
}
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing. /// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
/// All this constructor does is call HTTP::Parser::Clean(). /// All this constructor does is call HTTP::Parser::Clean().
@ -447,7 +523,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer) {
tmpA.erase(0, f + 1); tmpA.erase(0, f + 1);
method = tmpA; method = tmpA;
if (url.find('?') != std::string::npos) { if (url.find('?') != std::string::npos) {
parseVars(url.substr(url.find('?') + 1)); //parse GET variables parseVars(url.substr(url.find('?') + 1), vars); //parse GET variables
url.erase(url.find('?')); url.erase(url.find('?'));
} }
url = Encodings::URL::decode(url); url = Encodings::URL::decode(url);
@ -463,7 +539,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer) {
tmpA.erase(0, f + 1); tmpA.erase(0, f + 1);
protocol = tmpA; protocol = tmpA;
if (url.find('?') != std::string::npos) { if (url.find('?') != std::string::npos) {
parseVars(url.substr(url.find('?') + 1)); //parse GET variables parseVars(url.substr(url.find('?') + 1), vars); //parse GET variables
url.erase(url.find('?')); url.erase(url.find('?'));
} }
url = Encodings::URL::decode(url); url = Encodings::URL::decode(url);
@ -508,7 +584,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer) {
HTTPbuffer.erase(0, toappend); HTTPbuffer.erase(0, toappend);
} }
if (length == body.length()) { if (length == body.length()) {
parseVars(body); //parse POST variables parseVars(body, vars); //parse POST variables
return true; return true;
} else { } else {
return false; return false;
@ -560,12 +636,12 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer) {
return false; //empty input return false; //empty input
} //HTTPReader::parse } //HTTPReader::parse
/// Parses GET or POST-style variable data. ///HTTP variable parser to std::map<std::string, std::string> structure.
/// Saves to internal variable structure using HTTP::Parser::SetVar. ///Reads variables from data, decodes and stores them to storage.
void HTTP::Parser::parseVars(std::string data) { void HTTP::parseVars(const std::string & data, std::map<std::string, std::string> & storage) {
std::string varname; std::string varname;
std::string varval; std::string varval;
// position where a part start (e.g. after &) // position where a part starts (e.g. after &)
size_t pos = 0; size_t pos = 0;
while (pos < data.length()) { while (pos < data.length()) {
size_t nextpos = data.find('&', pos); size_t nextpos = data.find('&', pos);
@ -582,7 +658,9 @@ void HTTP::Parser::parseVars(std::string data) {
varname = data.substr(pos, nextpos - pos); varname = data.substr(pos, nextpos - pos);
varval.clear(); varval.clear();
} }
SetVar(Encodings::URL::decode(varname), Encodings::URL::decode(varval)); if (varname.size()){
storage[Encodings::URL::decode(varname)] = Encodings::URL::decode(varval);
}
if (nextpos == std::string::npos) { if (nextpos == std::string::npos) {
// in case the string is gigantic // in case the string is gigantic
break; break;

View file

@ -10,6 +10,12 @@
/// Holds all HTTP processing related code. /// Holds all HTTP processing related code.
namespace HTTP { namespace HTTP {
///HTTP variable parser to std::map<std::string, std::string> structure.
///Reads variables from data, decodes and stores them to storage.
void parseVars(const std::string & data, std::map<std::string, std::string> & storage);
/// Simple class for reading and writing HTTP 1.0 and 1.1. /// Simple class for reading and writing HTTP 1.0 and 1.1.
class Parser { class Parser {
public: public:
@ -56,13 +62,22 @@ namespace HTTP {
bool getChunks; bool getChunks;
unsigned int doingChunk; unsigned int doingChunk;
bool parse(std::string & HTTPbuffer); bool parse(std::string & HTTPbuffer);
void parseVars(std::string data);
std::string builder; std::string builder;
std::string read_buffer; std::string read_buffer;
std::map<std::string, std::string> headers; std::map<std::string, std::string> headers;
std::map<std::string, std::string> vars; std::map<std::string, std::string> vars;
void Trim(std::string & s); void Trim(std::string & s);
}; };
//HTTP::Parser class
///URL parsing class. Parses full URL into its subcomponents
class URL {
public:
URL(const std::string & url);
uint32_t getPort() const;
std::string host;///< Hostname or IP address of URL
std::string protocol;///<Protocol of URL
std::string port;///<Port of URL
std::string path;///<Path after the first slash, not inclusive
};
}//HTTP namespace }//HTTP namespace

View file

@ -721,6 +721,11 @@ namespace IPC {
return data[172]; return data[172];
} }
///\brief Gets PID field
uint32_t statExchange::getPID() {
return *(uint32_t*)(data+173);
}
///\brief Creates a semaphore guard, locks the semaphore on call ///\brief Creates a semaphore guard, locks the semaphore on call
semGuard::semGuard(semaphore * thisSemaphore) : mySemaphore(thisSemaphore) { semGuard::semGuard(semaphore * thisSemaphore) : mySemaphore(thisSemaphore) {
mySemaphore->wait(); mySemaphore->wait();

View file

@ -40,6 +40,7 @@ namespace IPC {
char getSync(); char getSync();
void setSync(char s); void setSync(char s);
unsigned int crc(); unsigned int crc();
uint32_t getPID();
private: private:
///\brief The payload for the stat exchange ///\brief The payload for the stat exchange
/// - 8 byte - now (timestamp of last statistics) /// - 8 byte - now (timestamp of last statistics)

View file

@ -377,10 +377,6 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock) {
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result);
if (s != 0) { if (s != 0) {
DEBUG_MSG(DLVL_FAIL, "Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s)); DEBUG_MSG(DLVL_FAIL, "Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s));
@ -975,14 +971,14 @@ int Socket::Server::getSocket() {
Socket::UDPConnection::UDPConnection(bool nonblock) { Socket::UDPConnection::UDPConnection(bool nonblock) {
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#warning UDP over IPv6 is currently disabled on windows #warning UDP over IPv6 is currently disabled on windows
isIPv6 = false; family = AF_INET;
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
#else #else
isIPv6 = true; family = AF_INET6;
sock = socket(AF_INET6, SOCK_DGRAM, 0); sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock == -1) { if (sock == -1) {
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
isIPv6 = false; family = AF_INET;
} }
#endif #endif
if (sock == -1) { if (sock == -1) {
@ -1005,14 +1001,14 @@ Socket::UDPConnection::UDPConnection(bool nonblock) {
Socket::UDPConnection::UDPConnection(const UDPConnection & o) { Socket::UDPConnection::UDPConnection(const UDPConnection & o) {
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#warning UDP over IPv6 is currently disabled on windows #warning UDP over IPv6 is currently disabled on windows
isIPv6 = false; family = AF_INET;
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
#else #else
isIPv6 = true; family = AF_INET6;
sock = socket(AF_INET6, SOCK_DGRAM, 0); sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock == -1) { if (sock == -1) {
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
isIPv6 = false; family = AF_INET;
} }
#endif #endif
if (sock == -1) { if (sock == -1) {
@ -1038,14 +1034,19 @@ Socket::UDPConnection::UDPConnection(const UDPConnection & o) {
data_len = 0; data_len = 0;
} }
/// Closes the UDP socket, cleans up any memory allocated by the socket. /// Close the UDP socket
Socket::UDPConnection::~UDPConnection() { void Socket::UDPConnection::close(){
if (sock != -1) { if (sock != -1) {
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR) { while (::close(sock) != 0 && errno == EINTR) {
} }
sock = -1; sock = -1;
} }
}
/// Closes the UDP socket, cleans up any memory allocated by the socket.
Socket::UDPConnection::~UDPConnection() {
close();
if (destAddr) { if (destAddr) {
free(destAddr); free(destAddr);
destAddr = 0; destAddr = 0;
@ -1064,21 +1065,38 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port) {
destAddr = 0; destAddr = 0;
} }
destAddr = malloc(sizeof(struct sockaddr_in6)); destAddr = malloc(sizeof(struct sockaddr_in6));
if (destAddr) { if (!destAddr) {
destAddr_size = sizeof(struct sockaddr_in6); return;
memset(destAddr, 0, destAddr_size);
((struct sockaddr_in6 *)destAddr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)destAddr)->sin6_port = htons(port);
if (inet_pton(AF_INET6, destIp.c_str(), &(((struct sockaddr_in6 *)destAddr)->sin6_addr)) == 1) {
return;
}
memset(destAddr, 0, destAddr_size);
((struct sockaddr_in *)destAddr)->sin_family = AF_INET;
((struct sockaddr_in *)destAddr)->sin_port = htons(port);
if (inet_pton(AF_INET, destIp.c_str(), &(((struct sockaddr_in *)destAddr)->sin_addr)) == 1) {
return;
}
} }
destAddr_size = sizeof(struct sockaddr_in6);
memset(destAddr, 0, destAddr_size);
struct addrinfo * result, *rp, hints;
std::stringstream ss;
ss << port;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
int s = getaddrinfo(destIp.c_str(), ss.str().c_str(), &hints, &result);
if (s != 0) {
DEBUG_MSG(DLVL_FAIL, "Could not connect UDP socket to %s:%i! Error: %s", destIp.c_str(), port, gai_strerror(s));
return;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
//assume success
memcpy(destAddr, rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(result);
return;
//\todo Possibly detect and handle failure
}
freeaddrinfo(result);
free(destAddr); free(destAddr);
destAddr = 0; destAddr = 0;
DEBUG_MSG(DLVL_FAIL, "Could not set destination for UDP socket: %s:%d", destIp.c_str(), port); DEBUG_MSG(DLVL_FAIL, "Could not set destination for UDP socket: %s:%d", destIp.c_str(), port);
@ -1164,64 +1182,163 @@ void Socket::UDPConnection::SendNow(const char * sdata, size_t len) {
} }
/// Bind to a port number, returning the bound port. /// Bind to a port number, returning the bound port.
/// Attempts to bind over IPv6 first. /// If that fails, returns zero.
/// If it fails, attempts to bind over IPv4. /// \arg port Port to bind to, required.
/// If that fails too, gives up and returns zero. /// \arg iface Interface address to listen for packets on (may be multicast address)
/// Prints a debug message at DLVL_FAIL level if binding failed. /// \arg multicastInterfaces Comma-separated list of interfaces to listen on for multicast packets. Optional, left out means automatically chosen by kernel.
/// \return Actually bound port number, or zero on error. /// \return Actually bound port number, or zero on error.
int Socket::UDPConnection::bind(int port, std::string iface, const std::string & multicastInterfaces) { int Socket::UDPConnection::bind(int port, std::string iface, const std::string & multicastInterfaces) {
close();//we open a new socket for each attempt
int result = 0; int result = 0;
if (isIPv6) { int addr_ret;
struct sockaddr_in6 s6; bool multicast = false;
memset(&s6, 0, sizeof(s6)); struct addrinfo hints, *addr_result, *rp;
s6.sin6_family = AF_INET6; memset(&hints, 0, sizeof(hints));
if (iface == "0.0.0.0" || iface.length() == 0) { hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE;
s6.sin6_addr = in6addr_any; hints.ai_family = AF_UNSPEC;
} else { hints.ai_socktype = SOCK_DGRAM;
inet_pton(AF_INET6, iface.c_str(), &s6.sin6_addr); hints.ai_protocol = IPPROTO_UDP;
std::stringstream ss;
ss << port;
if (iface == "0.0.0.0" || iface.length() == 0) {
if ((addr_ret = getaddrinfo(0, ss.str().c_str(), &hints, &addr_result)) != 0){
FAIL_MSG("Could not resolve %s for UDP: %s", iface.c_str(), gai_strerror(addr_ret));
return 0;
} }
s6.sin6_port = htons(port); }else{
int r = ::bind(sock, (sockaddr *)&s6, sizeof(s6)); if ((addr_ret = getaddrinfo(iface.c_str(), ss.str().c_str(), &hints, &addr_result)) != 0){
if (r == 0) { FAIL_MSG("Could not resolve %s for UDP: %s", iface.c_str(), gai_strerror(addr_ret));
result = ntohs(s6.sin6_port); return 0;
}
} else {
struct sockaddr_in s4;
memset(&s4, 0, sizeof(s4));
s4.sin_family = AF_INET;
if (iface == "0.0.0.0" || iface.length() == 0) {
s4.sin_addr.s_addr = htonl(INADDR_ANY);
} else {
inet_pton(AF_INET, iface.c_str(), &s4.sin_addr);
}
s4.sin_port = htons(port);
int r = ::bind(sock, (sockaddr *)&s4, sizeof(s4));
if (r == 0) {
result = ntohs(s4.sin_port);
} }
} }
if (!result){
DEBUG_MSG(DLVL_FAIL, "Could not bind %s UDP socket to port %d: %s", isIPv6 ? "IPv6" : "IPv4", port, strerror(errno)); std::string err_str;
for (rp = addr_result; rp != NULL; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock == -1) {
continue;
}
char human_addr[INET6_ADDRSTRLEN];
getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST);
MEDIUM_MSG("Attempting bind to %s (%s)", human_addr, rp->ai_family == AF_INET6 ? "IPv6" : "IPv4");
if (::bind(sock, rp->ai_addr, rp->ai_addrlen) == 0) {
family = rp->ai_family;
hints.ai_family = family;
break;
}
if (err_str.size()){err_str += ", ";}
err_str += human_addr;
err_str += ":";
err_str += strerror(errno);
close();//we open a new socket for each attempt
}
if (sock == -1){
FAIL_MSG("Could not open %s for UDP: %s", iface.c_str(), err_str.c_str());
freeaddrinfo(addr_result);
return 0; return 0;
} }
//Detect multicast //socket is bound! Let's collect some more data...
if (iface.length() && ((atoi(iface.c_str()) & 0xE0) == 0xE0)){ if (family == AF_INET6){
if (!multicastInterfaces.length()){ sockaddr_in6 * addr6 = (sockaddr_in6*)(rp->ai_addr);
WARN_MSG("Multicast IP given without any defined interfaces"); result = ntohs(addr6->sin6_port);
if (memcmp((char*)&(addr6->sin6_addr), "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){
//IPv6-mapped IPv4 address - 13th byte ([12]) holds the first IPv4 byte
multicast = (((char*)&(addr6->sin6_addr))[12] & 0xF0) == 0xE0;
}else{ }else{
struct ip_mreq group; //"normal" IPv6 address - prefix ff00::/8
inet_pton(AF_INET, iface.c_str(), &group.imr_multiaddr.s_addr); multicast = (((char*)&(addr6->sin6_addr))[0] == 0xFF);
size_t loc = 0;
while (loc != std::string::npos){
size_t nxtPos = multicastInterfaces.find(',', loc);
std::string curIface = multicastInterfaces.substr(loc, (nxtPos == std::string::npos ? nxtPos : nxtPos - loc));
inet_pton(AF_INET, curIface.c_str(), &group.imr_interface.s_addr);
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0) {
WARN_MSG("Unable to register for multicast on interface %s: %s", curIface.c_str() , strerror(errno));
}
loc = (nxtPos == std::string::npos ? nxtPos : nxtPos + 1);
}
} }
}else{
sockaddr_in * addr4 = (sockaddr_in*)(rp->ai_addr);
result = ntohs(addr4->sin_port);
//multicast has a "1110" bit prefix
multicast = (((char*)&(addr4->sin_addr))[0] & 0xF0) == 0xE0;
}
freeaddrinfo(addr_result);
//handle multicast membership(s)
if (multicast){
struct ipv6_mreq mreq6;
struct ip_mreq mreq4;
memset(&mreq4, 0, sizeof(mreq4));
memset(&mreq6, 0, sizeof(mreq6));
struct addrinfo *reslocal, *resmulti;
if ((addr_ret = getaddrinfo(iface.c_str(), 0, &hints, &resmulti)) != 0){
WARN_MSG("Unable to parse multicast address: %s", gai_strerror(addr_ret));
close();
result = -1;
return result;
}
if (!multicastInterfaces.length()){
if (family == AF_INET6){
memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6*)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr));
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0) {
FAIL_MSG("Unable to register for IPv6 multicast on all interfaces: %s", strerror(errno));
close();
result = -1;
}
}else{
mreq4.imr_multiaddr = ((sockaddr_in*)resmulti->ai_addr)->sin_addr;
mreq4.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0) {
FAIL_MSG("Unable to register for IPv4 multicast on all interfaces: %s", strerror(errno));
close();
result = -1;
}
}
}else{
size_t nxtPos = std::string::npos;
bool atLeastOne = false;
for (size_t loc = 0; loc != std::string::npos; loc = (nxtPos == std::string::npos ? nxtPos : nxtPos + 1)){
nxtPos = multicastInterfaces.find(',', loc);
std::string curIface = multicastInterfaces.substr(loc, (nxtPos == std::string::npos ? nxtPos : nxtPos - loc));
//do a bit of filtering for IPv6, removing the []-braces, if any
if (curIface[0] == '['){
if (curIface[curIface.size() - 1] == ']'){
curIface = curIface.substr(1, curIface.size()-2);
}else{
curIface = curIface.substr(1, curIface.size()-1);
}
}
if (family == AF_INET6){
INFO_MSG("Registering for IPv6 multicast on interface %s", curIface.c_str());
if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){
WARN_MSG("Unable to resolve IPv6 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret));
continue;
}
memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6*)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr));
mreq6.ipv6mr_interface = ((sockaddr_in6*)reslocal->ai_addr)->sin6_scope_id;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq6, sizeof(mreq6)) != 0) {
FAIL_MSG("Unable to register for IPv6 multicast on interface %s (%u): %s", curIface.c_str(), ((sockaddr_in6*)reslocal->ai_addr)->sin6_scope_id, strerror(errno));
}else{
atLeastOne = true;
}
}else{
INFO_MSG("Registering for IPv4 multicast on interface %s", curIface.c_str());
if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){
WARN_MSG("Unable to resolve IPv4 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret));
continue;
}
mreq4.imr_multiaddr = ((sockaddr_in*)resmulti->ai_addr)->sin_addr;
mreq4.imr_interface = ((sockaddr_in*)reslocal->ai_addr)->sin_addr;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0) {
FAIL_MSG("Unable to register for IPv4 multicast on interface %s: %s", curIface.c_str() , strerror(errno));
}else{
atLeastOne = true;
}
}
if (!atLeastOne){
close();
result = -1;
}
freeaddrinfo(reslocal);//free resolved interface addr
}//loop over all interfaces
}
freeaddrinfo(resmulti);//free resolved multicast addr
} }
return result; return result;
} }

View file

@ -130,13 +130,14 @@ namespace Socket {
unsigned int up;///< Amount of bytes transferred up. unsigned int up;///< Amount of bytes transferred up.
unsigned int down;///< Amount of bytes transferred down. unsigned int down;///< Amount of bytes transferred down.
unsigned int data_size;///< The size in bytes of the allocated space in the data pointer. unsigned int data_size;///< The size in bytes of the allocated space in the data pointer.
bool isIPv6;//<<< True if IPv6 socket, false otherwise. int family;///<Current socket address family
public: public:
char * data;///< Holds the last received packet. char * data;///< Holds the last received packet.
unsigned int data_len; ///< The size in bytes of the last received packet. unsigned int data_len; ///< The size in bytes of the last received packet.
UDPConnection(const UDPConnection & o); UDPConnection(const UDPConnection & o);
UDPConnection(bool nonblock = false); UDPConnection(bool nonblock = false);
~UDPConnection(); ~UDPConnection();
void close();
int getSock(); int getSock();
int bind(int port, std::string iface = "", const std::string & multicastAddress = ""); int bind(int port, std::string iface = "", const std::string & multicastAddress = "");
void setBlocking(bool blocking); void setBlocking(bool blocking);

View file

@ -86,17 +86,18 @@ var k=function(){function a(c){var b=mist.data.capabilities.connectors[c.connect
var o={},x=[];for(q in mist.data.capabilities.connectors)x.push([q,q]);var V=$("<span>");d.append(UI.buildUI([{label:"Protocol",type:"select",select:x,"function":function(){V.html(F($(this).getval()))}}])).append(V)}break;case "Streams":if(!("capabilities"in mist.data)){d.html("Loading..");mist.send(function(){UI.navto(a)},{capabilities:!0});break}h=$("<tbody>").append($("<tr>").append("<td>").attr("colspan",6).text("Loading.."));e=$("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Stream name").attr("data-sort-type", var o={},x=[];for(q in mist.data.capabilities.connectors)x.push([q,q]);var V=$("<span>");d.append(UI.buildUI([{label:"Protocol",type:"select",select:x,"function":function(){V.html(F($(this).getval()))}}])).append(V)}break;case "Streams":if(!("capabilities"in mist.data)){d.html("Loading..");mist.send(function(){UI.navto(a)},{capabilities:!0});break}h=$("<tbody>").append($("<tr>").append("<td>").attr("colspan",6).text("Loading.."));e=$("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Stream name").attr("data-sort-type",
"string").addClass("sorting-asc")).append($("<th>").text("Source").attr("data-sort-type","string")).append($("<th>").text("Status").attr("data-sort-type","int")).append($("<th>").css("text-align","right").text("Connections").attr("data-sort-type","int")).append($("<th>")).append($("<th>")))).append(h);d.append(UI.buildUI([{type:"help",help:"Here you can create, edit or delete new and existing streams. Immidiately go to the stream preview or view the information available about the stream with the info button."}])).append($("<button>").text("New stream").click(function(){UI.navto("Edit Stream")})).append(e); "string").addClass("sorting-asc")).append($("<th>").text("Source").attr("data-sort-type","string")).append($("<th>").text("Status").attr("data-sort-type","int")).append($("<th>").css("text-align","right").text("Connections").attr("data-sort-type","int")).append($("<th>")).append($("<th>")))).append(h);d.append(UI.buildUI([{type:"help",help:"Here you can create, edit or delete new and existing streams. Immidiately go to the stream preview or view the information available about the stream with the info button."}])).append($("<button>").text("New stream").click(function(){UI.navto("Edit Stream")})).append(e);
e.stupidtable();var E=function(){var a=[],c;for(c in mist.data.active_streams)a.push({streams:[mist.data.active_streams[c]],fields:["clients"],start:-2});mist.send(function(){$.extend(true,z,mist.data.streams);var a=0;h.html("");if(mist.data.LTS)for(a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");if(!(c.length<2)&&c[0]in mist.data.streams){c=W(mist.data.active_streams[a],mist.data.streams[c[0]]);c.online=1;z[mist.data.active_streams[a]]=c}}c=Object.keys(z);c.sort();for(var b in c){var d= e.stupidtable();var E=function(){var a=[],c;for(c in mist.data.active_streams)a.push({streams:[mist.data.active_streams[c]],fields:["clients"],start:-2});mist.send(function(){$.extend(true,z,mist.data.streams);var a=0;h.html("");if(mist.data.LTS)for(a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");if(!(c.length<2)&&c[0]in mist.data.streams){c=W(mist.data.active_streams[a],mist.data.streams[c[0]]);c.online=1;z[mist.data.active_streams[a]]=c}}c=Object.keys(z);c.sort();for(var b in c){var d=
c[b],e;e=d in mist.data.streams?mist.data.streams[d]:z[d];var f=$("<td>").css("text-align","right").html($("<span>").addClass("description").text("Loading..")),t=0;if(typeof mist.data.totals!="undefined"&&typeof mist.data.totals[d]!="undefined"){var g=mist.data.totals[d].all_protocols.clients,t=0;if(g.length){for(a in g)t=t+g[a][1];t=Math.round(t/g.length)}}f.html(UI.format.number(t));if(t==0&&e.online==1)e.online=2;t=$("<td>").css("text-align","right").css("white-space","nowrap");(!("ischild"in e)|| c[b],e;e=d in mist.data.streams?mist.data.streams[d]:z[d];var f=$("<td>").css("text-align","right").html($("<span>").addClass("description").text("Loading..")),g=0;if(typeof mist.data.totals!="undefined"&&typeof mist.data.totals[d]!="undefined"){var t=mist.data.totals[d].all_protocols.clients,g=0;if(t.length){for(a in t)g=g+t[a][1];g=Math.round(g/t.length)}}f.html(UI.format.number(g));if(g==0&&e.online==1)e.online=2;g=$("<td>").css("text-align","right").css("white-space","nowrap");(!("ischild"in e)||
!e.ischild)&&t.html($("<button>").text("Edit").click(function(){UI.navto("Edit Stream",$(this).closest("tr").data("index"))})).append($("<button>").text("Delete").click(function(){var a=$(this).closest("tr").data("index");if(confirm('Are you sure you want to delete the stream "'+a+'"?')){delete mist.data.streams[a];var c={};mist.data.LTS?c.deletestream=[a]:c.streams=mist.data.streams;mist.send(function(){UI.navto("Streams")},c)}}));g=$("<span>").text(d);e.ischild&&g.css("padding-left","1em");var i= !e.ischild)&&g.html($("<button>").text("Edit").click(function(){UI.navto("Edit Stream",$(this).closest("tr").data("index"))})).append($("<button>").text("Delete").click(function(){var a=$(this).closest("tr").data("index");if(confirm('Are you sure you want to delete the stream "'+a+'"?')){delete mist.data.streams[a];var c={};mist.data.LTS?c.deletestream=[a]:c.streams=mist.data.streams;mist.send(function(){UI.navto("Streams")},c)}}));t=$("<span>").text(d);e.ischild&&t.css("padding-left","1em");var i=
UI.format.status(e),j=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("tr").data("index"))});if(e.filesfound){i.html("");j="";f.html("")}h.append($("<tr>").data("index",d).html($("<td>").html(g).attr("title",d).addClass("overflow_ellipsis")).append($("<td>").text(e.source).attr("title",e.source).addClass("description").addClass("overflow_ellipsis").css("max-width","20em")).append($("<td>").data("sort-value",e.online).html(i)).append(f).append($("<td>").html(j)).append(t)); UI.format.status(e),j=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("tr").data("index"))});if(e.filesfound){i.html("");j="";f.html("")}h.append($("<tr>").data("index",d).html($("<td>").html(t).attr("title",d).addClass("overflow_ellipsis")).append($("<td>").text(e.source).attr("title",e.source).addClass("description").addClass("overflow_ellipsis").css("max-width","20em")).append($("<td>").data("sort-value",e.online).html(i)).append(f).append($("<td>").html(j)).append(g));
a++}},{totals:a,active_streams:true})},z=$.extend(!0,{},mist.data.streams),W=function(a,c){var b=$.extend({},c);delete b.meta;delete b.error;b.online=2;b.name=a;b.ischild=true;return b};if(mist.data.LTS){var C=0,G=0;for(l in mist.data.streams)r=mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"],mist.inputMatch(r.source_match,mist.data.streams[l].source)&&(z[l].source+="*",mist.send(function(a,c){var b=c.stream,d;for(d in a.browse.files)for(var e in mist.data.capabilities.inputs)if(!(e.indexOf("Buffer")>= a++}},{totals:a,active_streams:true})},z=$.extend(!0,{},mist.data.streams),W=function(a,c){var b=$.extend({},c);delete b.meta;delete b.error;b.online=2;b.name=a;b.ischild=true;return b};if(mist.data.LTS){var C=0,G=0;for(l in mist.data.streams)r=mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"],mist.inputMatch(r.source_match,mist.data.streams[l].source)&&(z[l].source+="*",mist.send(function(a,c){var b=c.stream,d;for(d in a.browse.files)for(var e in mist.data.capabilities.inputs)if(!(e.indexOf("Buffer")>=
0||e.indexOf("Folder")>=0)&&mist.inputMatch(mist.data.capabilities.inputs[e].source_match,"/"+a.browse.files[d])){var f=b+"+"+a.browse.files[d];z[f]=W(f,mist.data.streams[b]);z[f].source=mist.data.streams[b].source+a.browse.files[d]}"files"in a.browse&&a.browse.files.length?z[b].filesfound=true:mist.data.streams[b].filesfound=false;G++;if(C==G){mist.send(function(){E()},{active_streams:true});UI.interval.set(function(){E()},1E4)}},{browse:mist.data.streams[l].source},{stream:l}),C++);0==C&&(mist.send(function(){E()}, 0||e.indexOf("Folder")>=0)&&mist.inputMatch(mist.data.capabilities.inputs[e].source_match,"/"+a.browse.files[d])){var f=b+"+"+a.browse.files[d];z[f]=W(f,mist.data.streams[b]);z[f].source=mist.data.streams[b].source+a.browse.files[d]}"files"in a.browse&&a.browse.files.length?z[b].filesfound=true:mist.data.streams[b].filesfound=false;G++;if(C==G){mist.send(function(){E()},{active_streams:true});UI.interval.set(function(){E()},1E4)}},{browse:mist.data.streams[l].source},{stream:l}),C++);0==C&&(mist.send(function(){E()},
{active_streams:!0}),UI.interval.set(function(){E()},3E4))}else mist.send(function(){E()},{active_streams:!0}),UI.interval.set(function(){E()},1E4);break;case "Edit Stream":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");break}j=!1;""!=c&&(j=!0);j?(l=c,o=mist.data.streams[l],d.find("h2").append(' "'+l+'"')):(d.html($("<h2>").text("New Stream")),o={});l=[];for(q in mist.data.capabilities.inputs)l.push(mist.data.capabilities.inputs[q].source_match); {active_streams:!0}),UI.interval.set(function(){E()},3E4))}else mist.send(function(){E()},{active_streams:!0}),UI.interval.set(function(){E()},1E4);break;case "Edit Stream":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");break}j=!1;""!=c&&(j=!0);j?(l=c,o=mist.data.streams[l],d.find("h2").append(' "'+l+'"')):(d.html($("<h2>").text("New Stream")),o={});l=[];for(q in mist.data.capabilities.inputs)l.push(mist.data.capabilities.inputs[q].source_match);
var J=$("<div>");d.append(UI.buildUI([{label:"Stream name",type:"str",validate:["required","streamname"],pointer:{main:o,index:"name"},help:"Set the name this stream will be recognised by for players and/or stream pushing."},{label:"Source",type:"browse",validate:["required"],filetypes:l,pointer:{main:o,index:"source"},help:'Set the stream source.<table><tr><td>VoD:</td><td>You can browse to the file or folder as a source or simply enter the path to the file.</td></tr><tr><td>Live:</td><td>You\'ll need to enter "push://IP" with the IP of the machine pushing towards MistServer.<br>You can use "push://" to accept any source.</td></tr><tr><td>(Pro only)</td><td>Use "push://(IP)@password" to set a password protection for pushes.</td></tr></table>If you\'re unsure how to set the source properly, please view our Live pushing guide at the tools section.', var J=$("<div>");d.append(UI.buildUI([{label:"Stream name",type:"str",validate:["required","streamname"],pointer:{main:o,index:"name"},help:"Set the name this stream will be recognised by for players and/or stream pushing."},{label:"Source",type:"browse",validate:["required"],filetypes:l,pointer:{main:o,index:"source"},help:'Set the stream source.<table><tr><td>VoD:</td><td>You can browse to the file or folder as a source or simply enter the path to the file.</td></tr><tr><td>Live:</td><td>You\'ll need to enter "push://IP" with the IP of the machine pushing towards MistServer.<br>You can use "push://" to accept any source.</td></tr><tr><td>(Pro only)</td><td>Use "push://(IP)@password" to set a password protection for pushes.</td></tr></table>If you\'re unsure how to set the source properly, please view our Live pushing guide at the tools section.',
"function":function(){var a=$(this).val();if(a!=""){var c=null,b;for(b in mist.data.capabilities.inputs)if(typeof mist.data.capabilities.inputs[b].source_match!="undefined"&&mist.inputMatch(mist.data.capabilities.inputs[b].source_match,a)){c=b;break}if(c===null)J.html($("<h3>").text("Unrecognized input").addClass("red")).append($("<span>").text("Please edit the stream source.").addClass("red"));else{a=mist.data.capabilities.inputs[c];J.html($("<h3>").text(a.name+" Input options"));a=mist.convertBuildOptions(a, "function":function(){var a=$(this).val();if(a!=""){var c=null,b;for(b in mist.data.capabilities.inputs)if(typeof mist.data.capabilities.inputs[b].source_match!="undefined"&&mist.inputMatch(mist.data.capabilities.inputs[b].source_match,a)){c=b;break}if(c===null)J.html($("<h3>").text("Unrecognized input").addClass("red")).append($("<span>").text("Please edit the stream source.").addClass("red"));else{c=mist.data.capabilities.inputs[c];J.html($("<h3>").text(c.name+" Input options"));c=mist.convertBuildOptions(c,
o);J.append(UI.buildUI(a))}}}},$("<br>"),{type:"custom",custom:J},$("<br>"),$("<h3>").text("Encryption"),{type:"help",help:"To enable encryption, the licence acquisition url must be entered, as well as either the content key or the key ID and seed.<br>Unsure how you should fill in your encryption or missing your preferred encryption? Please contact us."},{label:"License acquisition url",type:"str",LTSonly:!0,pointer:{main:o,index:"la_url"}},$("<br>"),{label:"Content key",type:"str",LTSonly:!0,pointer:{main:o, o);"always_match"in mist.data.capabilities.inputs[b]&&mist.inputMatch(mist.data.capabilities.inputs[b].always_match,a)&&c.push({label:"Always on",type:"checkbox",help:"Keep this input available at all times, even when there are no active viewers.",pointer:{main:o,index:"always_on"}});J.append(UI.buildUI(c))}}}},{label:"Stop sessions",type:"checkbox",help:"When saving these stream settings, kill this stream's current connections.",pointer:{main:o,index:"stop_sessions"}},$("<br>"),{type:"custom",custom:J},
index:"contentkey"}},{type:"text",text:" - or - "},{label:"Key ID",type:"str",LTSonly:!0,pointer:{main:o,index:"keyid"}},{label:"Key seed",type:"str",LTSonly:!0,pointer:{main:o,index:"keyseed"}},{type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Streams")}},{type:"save",label:"Save","function":function(){if(!mist.data.streams)mist.data.streams={};mist.data.streams[o.name]=o;c!=o.name&&delete mist.data.streams[c];var a={};if(mist.data.LTS){a.addstream={};a.addstream[o.name]= $("<br>"),$("<h3>").text("Encryption"),{type:"help",help:"To enable encryption, the licence acquisition url must be entered, as well as either the content key or the key ID and seed.<br>Unsure how you should fill in your encryption or missing your preferred encryption? Please contact us."},{label:"License acquisition url",type:"str",LTSonly:!0,pointer:{main:o,index:"la_url"}},$("<br>"),{label:"Content key",type:"str",LTSonly:!0,pointer:{main:o,index:"contentkey"}},{type:"text",text:" - or - "},{label:"Key ID",
o;if(c!=o.name)a.deletestream=[c]}else a.streams=mist.data.streams;mist.send(function(){delete mist.data.streams[o.name].online;delete mist.data.streams[o.name].error;UI.navto("Streams")},a)}}]}]));break;case "Preview":if(""==c){d.append("Loading..");var N=function(c){var b={};c.sort();d.html($("<h2>").text(a)).append(UI.buildUI([{label:"Select a stream",type:"select",select:c,pointer:{main:b,index:"stream"}},{type:"buttons",buttons:[{type:"save",label:"Go","function":function(){UI.navto(a,b.stream)}}]}])); type:"str",LTSonly:!0,pointer:{main:o,index:"keyid"}},{label:"Key seed",type:"str",LTSonly:!0,pointer:{main:o,index:"keyseed"}},{type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Streams")}},{type:"save",label:"Save","function":function(){if(!mist.data.streams)mist.data.streams={};mist.data.streams[o.name]=o;c!=o.name&&delete mist.data.streams[c];var a={};if(mist.data.LTS){a.addstream={};a.addstream[o.name]=o;if(c!=o.name)a.deletestream=[c]}else a.streams=mist.data.streams;
if(o.stop_sessions&&c!=""){a.stop_sessions=c;delete o.stop_sessions}mist.send(function(){delete mist.data.streams[o.name].online;delete mist.data.streams[o.name].error;UI.navto("Streams")},a)}}]}]));break;case "Preview":if(""==c){d.append("Loading..");var N=function(c){var b={};c.sort();d.html($("<h2>").text(a)).append(UI.buildUI([{label:"Select a stream",type:"select",select:c,pointer:{main:b,index:"stream"}},{type:"buttons",buttons:[{type:"save",label:"Go","function":function(){UI.navto(a,b.stream)}}]}]));
UI.elements.secondary_menu.html("").append($("<a>").addClass("button").addClass("active").text("Choose stream").click(function(){UI.navto("Preview")}));var e=$("<div>").addClass("preview_icons");d.append($("<span>").addClass("description").text("Or, click a stream from the list below.")).append(e);for(var f in c){var g=c[f],h="";if(g.indexOf("+")>-1){h=g.split("+");h=mist.data.streams[h[0]].source+h[1]}else h=mist.data.streams[g].source;e.append($("<button>").append($("<span>").text(g)).append($("<span>").addClass("description").text(h)).attr("title", UI.elements.secondary_menu.html("").append($("<a>").addClass("button").addClass("active").text("Choose stream").click(function(){UI.navto("Preview")}));var e=$("<div>").addClass("preview_icons");d.append($("<span>").addClass("description").text("Or, click a stream from the list below.")).append(e);for(var f in c){var g=c[f],h="";if(g.indexOf("+")>-1){h=g.split("+");h=mist.data.streams[h[0]].source+h[1]}else h=mist.data.streams[g].source;e.append($("<button>").append($("<span>").text(g)).append($("<span>").addClass("description").text(h)).attr("title",
g).attr("data-stream",g).click(function(){UI.navto("Preview",$(this).attr("data-stream"))}))}};if(mist.data.LTS){if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");break}G=C=0;x={};for(l in mist.data.streams)r=mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"],mist.inputMatch(r.source_match,mist.data.streams[l].source)&&(mist.send(function(a,c){var b=c.stream,d;for(d in a.browse.files)for(var e in mist.data.capabilities.inputs)e.indexOf("Buffer")>= g).attr("data-stream",g).click(function(){UI.navto("Preview",$(this).attr("data-stream"))}))}};if(mist.data.LTS){if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");break}G=C=0;x={};for(l in mist.data.streams)r=mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"],mist.inputMatch(r.source_match,mist.data.streams[l].source)&&(mist.send(function(a,c){var b=c.stream,d;for(d in a.browse.files)for(var e in mist.data.capabilities.inputs)e.indexOf("Buffer")>=
0||e.indexOf("Folder")>=0||mist.inputMatch(mist.data.capabilities.inputs[e].source_match,"/"+a.browse.files[d])&&(x[b+"+"+a.browse.files[d]]=true);G++;C==G&&mist.send(function(){for(var a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");c.length>1&&c[0]in mist.data.streams&&(x[mist.data.active_streams[a]]=true)}x=Object.keys(x);x=x.concat(Object.keys(mist.data.streams));x.sort();N(x)},{active_streams:true})},{browse:mist.data.streams[l].source},{stream:l}),C++);0==C&&mist.send(function(){var c= 0||e.indexOf("Folder")>=0||mist.inputMatch(mist.data.capabilities.inputs[e].source_match,"/"+a.browse.files[d])&&(x[b+"+"+a.browse.files[d]]=true);G++;C==G&&mist.send(function(){for(var a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");c.length>1&&c[0]in mist.data.streams&&(x[mist.data.active_streams[a]]=true)}x=Object.keys(x);x=x.concat(Object.keys(mist.data.streams));x.sort();N(x)},{active_streams:true})},{browse:mist.data.streams[l].source},{stream:l}),C++);0==C&&mist.send(function(){var c=

View file

@ -2569,8 +2569,27 @@ var UI = {
$('<h3>').text(input.name+' Input options') $('<h3>').text(input.name+' Input options')
); );
var build = mist.convertBuildOptions(input,saveas); var build = mist.convertBuildOptions(input,saveas);
if (('always_match' in mist.data.capabilities.inputs[i]) && (mist.inputMatch(mist.data.capabilities.inputs[i].always_match,source))) {
build.push({
label: 'Always on',
type: 'checkbox',
help: 'Keep this input available at all times, even when there are no active viewers.',
pointer: {
main: saveas,
index: 'always_on'
}
});
}
$inputoptions.append(UI.buildUI(build)); $inputoptions.append(UI.buildUI(build));
} }
},{
label: 'Stop sessions',
type: 'checkbox',
help: 'When saving these stream settings, kill this stream\'s current connections.',
pointer: {
main: saveas,
index: 'stop_sessions'
}
},$('<br>'),{ },$('<br>'),{
type: 'custom', type: 'custom',
custom: $inputoptions custom: $inputoptions
@ -2645,6 +2664,11 @@ var UI = {
else { else {
send.streams = mist.data.streams; send.streams = mist.data.streams;
} }
if ((saveas.stop_sessions) && (other != '')) {
send.stop_sessions = other;
delete saveas.stop_sessions;
}
mist.send(function(){ mist.send(function(){
delete mist.data.streams[saveas.name].online; delete mist.data.streams[saveas.name].online;
delete mist.data.streams[saveas.name].error; delete mist.data.streams[saveas.name].error;

View file

@ -86,30 +86,18 @@ namespace Mist {
} }
void Input::checkHeaderTimes(std::string streamFile) { void Input::checkHeaderTimes(std::string streamFile) {
if (streamFile == "-" || streamFile == "push://") { struct stat bufStream;
struct stat bufHeader;
if (stat(streamFile.c_str(), &bufStream) != 0) {
INSANE_MSG("Source is not a file - ignoring header check");
return; return;
} }
std::string headerFile = streamFile + ".dtsh"; std::string headerFile = streamFile + ".dtsh";
FILE * tmp = fopen(headerFile.c_str(), "r"); if (stat(headerFile.c_str(), &bufHeader) != 0) {
if (tmp == NULL) { INSANE_MSG("No header exists to compare - ignoring header check");
DEBUG_MSG(DLVL_HIGH, "Can't open header: %s. Assuming all is fine.", headerFile.c_str());
return; return;
} }
struct stat bufStream; if (bufHeader.st_mtime < bufStream.st_mtime) {
struct stat bufHeader;
//fstat(fileno(streamFile), &bufStream);
//fstat(fileno(tmp), &bufHeader);
if (stat(streamFile.c_str(), &bufStream) != 0 || stat(headerFile.c_str(), &bufHeader) != 0) {
DEBUG_MSG(DLVL_HIGH, "Could not compare stream and header timestamps - assuming all is fine.");
fclose(tmp);
return;
}
int timeStream = bufStream.st_mtime;
int timeHeader = bufHeader.st_mtime;
fclose(tmp);
if (timeHeader < timeStream) {
//delete filename
INFO_MSG("Overwriting outdated DTSH header file: %s ", headerFile.c_str()); INFO_MSG("Overwriting outdated DTSH header file: %s ", headerFile.c_str());
remove(headerFile.c_str()); remove(headerFile.c_str());
} }