Global cleanups and standardization of code style.

This commit is contained in:
Thulinma 2012-12-11 11:03:33 +01:00
parent 51a9b4162c
commit 38ef8704f8
33 changed files with 4322 additions and 2824 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,14 +4,13 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <iostream> #include <iostream>
//#include <string.h>
#include <string> #include <string>
/// Holds all AMF parsing and creation related functions and classes. /// Holds all AMF parsing and creation related functions and classes.
namespace AMF{ namespace AMF {
/// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use.
enum obj0type { enum obj0type{
AMF0_NUMBER = 0x00, AMF0_NUMBER = 0x00,
AMF0_BOOL = 0x01, AMF0_BOOL = 0x01,
AMF0_STRING = 0x02, AMF0_STRING = 0x02,
@ -34,7 +33,7 @@ namespace AMF{
}; };
/// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use. /// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use.
enum obj3type { enum obj3type{
AMF3_UNDEFINED = 0x00, AMF3_UNDEFINED = 0x00,
AMF3_NULL = 0x01, AMF3_NULL = 0x01,
AMF3_FALSE = 0x02, AMF3_FALSE = 0x02,
@ -50,10 +49,10 @@ namespace AMF{
AMF3_BYTES = 0x0C, AMF3_BYTES = 0x0C,
AMF3_DDV_CONTAINER = 0xFF AMF3_DDV_CONTAINER = 0xFF
}; };
/// Recursive class that holds AMF0 objects. /// Recursive class that holds AMF0 objects.
/// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type. /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type.
class Object { class Object{
public: public:
std::string Indice(); std::string Indice();
obj0type GetType(); obj0type GetType();
@ -78,7 +77,8 @@ namespace AMF{
std::string strval; ///< Holds this objects string value, if any. std::string strval; ///< Holds this objects string value, if any.
double numval; ///< Holds this objects numeric value, if any. double numval; ///< Holds this objects numeric value, if any.
std::vector<Object> contents; ///< Holds this objects contents, if any (for container types). std::vector<Object> contents; ///< Holds this objects contents, if any (for container types).
};//AMFType };
//AMFType
/// Parses a C-string to a valid AMF::Object. /// Parses a C-string to a valid AMF::Object.
Object parse(const unsigned char * data, unsigned int len); Object parse(const unsigned char * data, unsigned int len);
@ -89,7 +89,7 @@ namespace AMF{
/// Recursive class that holds AMF3 objects. /// Recursive class that holds AMF3 objects.
/// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type. /// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type.
class Object3 { class Object3{
public: public:
std::string Indice(); std::string Indice();
obj3type GetType(); obj3type GetType();
@ -117,13 +117,14 @@ namespace AMF{
double dblval; ///< Holds this objects double value, if any. double dblval; ///< Holds this objects double value, if any.
int intval; ///< Holds this objects int value, if any. int intval; ///< Holds this objects int value, if any.
std::vector<Object3> contents; ///< Holds this objects contents, if any (for container types). std::vector<Object3> contents; ///< Holds this objects contents, if any (for container types).
};//AMFType };
//AMFType
/// Parses a C-string to a valid AMF::Object3. /// Parses a C-string to a valid AMF::Object3.
Object3 parse3(const unsigned char * data, unsigned int len); Object3 parse3(const unsigned char * data, unsigned int len);
/// Parses a std::string to a valid AMF::Object3. /// Parses a std::string to a valid AMF::Object3.
Object3 parse3(std::string data); Object3 parse3(std::string data);
/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions.
Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name);
};//AMF namespace } //AMF namespace

View file

@ -6,35 +6,21 @@
#include "auth.h" #include "auth.h"
#include "base64.h" #include "base64.h"
namespace Secure{ namespace Secure {
static unsigned char __gbv2keypub_der[] = { static unsigned char __gbv2keypub_der[] = {0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, 0x7d, 0x73, 0xc6, 0xe6, 0xfb,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, 0x69, 0xbf, 0x26, 0x79, 0xc7,
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, 0x5d, 0xac, 0x6a, 0x94, 0xb1,
0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, 0x16, 0xa2, 0x8e, 0x89, 0x12,
0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, 0xba, 0x63, 0xc9, 0xcc, 0xee,
0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, 0xa7, 0xe8, 0x28, 0x89, 0x25,
0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, 0x8f, 0x60, 0x2e, 0xc2, 0xd8,
0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, 0xee, 0x96, 0x3e, 0x4a, 0x9b,
0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, 0x7d, 0xac, 0xdc, 0x30, 0x56,
0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, 0x13, 0x65, 0xc9, 0x9a, 0x38,
0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, 0xad, 0x7e, 0xe5, 0x3c, 0xa8,
0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01}; ///< The GBv2 public key file.
0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a,
0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa,
0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42,
0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2,
0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4,
0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b,
0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee,
0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c,
0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49,
0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde,
0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8,
0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3,
0x79, 0x02, 0x03, 0x01, 0x00, 0x01
}; ///< The GBv2 public key file.
static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data
/// Attempts to load the GBv2 public key. /// Attempts to load the GBv2 public key.

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
namespace Secure{ namespace Secure {
class Auth{ class Auth{
private: private:
void * pubkey; ///< Holds the public key. void * pubkey; ///< Holds the public key.

View file

@ -3,62 +3,84 @@
/// Needed for base64_encode function /// Needed for base64_encode function
const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/// Helper for base64_decode function /// Helper for base64_decode function
inline bool Base64::is_base64(unsigned char c) { inline bool Base64::is_base64(unsigned char c){
return (isalnum(c) || (c == '+') || (c == '/')); return (isalnum(c) || (c == '+') || (c == '/'));
} }
/// Used to base64 encode data. Input is the plaintext as std::string, output is the encoded data as std::string. /// Used to base64 encode data. Input is the plaintext as std::string, output is the encoded data as std::string.
/// \param input Plaintext data to encode. /// \param input Plaintext data to encode.
/// \returns Base64 encoded data. /// \returns Base64 encoded data.
std::string Base64::encode(std::string const input) { std::string Base64::encode(std::string const input){
std::string ret; std::string ret;
unsigned int in_len = input.size(); unsigned int in_len = input.size();
char quad[4], triple[3]; char quad[4], triple[3];
unsigned int i, x, n = 3; unsigned int i, x, n = 3;
for (x = 0; x < in_len; x = x + 3){ for (x = 0; x < in_len; x = x + 3){
if ((in_len - x) / 3 == 0){n = (in_len - x) % 3;} if ((in_len - x) / 3 == 0){
for (i=0; i < 3; i++){triple[i] = '0';} n = (in_len - x) % 3;
for (i=0; i < n; i++){triple[i] = input[x + i];} }
for (i = 0; i < 3; i++){
triple[i] = '0';
}
for (i = 0; i < n; i++){
triple[i] = input[x + i];
}
quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100 quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100
quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11 quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11
quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110 quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110
quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111 quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111
if (n < 3){quad[3] = '=';} if (n < 3){
if (n < 2){quad[2] = '=';} quad[3] = '=';
for(i=0; i < 4; i++){ret += quad[i];} }
if (n < 2){
quad[2] = '=';
}
for (i = 0; i < 4; i++){
ret += quad[i];
}
} }
return ret; return ret;
}//base64_encode } //base64_encode
/// Used to base64 decode data. Input is the encoded data as std::string, output is the plaintext data as std::string. /// Used to base64 decode data. Input is the encoded data as std::string, output is the plaintext data as std::string.
/// \param encoded_string Base64 encoded data to decode. /// \param encoded_string Base64 encoded data to decode.
/// \returns Plaintext decoded data. /// \returns Plaintext decoded data.
std::string Base64::decode(std::string const& encoded_string) { std::string Base64::decode(std::string const& encoded_string){
int in_len = encoded_string.size(); int in_len = encoded_string.size();
int i = 0; int i = 0;
int j = 0; int j = 0;
int in_ = 0; int in_ = 0;
unsigned char char_array_4[4], char_array_3[3]; unsigned char char_array_4[4], char_array_3[3];
std::string ret; std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])){
char_array_4[i++] = encoded_string[in_]; in_++; char_array_4[i++ ] = encoded_string[in_];
if (i ==4) { in_++;
for (i = 0; i <4; i++){char_array_4[i] = chars.find(char_array_4[i]);} if (i == 4){
for (i = 0; i < 4; i++){
char_array_4[i] = chars.find(char_array_4[i]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++){ret += char_array_3[i];} for (i = 0; (i < 3); i++){
ret += char_array_3[i];
}
i = 0; i = 0;
} }
} }
if (i) { if (i){
for (j = i; j <4; j++){char_array_4[j] = 0;} for (j = i; j < 4; j++){
for (j = 0; j <4; j++){char_array_4[j] = chars.find(char_array_4[j]);} char_array_4[j] = 0;
}
for (j = 0; j < 4; j++){
char_array_4[j] = chars.find(char_array_4[j]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
} }
return ret; return ret;
} }

View file

@ -56,14 +56,18 @@ Util::Config::Config(std::string cmd, std::string version){
///\endcode ///\endcode
void Util::Config::addOption(std::string optname, JSON::Value option){ void Util::Config::addOption(std::string optname, JSON::Value option){
vals[optname] = option; vals[optname] = option;
if (!vals[optname].isMember("value") && vals[optname].isMember("default")){ if ( !vals[optname].isMember("value") && vals[optname].isMember("default")){
vals[optname]["value"].append(vals[optname]["default"]); vals[optname]["value"].append(vals[optname]["default"]);
vals[optname].removeMember("default"); vals[optname].removeMember("default");
} }
long_count = 0; long_count = 0;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
if (it->second.isMember("long")){long_count++;} if (it->second.isMember("long")){
if (it->second.isMember("long_off")){long_count++;} long_count++;
}
if (it->second.isMember("long_off")){
long_count++;
}
} }
} }
@ -73,16 +77,30 @@ void Util::Config::printHelp(std::ostream & output){
std::map<long long int, std::string> args; std::map<long long int, std::string> args;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
unsigned int current = 0; unsigned int current = 0;
if (it->second.isMember("long")){current += it->second["long"].asString().size() + 4;} if (it->second.isMember("long")){
if (it->second.isMember("short")){current += it->second["short"].asString().size() + 3;} current += it->second["long"].asString().size() + 4;
if (current > longest){longest = current;} }
if (it->second.isMember("short")){
current += it->second["short"].asString().size() + 3;
}
if (current > longest){
longest = current;
}
current = 0; current = 0;
if (it->second.isMember("long_off")){current += it->second["long_off"].asString().size() + 4;} if (it->second.isMember("long_off")){
if (it->second.isMember("short_off")){current += it->second["short_off"].asString().size() + 3;} current += it->second["long_off"].asString().size() + 4;
if (current > longest){longest = current;} }
if (it->second.isMember("short_off")){
current += it->second["short_off"].asString().size() + 3;
}
if (current > longest){
longest = current;
}
if (it->second.isMember("arg_num")){ if (it->second.isMember("arg_num")){
current = it->first.size() + 3; current = it->first.size() + 3;
if (current > longest){longest = current;} if (current > longest){
longest = current;
}
args[it->second["arg_num"].asInt()] = it->first; args[it->second["arg_num"].asInt()] = it->first;
} }
} }
@ -108,7 +126,9 @@ void Util::Config::printHelp(std::ostream & output){
f = "-" + it->second["short"].asString(); f = "-" + it->second["short"].asString();
} }
} }
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
if (it->second.isMember("arg")){ if (it->second.isMember("arg")){
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
}else{ }else{
@ -126,7 +146,9 @@ void Util::Config::printHelp(std::ostream & output){
f = "-" + it->second["short_off"].asString(); f = "-" + it->second["short_off"].asString();
} }
} }
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
if (it->second.isMember("arg")){ if (it->second.isMember("arg")){
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
}else{ }else{
@ -135,40 +157,49 @@ void Util::Config::printHelp(std::ostream & output){
} }
if (it->second.isMember("arg_num")){ if (it->second.isMember("arg_num")){
f = it->first; f = it->first;
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
} }
} }
} }
/// Parses commandline arguments. /// Parses commandline arguments.
/// Calls exit if an unknown option is encountered, printing a help message. /// Calls exit if an unknown option is encountered, printing a help message.
void Util::Config::parseArgs(int argc, char ** argv){ void Util::Config::parseArgs(int argc, char ** argv){
int opt = 0; int opt = 0;
std::string shortopts; std::string shortopts;
struct option * longOpts = (struct option*)calloc(long_count+1, sizeof(struct option)); struct option * longOpts = (struct option*)calloc(long_count + 1, sizeof(struct option));
int long_i = 0; int long_i = 0;
int arg_count = 0; int arg_count = 0;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
if (it->second.isMember("short")){ if (it->second.isMember("short")){
shortopts += it->second["short"].asString(); shortopts += it->second["short"].asString();
if (it->second.isMember("arg")){shortopts += ":";} if (it->second.isMember("arg")){
shortopts += ":";
}
} }
if (it->second.isMember("short_off")){ if (it->second.isMember("short_off")){
shortopts += it->second["short_off"].asString(); shortopts += it->second["short_off"].asString();
if (it->second.isMember("arg")){shortopts += ":";} if (it->second.isMember("arg")){
shortopts += ":";
}
} }
if (it->second.isMember("long")){ if (it->second.isMember("long")){
longOpts[long_i].name = it->second["long"].asString().c_str(); longOpts[long_i].name = it->second["long"].asString().c_str();
longOpts[long_i].val = it->second["short"].asString()[0]; longOpts[long_i].val = it->second["short"].asString()[0];
if (it->second.isMember("arg")){longOpts[long_i].has_arg = 1;} if (it->second.isMember("arg")){
longOpts[long_i].has_arg = 1;
}
long_i++; long_i++;
} }
if (it->second.isMember("long_off")){ if (it->second.isMember("long_off")){
longOpts[long_i].name = it->second["long_off"].asString().c_str(); longOpts[long_i].name = it->second["long_off"].asString().c_str();
longOpts[long_i].val = it->second["short_off"].asString()[0]; longOpts[long_i].val = it->second["short_off"].asString()[0];
if (it->second.isMember("arg")){longOpts[long_i].has_arg = 1;} if (it->second.isMember("arg")){
longOpts[long_i].has_arg = 1;
}
long_i++; long_i++;
} }
if (it->second.isMember("arg_num") && !(it->second.isMember("value") && it->second["value"].size())){ if (it->second.isMember("arg_num") && !(it->second.isMember("value") && it->second["value"].size())){
@ -203,10 +234,10 @@ void Util::Config::parseArgs(int argc, char ** argv){
} }
break; break;
} }
}//commandline options parser } //commandline options parser
free(longOpts);//free the long options array free(longOpts); //free the long options array
long_i = 1;//re-use long_i as an argument counter long_i = 1; //re-use long_i as an argument counter
while (optind < argc){//parse all remaining options, ignoring anything unexpected. while (optind < argc){ //parse all remaining options, ignoring anything unexpected.
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
if (it->second.isMember("arg_num") && it->second["arg_num"].asInt() == long_i){ if (it->second.isMember("arg_num") && it->second["arg_num"].asInt() == long_i){
it->second["value"].append((std::string)argv[optind]); it->second["value"].append((std::string)argv[optind]);
@ -226,18 +257,18 @@ void Util::Config::parseArgs(int argc, char ** argv){
/// Returns a reference to the current value of an option or default if none was set. /// Returns a reference to the current value of an option or default if none was set.
/// If the option does not exist, this exits the application with a return code of 37. /// If the option does not exist, this exits the application with a return code of 37.
JSON::Value & Util::Config::getOption(std::string optname, bool asArray){ JSON::Value & Util::Config::getOption(std::string optname, bool asArray){
if (!vals.isMember(optname)){ if ( !vals.isMember(optname)){
std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl; std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl;
exit(37); exit(37);
} }
if (!vals[optname].isMember("value") || !vals[optname]["value"].isArray()){ if ( !vals[optname].isMember("value") || !vals[optname]["value"].isArray()){
vals[optname]["value"].append(JSON::Value()); vals[optname]["value"].append(JSON::Value());
} }
if (asArray){ if (asArray){
return vals[optname]["value"]; return vals[optname]["value"];
}else{ }else{
int n = vals[optname]["value"].size(); int n = vals[optname]["value"].size();
return vals[optname]["value"][n-1]; return vals[optname]["value"][n - 1];
} }
} }
@ -273,7 +304,7 @@ void Util::Config::activate(){
} }
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = signal_handler; new_action.sa_handler = signal_handler;
sigemptyset (&new_action.sa_mask); sigemptyset( &new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGINT, &new_action, NULL); sigaction(SIGINT, &new_action, NULL);
sigaction(SIGHUP, &new_action, NULL); sigaction(SIGHUP, &new_action, NULL);
@ -288,41 +319,53 @@ void Util::Config::activate(){
/// signal, and ignores all other signals. /// signal, and ignores all other signals.
void Util::Config::signal_handler(int signum){ void Util::Config::signal_handler(int signum){
switch (signum){ switch (signum){
case SIGINT://these three signals will set is_active to false. case SIGINT: //these three signals will set is_active to false.
case SIGHUP: case SIGHUP:
case SIGTERM: case SIGTERM:
is_active = false; is_active = false;
break; break;
case SIGCHLD://when a child dies, reap it. case SIGCHLD: //when a child dies, reap it.
wait(0); wait(0);
break; break;
default: //other signals are ignored default: //other signals are ignored
break; break;
} }
}//signal_handler } //signal_handler
/// Adds the default connector options to this Util::Config object. /// Adds the default connector options to this Util::Config object.
void Util::Config::addConnectorOptions(int port){ void Util::Config::addConnectorOptions(int port){
JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}");
stored_port["value"].append((long long int)port); stored_port["value"].append((long long int)port);
addOption("listen_port", stored_port); addOption("listen_port", stored_port);
addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); addOption("listen_interface",
addOption("username", JSON::fromString("{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); JSON::fromString(
addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); "{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}"));
}//addConnectorOptions addOption("username",
JSON::fromString(
"{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}"));
addOption("daemonize",
JSON::fromString(
"{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}"));
} //addConnectorOptions
/// Gets directory the current executable is stored in. /// Gets directory the current executable is stored in.
std::string Util::getMyPath(){ std::string Util::getMyPath(){
char mypath[500]; char mypath[500];
int ret = readlink("/proc/self/exe", mypath, 500); int ret = readlink("/proc/self/exe", mypath, 500);
if (ret != -1){mypath[ret] = 0;}else{mypath[0] = 0;} if (ret != -1){
mypath[ret] = 0;
}else{
mypath[0] = 0;
}
std::string tPath = mypath; std::string tPath = mypath;
size_t slash = tPath.rfind('/'); size_t slash = tPath.rfind('/');
if (slash == std::string::npos){ if (slash == std::string::npos){
slash = tPath.rfind('\\'); slash = tPath.rfind('\\');
if (slash == std::string::npos){return "";} if (slash == std::string::npos){
return "";
}
} }
tPath.resize(slash+1); tPath.resize(slash + 1);
return tPath; return tPath;
} }
@ -330,20 +373,20 @@ std::string Util::getMyPath(){
void Util::setUser(std::string username){ void Util::setUser(std::string username){
if (username != "root"){ if (username != "root"){
struct passwd * user_info = getpwnam(username.c_str()); struct passwd * user_info = getpwnam(username.c_str());
if (!user_info){ if ( !user_info){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str());
#endif #endif
return; return;
}else{ }else{
if (setuid(user_info->pw_uid) != 0){ if (setuid(user_info->pw_uid) != 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str());
#endif #endif
}else{ }else{
#if DEBUG >= 3 #if DEBUG >= 3
fprintf(stderr, "Changed user to %s\n", username.c_str()); fprintf(stderr, "Changed user to %s\n", username.c_str());
#endif #endif
} }
} }
} }
@ -354,12 +397,12 @@ void Util::setUser(std::string username){
/// Does not change directory to root. /// Does not change directory to root.
/// Does redirect output to /dev/null /// Does redirect output to /dev/null
void Util::Daemonize(){ void Util::Daemonize(){
#if DEBUG >= 3 #if DEBUG >= 3
fprintf(stderr, "Going into background mode...\n"); fprintf(stderr, "Going into background mode...\n");
#endif #endif
if (daemon(1, 0) < 0) { if (daemon(1, 0) < 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno));
#endif #endif
} }
} }

View file

@ -2,11 +2,16 @@
/// Contains generic function headers for managing configuration. /// Contains generic function headers for managing configuration.
#pragma once #pragma once
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "unknown"
#endif
#include <string> #include <string>
#include "json.h" #include "json.h"
/// Contains utility code, not directly related to streaming media /// Contains utility code, not directly related to streaming media
namespace Util{ namespace Util {
/// Deals with parsing configuration from commandline options. /// Deals with parsing configuration from commandline options.
class Config{ class Config{
@ -40,4 +45,5 @@ namespace Util{
/// Will turn the current process into a daemon. /// Will turn the current process into a daemon.
void Daemonize(); void Daemonize();
}; }
;

View file

View file

@ -1,25 +0,0 @@
/// \file crypto.h
/// Holds all headers needed for RTMP cryptography functions.
#pragma once
#include <stdint.h>
#include <string>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/rc4.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/hmac.h>
void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut);
void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult);
extern uint8_t genuineFMSKey[];
bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme);

View file

@ -5,12 +5,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> //for memcmp #include <string.h> //for memcmp
#include <arpa/inet.h> //for htonl/ntohl #include <arpa/inet.h> //for htonl/ntohl
char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Header[] = "DTSC";
char DTSC::Magic_Packet[] = "DTPD"; char DTSC::Magic_Packet[] = "DTPD";
/// Initializes a DTSC::Stream with only one packet buffer. /// Initializes a DTSC::Stream with only one packet buffer.
DTSC::Stream::Stream(){ DTSC::Stream::Stream(){
datapointertype = DTSC::INVALID;
datapointer = 0; datapointer = 0;
buffercount = 1; buffercount = 1;
} }
@ -18,8 +18,11 @@ DTSC::Stream::Stream(){
/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. /// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers.
/// The actual buffer count may not at all times be the requested amount. /// The actual buffer count may not at all times be the requested amount.
DTSC::Stream::Stream(unsigned int rbuffers){ DTSC::Stream::Stream(unsigned int rbuffers){
datapointertype = DTSC::INVALID;
datapointer = 0; datapointer = 0;
if (rbuffers < 1){rbuffers = 1;} if (rbuffers < 1){
rbuffers = 1;
}
buffercount = rbuffers; buffercount = rbuffers;
} }
@ -39,15 +42,19 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
if (buffer.length() > 8){ if (buffer.length() > 8){
if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){
len = ntohl(((uint32_t *)buffer.c_str())[1]); len = ntohl(((uint32_t *)buffer.c_str())[1]);
if (buffer.length() < len+8){return false;} if (buffer.length() < len + 8){
return false;
}
unsigned int i = 0; unsigned int i = 0;
metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i);
buffer.erase(0, len+8); buffer.erase(0, len + 8);
return false; return false;
} }
if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){
len = ntohl(((uint32_t *)buffer.c_str())[1]); len = ntohl(((uint32_t *)buffer.c_str())[1]);
if (buffer.length() < len+8){return false;} if (buffer.length() < len + 8){
return false;
}
buffers.push_front(JSON::Value()); buffers.push_front(JSON::Value());
unsigned int i = 0; unsigned int i = 0;
buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i);
@ -59,23 +66,33 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
} }
if (buffers.front().isMember("datatype")){ if (buffers.front().isMember("datatype")){
std::string tmp = buffers.front()["datatype"].asString(); std::string tmp = buffers.front()["datatype"].asString();
if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "video"){
if (tmp == "audio"){datapointertype = AUDIO;} datapointertype = VIDEO;
if (tmp == "meta"){datapointertype = META;} }
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} if (tmp == "audio"){
datapointertype = AUDIO;
}
if (tmp == "meta"){
datapointertype = META;
}
if (tmp == "pause_marker"){
datapointertype = PAUSEMARK;
}
}
buffer.erase(0, len + 8);
while (buffers.size() > buffercount){
buffers.pop_back();
} }
buffer.erase(0, len+8);
while (buffers.size() > buffercount){buffers.pop_back();}
advanceRings(); advanceRings();
syncing = false; syncing = false;
return true; return true;
} }
#if DEBUG >= 2 #if DEBUG >= 2
if (!syncing){ if (!syncing){
std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl;
syncing = true; syncing = true;
} }
#endif #endif
size_t magic_search = buffer.find(Magic_Packet); size_t magic_search = buffer.find(Magic_Packet);
if (magic_search == std::string::npos){ if (magic_search == std::string::npos){
buffer.clear(); buffer.clear();
@ -97,18 +114,22 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
std::string header_bytes = buffer.copy(8); std::string header_bytes = buffer.copy(8);
if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){ if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){
len = ntohl(((uint32_t *)header_bytes.c_str())[1]); len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
if (!buffer.available(len+8)){return false;} if ( !buffer.available(len + 8)){
return false;
}
unsigned int i = 0; unsigned int i = 0;
std::string wholepacket = buffer.remove(len+8); std::string wholepacket = buffer.remove(len + 8);
metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i);
return false; return false;
} }
if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){
len = ntohl(((uint32_t *)header_bytes.c_str())[1]); len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
if (!buffer.available(len+8)){return false;} if ( !buffer.available(len + 8)){
return false;
}
buffers.push_front(JSON::Value()); buffers.push_front(JSON::Value());
unsigned int i = 0; unsigned int i = 0;
std::string wholepacket = buffer.remove(len+8); std::string wholepacket = buffer.remove(len + 8);
buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i);
datapointertype = INVALID; datapointertype = INVALID;
if (buffers.front().isMember("data")){ if (buffers.front().isMember("data")){
@ -118,22 +139,32 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
} }
if (buffers.front().isMember("datatype")){ if (buffers.front().isMember("datatype")){
std::string tmp = buffers.front()["datatype"].asString(); std::string tmp = buffers.front()["datatype"].asString();
if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "video"){
if (tmp == "audio"){datapointertype = AUDIO;} datapointertype = VIDEO;
if (tmp == "meta"){datapointertype = META;} }
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} if (tmp == "audio"){
datapointertype = AUDIO;
}
if (tmp == "meta"){
datapointertype = META;
}
if (tmp == "pause_marker"){
datapointertype = PAUSEMARK;
}
}
while (buffers.size() > buffercount){
buffers.pop_back();
} }
while (buffers.size() > buffercount){buffers.pop_back();}
advanceRings(); advanceRings();
syncing = false; syncing = false;
return true; return true;
} }
#if DEBUG >= 2 #if DEBUG >= 2
if (!syncing){ if (!syncing){
std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl; std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl;
syncing = true; syncing = true;
} }
#endif #endif
buffer.get().clear(); buffer.get().clear();
} }
return false; return false;
@ -185,19 +216,30 @@ void DTSC::Stream::advanceRings(){
std::deque<DTSC::Ring>::iterator dit; std::deque<DTSC::Ring>::iterator dit;
std::set<DTSC::Ring *>::iterator sit; std::set<DTSC::Ring *>::iterator sit;
for (sit = rings.begin(); sit != rings.end(); sit++){ for (sit = rings.begin(); sit != rings.end(); sit++){
(*sit)->b++; ( *sit)->b++;
if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} if (( *sit)->waiting){
if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;} ( *sit)->waiting = false;
( *sit)->b = 0;
}
if (( *sit)->starved || (( *sit)->b >= buffers.size())){
( *sit)->starved = true;
( *sit)->b = 0;
}
} }
for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ for (dit = keyframes.begin(); dit != keyframes.end(); dit++){
dit->b++; dit->b++;
if (dit->b >= buffers.size()){keyframes.erase(dit); break;} if (dit->b >= buffers.size()){
keyframes.erase(dit);
break;
}
} }
if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){
keyframes.push_front(DTSC::Ring(0)); keyframes.push_front(DTSC::Ring(0));
} }
//increase buffer size if no keyframes available //increase buffer size if no keyframes available
if ((buffercount > 1) && (keyframes.size() < 1)){buffercount++;} if ((buffercount > 1) && (keyframes.size() < 1)){
buffercount++;
}
} }
/// Constructs a new Ring, at the given buffer position. /// Constructs a new Ring, at the given buffer position.
@ -235,7 +277,9 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){
/// Drops all Ring classes that have been given out. /// Drops all Ring classes that have been given out.
DTSC::Stream::~Stream(){ DTSC::Stream::~Stream(){
std::set<DTSC::Ring *>::iterator sit; std::set<DTSC::Ring *>::iterator sit;
for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} for (sit = rings.begin(); sit != rings.end(); sit++){
delete ( *sit);
}
} }
/// Open a filename for DTSC reading/writing. /// Open a filename for DTSC reading/writing.
@ -247,12 +291,12 @@ DTSC::File::File(std::string filename, bool create){
fseek(F, 0, SEEK_SET); fseek(F, 0, SEEK_SET);
fwrite(DTSC::Magic_Header, 4, 1, F); fwrite(DTSC::Magic_Header, 4, 1, F);
memset(buffer, 0, 4); memset(buffer, 0, 4);
fwrite(buffer, 4, 1, F);//write 4 zero-bytes fwrite(buffer, 4, 1, F); //write 4 zero-bytes
headerSize = 0; headerSize = 0;
}else{ }else{
F = fopen(filename.c_str(), "r+b"); F = fopen(filename.c_str(), "r+b");
} }
if (!F){ if ( !F){
fprintf(stderr, "Could not open file %s\n", filename.c_str()); fprintf(stderr, "Could not open file %s\n", filename.c_str());
return; return;
} }
@ -262,15 +306,15 @@ DTSC::File::File(std::string filename, bool create){
if (fread(buffer, 4, 1, F) != 1){ if (fread(buffer, 4, 1, F) != 1){
fseek(F, 4, SEEK_SET); fseek(F, 4, SEEK_SET);
memset(buffer, 0, 4); memset(buffer, 0, 4);
fwrite(buffer, 4, 1, F);//write 4 zero-bytes fwrite(buffer, 4, 1, F); //write 4 zero-bytes
}else{ }else{
uint32_t * ubuffer = (uint32_t *)buffer; uint32_t * ubuffer = (uint32_t *)buffer;
headerSize = ntohl(ubuffer[0]); headerSize = ntohl(ubuffer[0]);
} }
readHeader(0); readHeader(0);
fseek(F, 8+headerSize, SEEK_SET); fseek(F, 8 + headerSize, SEEK_SET);
currframe = 1; currframe = 1;
frames[1] = 8+headerSize; frames[1] = 8 + headerSize;
msframes[1] = 0; msframes[1] = 0;
} }
@ -289,7 +333,7 @@ bool DTSC::File::writeHeader(std::string & header, bool force){
headerSize = header.size(); headerSize = header.size();
fseek(F, 8, SEEK_SET); fseek(F, 8, SEEK_SET);
int ret = fwrite(header.c_str(), headerSize, 1, F); int ret = fwrite(header.c_str(), headerSize, 1, F);
fseek(F, 8+headerSize, SEEK_SET); fseek(F, 8 + headerSize, SEEK_SET);
return (ret == 1); return (ret == 1);
} }
@ -299,13 +343,19 @@ long long int DTSC::File::addHeader(std::string & header){
fseek(F, 0, SEEK_END); fseek(F, 0, SEEK_END);
long long int writePos = ftell(F); long long int writePos = ftell(F);
int hSize = htonl(header.size()); int hSize = htonl(header.size());
int ret = fwrite(DTSC::Magic_Header, 4, 1, F);//write header int ret = fwrite(DTSC::Magic_Header, 4, 1, F); //write header
if (ret != 1){return 0;} if (ret != 1){
ret = fwrite((void*)(&hSize), 4, 1, F);//write size return 0;
if (ret != 1){return 0;} }
ret = fwrite(header.c_str(), header.size(), 1, F);//write contents ret = fwrite((void*)( &hSize), 4, 1, F); //write size
if (ret != 1){return 0;} if (ret != 1){
return writePos;//return position written at return 0;
}
ret = fwrite(header.c_str(), header.size(), 1, F); //write contents
if (ret != 1){
return 0;
}
return writePos; //return position written at
} }
/// Reads the header at the given file position. /// Reads the header at the given file position.
@ -315,9 +365,9 @@ void DTSC::File::readHeader(int pos){
fseek(F, pos, SEEK_SET); fseek(F, pos, SEEK_SET);
if (fread(buffer, 4, 1, F) != 1){ if (fread(buffer, 4, 1, F) != 1){
if (feof(F)){ if (feof(F)){
#if DEBUG >= 4 #if DEBUG >= 4
fprintf(stderr, "End of file reached (H%i)\n", pos); fprintf(stderr, "End of file reached (H%i)\n", pos);
#endif #endif
}else{ }else{
fprintf(stderr, "Could not read header (H%i)\n", pos); fprintf(stderr, "Could not read header (H%i)\n", pos);
} }
@ -355,13 +405,13 @@ void DTSC::File::readHeader(int pos){
if (metadata.isMember("keytime")){ if (metadata.isMember("keytime")){
msframes.clear(); msframes.clear();
for (int i = 0; i < metadata["keytime"].size(); ++i){ for (int i = 0; i < metadata["keytime"].size(); ++i){
msframes[i+1] = metadata["keytime"][i].asInt(); msframes[i + 1] = metadata["keytime"][i].asInt();
} }
} }
if (metadata.isMember("keybpos")){ if (metadata.isMember("keybpos")){
frames.clear(); frames.clear();
for (int i = 0; i < metadata["keybpos"].size(); ++i){ for (int i = 0; i < metadata["keybpos"].size(); ++i){
frames[i+1] = metadata["keybpos"][i].asInt(); frames[i + 1] = metadata["keybpos"][i].asInt();
} }
} }
} }
@ -373,9 +423,9 @@ void DTSC::File::seekNext(){
lastreadpos = ftell(F); lastreadpos = ftell(F);
if (fread(buffer, 4, 1, F) != 1){ if (fread(buffer, 4, 1, F) != 1){
if (feof(F)){ if (feof(F)){
#if DEBUG >= 4 #if DEBUG >= 4
fprintf(stderr, "End of file reached.\n"); fprintf(stderr, "End of file reached.\n");
#endif #endif
}else{ }else{
fprintf(stderr, "Could not read header\n"); fprintf(stderr, "Could not read header\n");
} }
@ -409,13 +459,13 @@ void DTSC::File::seekNext(){
if (frames[currframe] != lastreadpos){ if (frames[currframe] != lastreadpos){
currframe++; currframe++;
currtime = jsonbuffer["time"].asInt(); currtime = jsonbuffer["time"].asInt();
#if DEBUG >= 6 #if DEBUG >= 6
if (frames[currframe] != lastreadpos){ if (frames[currframe] != lastreadpos){
std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl;
}else{ } else{
std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl;
} }
#endif #endif
frames[currframe] = lastreadpos; frames[currframe] = lastreadpos;
msframes[currframe] = currtime; msframes[currframe] = currtime;
} }
@ -423,36 +473,47 @@ void DTSC::File::seekNext(){
} }
/// Returns the byte positon of the start of the last packet that was read. /// Returns the byte positon of the start of the last packet that was read.
long long int DTSC::File::getLastReadPos(){return lastreadpos;} long long int DTSC::File::getLastReadPos(){
return lastreadpos;
}
/// Returns the internal buffer of the last read packet in raw binary format. /// Returns the internal buffer of the last read packet in raw binary format.
std::string & DTSC::File::getPacket(){return strbuffer;} std::string & DTSC::File::getPacket(){
return strbuffer;
}
/// Returns the internal buffer of the last read packet in JSON format. /// Returns the internal buffer of the last read packet in JSON format.
JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} JSON::Value & DTSC::File::getJSON(){
return jsonbuffer;
}
/// Attempts to seek to the given frame number within the file. /// Attempts to seek to the given frame number within the file.
/// Returns true if successful, false otherwise. /// Returns true if successful, false otherwise.
bool DTSC::File::seek_frame(int frameno){ bool DTSC::File::seek_frame(int frameno){
if (frames.count(frameno) > 0){ if (frames.count(frameno) > 0){
if (fseek(F, frames[frameno], SEEK_SET) == 0){ if (fseek(F, frames[frameno], SEEK_SET) == 0){
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl; std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl;
#endif #endif
currframe = frameno; currframe = frameno;
return true; return true;
} }
}else{ }else{
for (int i = frameno; i >= 1; --i){ for (int i = frameno; i >= 1; --i){
if (frames.count(i) > 0){currframe = i; break;} if (frames.count(i) > 0){
currframe = i;
break;
}
} }
if (fseek(F, frames[currframe], SEEK_SET) == 0){ if (fseek(F, frames[currframe], SEEK_SET) == 0){
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl;
#endif #endif
while (currframe < frameno){ while (currframe < frameno){
seekNext(); seekNext();
if (jsonbuffer.isNull()){return false;} if (jsonbuffer.isNull()){
return false;
}
} }
seek_frame(frameno); seek_frame(frameno);
return true; return true;
@ -468,16 +529,23 @@ bool DTSC::File::seek_time(int ms){
currtime = 0; currtime = 0;
currframe = 1; currframe = 1;
for (it = msframes.begin(); it != msframes.end(); ++it){ for (it = msframes.begin(); it != msframes.end(); ++it){
if (it->second > ms){break;} if (it->second > ms){
if (it->second > currtime){currtime = it->second; currframe = it->first;} break;
}
if (it->second > currtime){
currtime = it->second;
currframe = it->first;
}
} }
if (fseek(F, frames[currframe], SEEK_SET) == 0){ if (fseek(F, frames[currframe], SEEK_SET) == 0){
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl;
#endif #endif
while (currtime < ms){ while (currtime < ms){
seekNext(); seekNext();
if (jsonbuffer.isNull()){return false;} if (jsonbuffer.isNull()){
return false;
}
} }
if (currtime > ms){ if (currtime > ms){
return seek_frame(currframe - 1); return seek_frame(currframe - 1);

View file

@ -12,8 +12,6 @@
#include "json.h" #include "json.h"
#include "socket.h" #include "socket.h"
/// Holds all DDVTECH Stream Container classes and parsers. /// Holds all DDVTECH Stream Container classes and parsers.
///length (int, length in seconds, if available) ///length (int, length in seconds, if available)
///video: ///video:
@ -50,10 +48,10 @@
/// - nalu (int, if set, is a nalu) /// - nalu (int, if set, is a nalu)
/// - nalu_end (int, if set, is a end-of-sequence) /// - nalu_end (int, if set, is a end-of-sequence)
/// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) /// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames)
namespace DTSC{ namespace DTSC {
/// This enum holds all possible datatypes for DTSC packets. /// This enum holds all possible datatypes for DTSC packets.
enum datatype { enum datatype{
AUDIO, ///< Stream Audio data AUDIO, ///< Stream Audio data
VIDEO, ///< Stream Video data VIDEO, ///< Stream Video data
META, ///< Stream Metadata META, ///< Stream Metadata
@ -91,12 +89,12 @@ namespace DTSC{
FILE * F; FILE * F;
unsigned long headerSize; unsigned long headerSize;
char buffer[4]; char buffer[4];
};//FileWriter };
//FileWriter
/// A part from the DTSC::Stream ringbuffer. /// A part from the DTSC::Stream ringbuffer.
/// Holds information about a buffer that will stay consistent /// Holds information about a buffer that will stay consistent
class Ring { class Ring{
public: public:
Ring(unsigned int v); Ring(unsigned int v);
volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly!
@ -107,7 +105,7 @@ namespace DTSC{
/// Holds temporary data for a DTSC stream and provides functions to utilize it. /// Holds temporary data for a DTSC stream and provides functions to utilize it.
/// Optionally also acts as a ring buffer of a certain requested size. /// Optionally also acts as a ring buffer of a certain requested size.
/// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe. /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe.
class Stream { class Stream{
public: public:
Stream(); Stream();
~Stream(); ~Stream();
@ -125,7 +123,7 @@ namespace DTSC{
Ring * getRing(); Ring * getRing();
unsigned int getTime(); unsigned int getTime();
void dropRing(Ring * ptr); void dropRing(Ring * ptr);
private: private:
std::deque<JSON::Value> buffers; std::deque<JSON::Value> buffers;
std::set<DTSC::Ring *> rings; std::set<DTSC::Ring *> rings;
std::deque<DTSC::Ring> keyframes; std::deque<DTSC::Ring> keyframes;
@ -134,4 +132,4 @@ namespace DTSC{
datatype datapointertype; datatype datapointertype;
unsigned int buffercount; unsigned int buffercount;
}; };
}; }

View file

@ -1,215 +1,265 @@
#include "filesystem.h" #include "filesystem.h"
Filesystem::Directory::Directory(std::string PathName, std::string BasePath){
Filesystem::Directory::Directory( std::string PathName, std::string BasePath ) {
MyBase = BasePath; MyBase = BasePath;
if( PathName[0] == '/' ) { PathName.erase(0,1); } if (PathName[0] == '/'){
if( BasePath[BasePath.size()-1] != '/' ) { BasePath += "/"; } PathName.erase(0, 1);
}
if (BasePath[BasePath.size() - 1] != '/'){
BasePath += "/";
}
MyPath = PathName; MyPath = PathName;
FillEntries( ); FillEntries();
} }
Filesystem::Directory::~Directory( ) { } Filesystem::Directory::~Directory(){
}
void Filesystem::Directory::FillEntries( ) { void Filesystem::Directory::FillEntries(){
fprintf( stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str() ); fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str());
ValidDir = true; ValidDir = true;
struct stat StatBuf; struct stat StatBuf;
Entries.clear(); Entries.clear();
DIR * Dirp = opendir( (MyBase + MyPath).c_str() ); DIR * Dirp = opendir((MyBase + MyPath).c_str());
if( !Dirp ) { if ( !Dirp){
ValidDir = false; ValidDir = false;
} else { }else{
dirent * entry; dirent * entry;
while( entry = readdir( Dirp ) ) { while ((entry = readdir(Dirp))){
if( stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1 ) { if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){
fprintf( stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno) ); fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno));
continue; continue;
} }
///Convert stat to string ///Convert stat to string
Entries[ std::string( entry->d_name ) ] = StatBuf; Entries[std::string(entry->d_name)] = StatBuf;
} }
} }
fprintf( stderr, "Valid dir: %d\n", ValidDir ); fprintf(stderr, "Valid dir: %d\n", ValidDir);
fprintf( stderr, "#Entries: %d\n", Entries.size() ); fprintf(stderr, "#Entries: %d\n", Entries.size());
} }
void Filesystem::Directory::Print( ) { void Filesystem::Directory::Print(){
if( !ValidDir ) { if ( !ValidDir){
printf( "%s is not a valid directory\n", (MyBase + MyPath).c_str() ); printf("%s is not a valid directory\n", (MyBase + MyPath).c_str());
return; return;
} }
printf( "%s:\n", (MyBase + MyPath).c_str() ); printf("%s:\n", (MyBase + MyPath).c_str());
for( std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++ ) { for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){
printf( "\t%s\n", (*it).first.c_str() ); printf("\t%s\n", ( *it).first.c_str());
} }
printf( "\n" ); printf("\n");
} }
bool Filesystem::Directory::IsDir( ) { bool Filesystem::Directory::IsDir(){
return ValidDir; return ValidDir;
} }
std::string Filesystem::Directory::PWD( ) { std::string Filesystem::Directory::PWD(){
return "/" + MyPath; return "/" + MyPath;
} }
std::string Filesystem::Directory::LIST( std::vector<std::string> ActiveStreams ) { std::string Filesystem::Directory::LIST(std::vector<std::string> ActiveStreams){
FillEntries( ); FillEntries();
int MyPermissions; int MyPermissions;
std::stringstream Converter; std::stringstream Converter;
passwd* pwd;//For Username passwd* pwd; //For Username
group* grp;//For Groupname group* grp; //For Groupname
tm* tm;//For time localisation tm* tm; //For time localisation
char datestring[256];//For time localisation char datestring[256]; //For time localisation
std::string MyLoc = MyBase + MyPath; std::string MyLoc = MyBase + MyPath;
if( MyLoc[MyLoc.size()-1] != '/' ) { MyLoc += "/"; } if (MyLoc[MyLoc.size() - 1] != '/'){
MyLoc += "/";
for( std::map<std::string,struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++ ) { }
bool Active = ( std::find( ActiveStreams.begin(), ActiveStreams.end(), (*it).first ) != ActiveStreams.end() ); for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){
fprintf( stderr, "%s active?: %d\n", (*it).first.c_str(), Active );
fprintf( stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath] ); bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), ( *it).first) != ActiveStreams.end());
fprintf( stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE ); fprintf(stderr, "%s active?: %d\n", ( *it).first.c_str(), Active);
if( ( Active && ( MyVisible[MyPath] & S_ACTIVE ) ) || ( (!Active) && ( MyVisible[MyPath] & S_INACTIVE ) ) || ( ((*it).second.st_mode / 010000 ) == 4 ) ) { fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]);
if( ((*it).second.st_mode / 010000) == 4 ) { Converter << 'd'; } else { Converter << '-'; } fprintf(stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE);
MyPermissions = ( ( (*it).second.st_mode % 010000 ) / 0100 ); if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || (( !Active) && (MyVisible[MyPath] & S_INACTIVE)) || ((( *it).second.st_mode / 010000) == 4)){
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } if ((( *it).second.st_mode / 010000) == 4){
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } Converter << 'd';
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } }else{
MyPermissions = ( ( (*it).second.st_mode % 0100 ) / 010 ); Converter << '-';
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } }
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } MyPermissions = ((( *it).second.st_mode % 010000) / 0100);
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } if (MyPermissions & 4){
MyPermissions = ( (*it).second.st_mode % 010 ); Converter << 'r';
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } }else{
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } Converter << '-';
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } }
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
MyPermissions = ((( *it).second.st_mode % 0100) / 010);
if (MyPermissions & 4){
Converter << 'r';
}else{
Converter << '-';
}
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
MyPermissions = (( *it).second.st_mode % 010);
if (MyPermissions & 4){
Converter << 'r';
}else{
Converter << '-';
}
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
Converter << ' '; Converter << ' ';
Converter << (*it).second.st_nlink; Converter << ( *it).second.st_nlink;
Converter << ' '; Converter << ' ';
if( (pwd = getpwuid((*it).second.st_uid)) ) { if ((pwd = getpwuid(( *it).second.st_uid))){
Converter << pwd->pw_name; Converter << pwd->pw_name;
} else { }else{
Converter << (*it).second.st_uid; Converter << ( *it).second.st_uid;
} }
Converter << ' '; Converter << ' ';
if( (grp = getgrgid((*it).second.st_gid) ) ) { if ((grp = getgrgid(( *it).second.st_gid))){
Converter << grp->gr_name; Converter << grp->gr_name;
} else { }else{
Converter << (*it).second.st_gid; Converter << ( *it).second.st_gid;
} }
Converter << ' '; Converter << ' ';
Converter << (*it).second.st_size; Converter << ( *it).second.st_size;
Converter << ' '; Converter << ' ';
tm = localtime(&((*it).second.st_mtime)); tm = localtime( &(( *it).second.st_mtime));
strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm); strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm);
Converter << datestring; Converter << datestring;
Converter << ' '; Converter << ' ';
Converter << (*it).first; Converter << ( *it).first;
Converter << '\n'; Converter << '\n';
} }
} }
return Converter.str(); return Converter.str();
} }
bool Filesystem::Directory::CWD( std::string Path ) { bool Filesystem::Directory::CWD(std::string Path){
if( Path[0] == '/' ) { if (Path[0] == '/'){
Path.erase(0,1); Path.erase(0, 1);
MyPath = Path; MyPath = Path;
} else { }else{
if( MyPath != "" ) { if (MyPath != ""){
MyPath += "/"; MyPath += "/";
} }
MyPath += Path; MyPath += Path;
} }
FillEntries(); FillEntries();
printf( "New Path: %s\n", MyPath.c_str() ); printf("New Path: %s\n", MyPath.c_str());
if( MyPermissions.find( MyPath ) != MyPermissions.end() ) { if (MyPermissions.find(MyPath) != MyPermissions.end()){
printf( "\tPermissions: %d\n", MyPermissions[MyPath] ); printf("\tPermissions: %d\n", MyPermissions[MyPath]);
} }
return SimplifyPath( ); return SimplifyPath();
} }
bool Filesystem::Directory::CDUP( ) { bool Filesystem::Directory::CDUP(){
return CWD( ".." ); return CWD("..");
} }
std::string Filesystem::Directory::RETR( std::string Path ) { std::string Filesystem::Directory::RETR(std::string Path){
std::string Result; std::string Result;
std::string FileName; std::string FileName;
if( Path[0] == '/' ) { if (Path[0] == '/'){
Path.erase(0,1); Path.erase(0, 1);
FileName = MyBase + Path; FileName = MyBase + Path;
} else { }else{
FileName = MyBase + MyPath + "/" + Path; FileName = MyBase + MyPath + "/" + Path;
} }
std::ifstream File; std::ifstream File;
File.open( FileName.c_str() ); File.open(FileName.c_str());
while( File.good() ) { Result += File.get(); } while (File.good()){
Result += File.get();
}
File.close(); File.close();
return Result; return Result;
} }
void Filesystem::Directory::STOR( std::string Path, std::string Data ) { void Filesystem::Directory::STOR(std::string Path, std::string Data){
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_STOR ) ) { if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_STOR)){
std::string FileName; std::string FileName;
if( Path[0] == '/' ) { if (Path[0] == '/'){
Path.erase(0,1); Path.erase(0, 1);
FileName = MyBase + Path; FileName = MyBase + Path;
} else { }else{
FileName = MyBase + MyPath + "/" + Path; FileName = MyBase + MyPath + "/" + Path;
} }
std::ofstream File; std::ofstream File;
File.open( FileName.c_str() ); File.open(FileName.c_str());
File << Data; File << Data;
File.close(); File.close();
} }
} }
bool Filesystem::Directory::SimplifyPath( ) { bool Filesystem::Directory::SimplifyPath(){
MyPath += "/"; MyPath += "/";
fprintf( stderr, "MyPath: %s\n", MyPath.c_str() ); fprintf(stderr, "MyPath: %s\n", MyPath.c_str());
std::vector<std::string> TempPath; std::vector<std::string> TempPath;
std::string TempString; std::string TempString;
for( std::string::iterator it = MyPath.begin(); it != MyPath.end(); it ++ ) { for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++){
if( (*it) == '/' ) { if (( *it) == '/'){
if( TempString == ".." ) { if (TempString == ".."){
if( !TempPath.size() ) { if ( !TempPath.size()){
return false; return false;
} }
TempPath.erase( (TempPath.end()-1) ); TempPath.erase((TempPath.end() - 1));
} else if ( TempString != "." && TempString != "" ) { }else if (TempString != "." && TempString != ""){
TempPath.push_back( TempString ); TempPath.push_back(TempString);
} }
TempString = ""; TempString = "";
} else { }else{
TempString += (*it); TempString += ( *it);
} }
} }
MyPath = ""; MyPath = "";
for( std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++ ) { for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++){
MyPath += (*it); MyPath += ( *it);
if( it != ( TempPath.end() - 1 ) ) { MyPath += "/"; } if (it != (TempPath.end() - 1)){
MyPath += "/";
}
} }
if( MyVisible.find( MyPath ) == MyVisible.end() ) { if (MyVisible.find(MyPath) == MyVisible.end()){
MyVisible[MyPath] = S_ALL; MyVisible[MyPath] = S_ALL;
} }
return true; return true;
} }
bool Filesystem::Directory::DELE( std::string Path ) { bool Filesystem::Directory::DELE(std::string Path){
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_DELE ) ) { if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_DELE)){
std::string FileName; std::string FileName;
if( Path[0] == '/' ) { if (Path[0] == '/'){
Path.erase(0,1); Path.erase(0, 1);
FileName = MyBase + Path; FileName = MyBase + Path;
} else { }else{
FileName = MyBase + MyPath + "/" + Path; FileName = MyBase + MyPath + "/" + Path;
} }
if( std::remove( FileName.c_str() ) ) { if (std::remove(FileName.c_str())){
fprintf( stderr, "Removing file %s unsuccesfull\n", FileName.c_str() ); fprintf(stderr, "Removing file %s unsuccesfull\n", FileName.c_str());
return false; return false;
} }
return true; return true;
@ -217,39 +267,39 @@ bool Filesystem::Directory::DELE( std::string Path ) {
return false; return false;
} }
bool Filesystem::Directory::MKD( std::string Path ) { bool Filesystem::Directory::MKD(std::string Path){
std::string FileName; std::string FileName;
if( Path[0] == '/' ) { if (Path[0] == '/'){
Path.erase(0,1); Path.erase(0, 1);
FileName = MyBase + Path; FileName = MyBase + Path;
} else { }else{
FileName = MyBase + MyPath + "/" + Path; FileName = MyBase + MyPath + "/" + Path;
} }
if( mkdir( FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) ) { if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)){
fprintf( stderr, "Creating directory %s unsuccesfull\n", FileName.c_str() ); fprintf(stderr, "Creating directory %s unsuccesfull\n", FileName.c_str());
return false; return false;
} }
MyVisible[FileName] = S_ALL; MyVisible[FileName] = S_ALL;
return true; return true;
} }
bool Filesystem::Directory::Rename( std::string From, std::string To ) { bool Filesystem::Directory::Rename(std::string From, std::string To){
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_RNFT ) ) { if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_RNFT)){
std::string FileFrom; std::string FileFrom;
if( From[0] == '/' ) { if (From[0] == '/'){
From.erase(0,1); From.erase(0, 1);
FileFrom = MyBase + From; FileFrom = MyBase + From;
} else { }else{
FileFrom = MyBase + MyPath + "/" + From; FileFrom = MyBase + MyPath + "/" + From;
} }
std::string FileTo; std::string FileTo;
if( To[0] == '/' ) { if (To[0] == '/'){
FileTo = MyBase + To; FileTo = MyBase + To;
} else { }else{
FileTo = MyBase + MyPath + "/" + To; FileTo = MyBase + MyPath + "/" + To;
} }
if( std::rename( FileFrom.c_str(), FileTo.c_str() ) ) { if (std::rename(FileFrom.c_str(), FileTo.c_str())){
fprintf( stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str() ); fprintf(stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str());
return false; return false;
} }
return true; return true;
@ -257,17 +307,17 @@ bool Filesystem::Directory::Rename( std::string From, std::string To ) {
return false; return false;
} }
void Filesystem::Directory::SetPermissions( std::string Path, char Permissions ) { void Filesystem::Directory::SetPermissions(std::string Path, char Permissions){
MyPermissions[Path] = Permissions; MyPermissions[Path] = Permissions;
} }
bool Filesystem::Directory::HasPermission( char Permission ) { bool Filesystem::Directory::HasPermission(char Permission){
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & Permission ) ) { if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & Permission)){
return true; return true;
} }
return false; return false;
} }
void Filesystem::Directory::SetVisibility( std::string Pathname, char Visible ) { void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible){
MyVisible[Pathname] = Visible; MyVisible[Pathname] = Visible;
} }

View file

@ -21,49 +21,47 @@
#include <errno.h> #include <errno.h>
namespace Filesystem { namespace Filesystem {
enum DIR_Permissions { enum DIR_Permissions{
P_LIST = 0x01,//List P_LIST = 0x01, //List
P_RETR = 0x02,//Retrieve P_RETR = 0x02, //Retrieve
P_STOR = 0x04,//Store P_STOR = 0x04, //Store
P_RNFT = 0x08,//Rename From/To P_RNFT = 0x08, //Rename From/To
P_DELE = 0x10,//Delete P_DELE = 0x10, //Delete
P_MKD = 0x20,//Make directory P_MKD = 0x20, //Make directory
P_RMD = 0x40,//Remove directory P_RMD = 0x40, //Remove directory
}; };
enum DIR_Show { enum DIR_Show{
S_NONE = 0x00, S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03,
S_ACTIVE = 0x01,
S_INACTIVE = 0x02,
S_ALL = 0x03,
}; };
class Directory { class Directory{
public: public:
Directory( std::string PathName = "", std::string BasePath = "."); Directory(std::string PathName = "", std::string BasePath = ".");
~Directory( ); ~Directory();
void Print( ); void Print();
bool IsDir( ); bool IsDir();
std::string PWD( ); std::string PWD();
std::string LIST( std::vector<std::string> ActiveStreams = std::vector<std::string>() ); std::string LIST(std::vector<std::string> ActiveStreams = std::vector<std::string>());
bool CWD( std::string Path ); bool CWD(std::string Path);
bool CDUP( ); bool CDUP();
bool DELE( std::string Path ); bool DELE(std::string Path);
bool MKD( std::string Path ); bool MKD(std::string Path);
std::string RETR( std::string Path ); std::string RETR(std::string Path);
void STOR( std::string Path, std::string Data ); void STOR(std::string Path, std::string Data);
bool Rename( std::string From, std::string To ); bool Rename(std::string From, std::string To);
void SetPermissions( std::string PathName, char Permissions ); void SetPermissions(std::string PathName, char Permissions);
bool HasPermission( char Permission ); bool HasPermission(char Permission);
void SetVisibility( std::string Pathname, char Visible ); void SetVisibility(std::string Pathname, char Visible);
private: private:
bool ValidDir; bool ValidDir;
bool SimplifyPath( ); bool SimplifyPath();
void FillEntries( ); void FillEntries();
std::string MyBase; std::string MyBase;
std::string MyPath; std::string MyPath;
std::map< std::string, struct stat > Entries; std::map<std::string, struct stat> Entries;
std::map< std::string, char > MyPermissions; std::map<std::string, char> MyPermissions;
std::map< std::string, char > MyVisible; std::map<std::string, char> MyVisible;
};//Directory Class };
};//Filesystem namespace //Directory Class
}//Filesystem namespace

File diff suppressed because it is too large Load diff

View file

@ -7,10 +7,11 @@
#include "json.h" #include "json.h"
#include <string> #include <string>
//forward declaration of RTMPStream::Chunk to avoid circular dependencies. //forward declaration of RTMPStream::Chunk to avoid circular dependencies.
namespace RTMPStream{ namespace RTMPStream {
class Chunk; class Chunk;
}; }
/// This namespace holds all FLV-parsing related functionality. /// This namespace holds all FLV-parsing related functionality.
namespace FLV { namespace FLV {
@ -24,7 +25,7 @@ namespace FLV {
bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV". bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV".
/// This class is used to hold, work with and get information about a single FLV tag. /// This class is used to hold, work with and get information about a single FLV tag.
class Tag { class Tag{
public: public:
int len; ///< Actual length of tag. int len; ///< Actual length of tag.
bool isKeyframe; ///< True if current tag is a video keyframe. bool isKeyframe; ///< True if current tag is a video keyframe.
@ -36,7 +37,7 @@ namespace FLV {
void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag.
Tag(); ///< Constructor for a new, empty, tag. Tag(); ///< Constructor for a new, empty, tag.
Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag. Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag.
Tag & operator= (const Tag& O); ///< Assignment operator - works exactly like the copy constructor. Tag & operator=(const Tag& O); ///< Assignment operator - works exactly like the copy constructor.
Tag(const RTMPStream::Chunk& O); ///<Copy constructor from a RTMP chunk. Tag(const RTMPStream::Chunk& O); ///<Copy constructor from a RTMP chunk.
//loader functions //loader functions
bool ChunkLoader(const RTMPStream::Chunk& O); bool ChunkLoader(const RTMPStream::Chunk& O);
@ -59,6 +60,7 @@ namespace FLV {
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val); void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val);
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val); void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val);
bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem); bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem);
};//Tag };
//Tag
};//FLV namespace }//FLV namespace

View file

@ -1,7 +1,8 @@
#include "ftp.h" #include "ftp.h"
FTP::User::User( Socket::Connection NewConnection, std::map<std::string,std::string> Credentials ) { FTP::User::User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials){
Conn = NewConnection; Conn = NewConnection;
MyPassivePort = 0;
USER = ""; USER = "";
PASS = ""; PASS = "";
MODE = MODE_STREAM; MODE = MODE_STREAM;
@ -10,78 +11,150 @@ FTP::User::User( Socket::Connection NewConnection, std::map<std::string,std::str
PORT = 20; PORT = 20;
RNFR = ""; RNFR = "";
AllCredentials = Credentials; AllCredentials = Credentials;
MyDir = Filesystem::Directory( "", FTPBasePath ); MyDir = Filesystem::Directory("", FTPBasePath);
MyDir.SetPermissions( "", Filesystem::P_LIST ); MyDir.SetPermissions("", Filesystem::P_LIST);
MyDir.SetPermissions( "Unconverted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_STOR | Filesystem::P_RETR ); MyDir.SetPermissions("Unconverted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_STOR | Filesystem::P_RETR);
MyDir.SetPermissions( "Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR ); MyDir.SetPermissions("Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR);
MyDir.SetPermissions( "OnDemand", Filesystem::P_LIST | Filesystem::P_RETR ); MyDir.SetPermissions("OnDemand", Filesystem::P_LIST | Filesystem::P_RETR);
MyDir.SetPermissions( "Live", Filesystem::P_LIST ); MyDir.SetPermissions("Live", Filesystem::P_LIST);
MyDir.SetVisibility( "Converted", Filesystem::S_INACTIVE ); MyDir.SetVisibility("Converted", Filesystem::S_INACTIVE);
MyDir.SetVisibility( "OnDemand", Filesystem::S_ACTIVE ); MyDir.SetVisibility("OnDemand", Filesystem::S_ACTIVE);
JSON::Value MyConfig = JSON::fromFile( "/tmp/mist/streamlist" ); JSON::Value MyConfig = JSON::fromFile("/tmp/mist/streamlist");
fprintf( stderr, "Streamamount: %d\n", MyConfig["streams"].size() ); fprintf(stderr, "Streamamount: %d\n", MyConfig["streams"].size());
for( JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++ ) { for (JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++){
std::string ThisStream = (*it).second["channel"]["URL"].toString(); std::string ThisStream = ( *it).second["channel"]["URL"].toString();
ThisStream.erase( ThisStream.begin() ); ThisStream.erase(ThisStream.begin());
ThisStream.erase( ThisStream.end() - 1 ); ThisStream.erase(ThisStream.end() - 1);
while( ThisStream.find( '/' ) != std::string::npos ) { while (ThisStream.find('/') != std::string::npos){
ThisStream.erase(0,ThisStream.find('/')+1); ThisStream.erase(0, ThisStream.find('/') + 1);
} }
ActiveStreams.push_back( ThisStream ); ActiveStreams.push_back(ThisStream);
fprintf( stderr, "\t%s\n", ThisStream.c_str() ); fprintf(stderr, "\t%s\n", ThisStream.c_str());
} }
} }
FTP::User::~User( ) { } FTP::User::~User(){
}
int FTP::User::ParseCommand( std::string Command ) { int FTP::User::ParseCommand(std::string Command){
Commands ThisCmd = CMD_NOCMD; Commands ThisCmd = CMD_NOCMD;
if( Command.substr(0,4) == "NOOP" ) { ThisCmd = CMD_NOOP; Command.erase(0,5); } if (Command.substr(0, 4) == "NOOP"){
if( Command.substr(0,4) == "USER" ) { ThisCmd = CMD_USER; Command.erase(0,5); } ThisCmd = CMD_NOOP;
if( Command.substr(0,4) == "PASS" ) { ThisCmd = CMD_PASS; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "QUIT" ) { ThisCmd = CMD_QUIT; Command.erase(0,5); } }
if( Command.substr(0,4) == "PORT" ) { ThisCmd = CMD_PORT; Command.erase(0,5); } if (Command.substr(0, 4) == "USER"){
if( Command.substr(0,4) == "RETR" ) { ThisCmd = CMD_RETR; Command.erase(0,5); } ThisCmd = CMD_USER;
if( Command.substr(0,4) == "STOR" ) { ThisCmd = CMD_STOR; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "TYPE" ) { ThisCmd = CMD_TYPE; Command.erase(0,5); } }
if( Command.substr(0,4) == "MODE" ) { ThisCmd = CMD_MODE; Command.erase(0,5); } if (Command.substr(0, 4) == "PASS"){
if( Command.substr(0,4) == "STRU" ) { ThisCmd = CMD_STRU; Command.erase(0,5); } ThisCmd = CMD_PASS;
if( Command.substr(0,4) == "EPSV" ) { ThisCmd = CMD_EPSV; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "PASV" ) { ThisCmd = CMD_PASV; Command.erase(0,5); } }
if( Command.substr(0,4) == "LIST" ) { ThisCmd = CMD_LIST; Command.erase(0,5); } if (Command.substr(0, 4) == "QUIT"){
if( Command.substr(0,4) == "CDUP" ) { ThisCmd = CMD_CDUP; Command.erase(0,5); } ThisCmd = CMD_QUIT;
if( Command.substr(0,4) == "DELE" ) { ThisCmd = CMD_DELE; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "RNFR" ) { ThisCmd = CMD_RNFR; Command.erase(0,5); } }
if( Command.substr(0,4) == "RNTO" ) { ThisCmd = CMD_RNTO; Command.erase(0,5); } if (Command.substr(0, 4) == "PORT"){
if( Command.substr(0,3) == "PWD" ) { ThisCmd = CMD_PWD; Command.erase(0,4); } ThisCmd = CMD_PORT;
if( Command.substr(0,3) == "CWD" ) { ThisCmd = CMD_CWD; Command.erase(0,4); } Command.erase(0, 5);
if( Command.substr(0,3) == "RMD" ) { ThisCmd = CMD_RMD; Command.erase(0,4); } }
if( Command.substr(0,3) == "MKD" ) { ThisCmd = CMD_MKD; Command.erase(0,4); } if (Command.substr(0, 4) == "RETR"){
if( ThisCmd != CMD_RNTO ) { RNFR = ""; } ThisCmd = CMD_RETR;
switch( ThisCmd ) { Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STOR"){
ThisCmd = CMD_STOR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "TYPE"){
ThisCmd = CMD_TYPE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "MODE"){
ThisCmd = CMD_MODE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STRU"){
ThisCmd = CMD_STRU;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "EPSV"){
ThisCmd = CMD_EPSV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PASV"){
ThisCmd = CMD_PASV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "LIST"){
ThisCmd = CMD_LIST;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "CDUP"){
ThisCmd = CMD_CDUP;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "DELE"){
ThisCmd = CMD_DELE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNFR"){
ThisCmd = CMD_RNFR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNTO"){
ThisCmd = CMD_RNTO;
Command.erase(0, 5);
}
if (Command.substr(0, 3) == "PWD"){
ThisCmd = CMD_PWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "CWD"){
ThisCmd = CMD_CWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "RMD"){
ThisCmd = CMD_RMD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "MKD"){
ThisCmd = CMD_MKD;
Command.erase(0, 4);
}
if (ThisCmd != CMD_RNTO){
RNFR = "";
}
switch (ThisCmd){
case CMD_NOOP: { case CMD_NOOP: {
return 200;//Command okay. return 200; //Command okay.
break; break;
} }
case CMD_USER: { case CMD_USER: {
USER = ""; USER = "";
PASS = ""; PASS = "";
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
USER = Command; USER = Command;
return 331;//User name okay, need password. return 331; //User name okay, need password.
break; break;
} }
case CMD_PASS: { case CMD_PASS: {
if( USER == "" ) { return 503; }//Bad sequence of commands if (USER == ""){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 503;
} //Bad sequence of commands
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
PASS = Command; PASS = Command;
if( !LoggedIn( ) ) { if ( !LoggedIn()){
USER = ""; USER = "";
PASS =""; PASS = "";
return 530;//Not logged in. return 530; //Not logged in.
} }
return 230; return 230;
break; break;
@ -89,135 +162,184 @@ int FTP::User::ParseCommand( std::string Command ) {
case CMD_LIST: { case CMD_LIST: {
std::cout << "Listening on :" << MyPassivePort << "\n"; std::cout << "Listening on :" << MyPassivePort << "\n";
Socket::Connection Connected = Passive.accept(); Socket::Connection Connected = Passive.accept();
if( Connected.connected() ) { if (Connected.connected()){
Conn.Send( "125 Data connection already open; transfer starting.\n" ); Conn.Send("125 Data connection already open; transfer starting.\n");
} else { }else{
Conn.Send( "150 File status okay; about to open data connection.\n" ); Conn.Send("150 File status okay; about to open data connection.\n");
} }
while( !Connected.connected() ) { while ( !Connected.connected()){
Connected = Passive.accept(); Connected = Passive.accept();
} }
fprintf( stderr, "Sending LIST information\n" ); fprintf(stderr, "Sending LIST information\n");
std::string tmpstr = MyDir.LIST( ActiveStreams ); std::string tmpstr = MyDir.LIST(ActiveStreams);
Connected.Send( tmpstr ); Connected.Send(tmpstr);
Connected.close( ); Connected.close();
return 226; return 226;
break; break;
} }
case CMD_QUIT: { case CMD_QUIT: {
return 221;//Service closing control connection. Logged out if appropriate. return 221; //Service closing control connection. Logged out if appropriate.
break; break;
} }
case CMD_PORT: { case CMD_PORT: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
PORT = atoi( Command.c_str() ); } //Not logged in.
return 200;//Command okay. if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
PORT = atoi(Command.c_str());
return 200; //Command okay.
break; break;
} }
case CMD_EPSV: { case CMD_EPSV: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999); MyPassivePort = (rand() % 9999);
std::cout << ":" << MyPassivePort << "\n"; std::cout << ":" << MyPassivePort << "\n";
Passive = Socket::Server(MyPassivePort,"0.0.0.0",true); Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 229; return 229;
break; break;
} }
case CMD_PASV: { case CMD_PASV: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999) + 49152; MyPassivePort = (rand() % 9999) + 49152;
std::cout << ":" << MyPassivePort << "\n"; std::cout << ":" << MyPassivePort << "\n";
Passive = Socket::Server(MyPassivePort,"0.0.0.0",true); Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 227; return 227;
break; break;
} }
case CMD_RETR: { case CMD_RETR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_RETR ) ) { return 550; }//Access denied. } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_RETR)){
return 550;
} //Access denied.
std::cout << "Listening on :" << MyPassivePort << "\n"; std::cout << "Listening on :" << MyPassivePort << "\n";
Socket::Connection Connected = Passive.accept(); Socket::Connection Connected = Passive.accept();
if( Connected.connected() ) { if (Connected.connected()){
Conn.Send( "125 Data connection already open; transfer starting.\n" ); Conn.Send("125 Data connection already open; transfer starting.\n");
} else { }else{
Conn.Send( "150 File status okay; about to open data connection.\n" ); Conn.Send("150 File status okay; about to open data connection.\n");
} }
while( !Connected.connected() ) { while ( !Connected.connected()){
Connected = Passive.accept(); Connected = Passive.accept();
} }
fprintf( stderr, "Sending RETR information\n" ); fprintf(stderr, "Sending RETR information\n");
std::string tmpstr = MyDir.RETR( Command ); std::string tmpstr = MyDir.RETR(Command);
Connected.Send( tmpstr ); Connected.Send(tmpstr);
Connected.close(); Connected.close();
return 226; return 226;
break; break;
} }
case CMD_STOR: { case CMD_STOR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_STOR ) ) { return 550; }//Access denied. } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_STOR)){
return 550;
} //Access denied.
std::cout << "Listening on :" << MyPassivePort << "\n"; std::cout << "Listening on :" << MyPassivePort << "\n";
Socket::Connection Connected = Passive.accept(); Socket::Connection Connected = Passive.accept();
if( Connected.connected() ) { if (Connected.connected()){
Conn.Send( "125 Data connection already open; transfer starting.\n" ); Conn.Send("125 Data connection already open; transfer starting.\n");
} else { }else{
Conn.Send( "150 File status okay; about to open data connection.\n" ); Conn.Send("150 File status okay; about to open data connection.\n");
} }
while( !Connected.connected() ) { while ( !Connected.connected()){
Connected = Passive.accept(); Connected = Passive.accept();
} }
fprintf( stderr, "Reading STOR information\n" ); fprintf(stderr, "Reading STOR information\n");
std::string Buffer; std::string Buffer;
while( Connected.spool() ) { } while (Connected.spool()){
}
/// \todo Comment me back in. ^_^ /// \todo Comment me back in. ^_^
//Buffer = Connected.Received(); //Buffer = Connected.Received();
MyDir.STOR( Command, Buffer ); MyDir.STOR(Command, Buffer);
return 250; return 250;
break; break;
} }
case CMD_TYPE: { case CMD_TYPE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 && Command.size() != 3 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
switch( Command[0] ) { if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1 && Command.size() != 3){
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]){
case 'A': { case 'A': {
if( Command.size() > 1 ) { if (Command.size() > 1){
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. if (Command[1] != ' '){
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N'){
return 504;
} //Command not implemented for that parameter.
} }
TYPE = TYPE_ASCII_NONPRINT; TYPE = TYPE_ASCII_NONPRINT;
break; break;
} }
case 'I': { case 'I': {
if( Command.size() > 1 ) { if (Command.size() > 1){
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. if (Command[1] != ' '){
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N'){
return 504;
} //Command not implemented for that parameter.
} }
TYPE = TYPE_IMAGE_NONPRINT; TYPE = TYPE_IMAGE_NONPRINT;
break; break;
} }
default: { default: {
return 504;//Command not implemented for that parameter. return 504; //Command not implemented for that parameter.
break; break;
} }
} }
return 200;//Command okay. return 200; //Command okay.
break; break;
} }
case CMD_MODE: { case CMD_MODE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
if( Command[0] != 'S' ) { return 504; }//Command not implemented for that parameter. if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1){
return 501;
} //Syntax error in parameters or arguments.
if (Command[0] != 'S'){
return 504;
} //Command not implemented for that parameter.
MODE = MODE_STREAM; MODE = MODE_STREAM;
return 200;//Command okay. return 200; //Command okay.
break; break;
} }
case CMD_STRU: { case CMD_STRU: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
switch( Command[0] ) { if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1){
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]){
case 'F': { case 'F': {
STRU = STRU_FILE; STRU = STRU_FILE;
break; break;
@ -227,24 +349,30 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
default: { default: {
return 504;//Command not implemented for that parameter. return 504; //Command not implemented for that parameter.
break; break;
} }
} }
return 200;//Command okay. return 200; //Command okay.
break; break;
} }
case CMD_PWD: { case CMD_PWD: {
if( !LoggedIn( ) ) { return 550; }//Not logged in. if ( !LoggedIn()){
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. return 550;
return 2570;//257 -- 0 to indicate PWD over MKD } //Not logged in.
if (Command != ""){
return 501;
} //Syntax error in parameters or arguments.
return 2570; //257 -- 0 to indicate PWD over MKD
break; break;
} }
case CMD_CWD: { case CMD_CWD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
Filesystem::Directory TmpDir = MyDir; Filesystem::Directory TmpDir = MyDir;
if( TmpDir.CWD( Command ) ) { if (TmpDir.CWD(Command)){
if( TmpDir.IsDir( ) ) { if (TmpDir.IsDir()){
MyDir = TmpDir; MyDir = TmpDir;
return 250; return 250;
} }
@ -253,11 +381,15 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_CDUP: { case CMD_CDUP: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
} //Not logged in.
if (Command != ""){
return 501;
} //Syntax error in parameters or arguments.
Filesystem::Directory TmpDir = MyDir; Filesystem::Directory TmpDir = MyDir;
if( TmpDir.CDUP( ) ) { if (TmpDir.CDUP()){
if( TmpDir.IsDir( ) ) { if (TmpDir.IsDir()){
MyDir = TmpDir; MyDir = TmpDir;
return 250; return 250;
} }
@ -266,62 +398,98 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_DELE: { case CMD_DELE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.DELE( Command ) ) { return 550; } } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.DELE(Command)){
return 550;
}
return 250; return 250;
break; break;
} }
case CMD_RMD: { case CMD_RMD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_RMD ) ) { return 550; } } //Not logged in.
if( !MyDir.DELE( Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_RMD)){
return 550;
}
if ( !MyDir.DELE(Command)){
return 550;
}
return 250; return 250;
break; break;
} }
case CMD_MKD: { case CMD_MKD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_MKD ) ) { return 550; } } //Not logged in.
if( !MyDir.MKD( Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_MKD)){
return 550;
}
if ( !MyDir.MKD(Command)){
return 550;
}
return 2571; return 2571;
break; break;
} }
case CMD_RNFR: { case CMD_RNFR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
} //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
RNFR = Command; RNFR = Command;
return 350;//Awaiting further information return 350; //Awaiting further information
} }
case CMD_RNTO: { case CMD_RNTO: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( RNFR == "" ) { return 503; } //Bad sequence of commands } //Not logged in.
if( !MyDir.Rename( RNFR, Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (RNFR == ""){
return 503;
} //Bad sequence of commands
if ( !MyDir.Rename(RNFR, Command)){
return 550;
}
return 250; return 250;
} }
default: { default: {
return 502;//Command not implemented. return 502; //Command not implemented.
break; break;
} }
} }
} }
bool FTP::User::LoggedIn( ) { bool FTP::User::LoggedIn(){
if( USER == "" || PASS == "" ) { return false; } if (USER == "" || PASS == ""){
if( !AllCredentials.size() ) { return false;
}
if ( !AllCredentials.size()){
return true; return true;
} }
if( ( AllCredentials.find( USER ) != AllCredentials.end() ) && AllCredentials[USER] == PASS ) { if ((AllCredentials.find(USER) != AllCredentials.end()) && AllCredentials[USER] == PASS){
return true; return true;
} }
return false; return false;
} }
std::string FTP::User::NumToMsg( int MsgNum ) { std::string FTP::User::NumToMsg(int MsgNum){
std::string Result; std::string Result;
switch( MsgNum ) { switch (MsgNum){
case 200: { case 200: {
Result = "200 Message okay.\n"; Result = "200 Message okay.\n";
break; break;
@ -335,15 +503,15 @@ std::string FTP::User::NumToMsg( int MsgNum ) {
break; break;
} }
case 227: { case 227: {
std::stringstream sstr; std::stringstream sstr;
sstr << "227 Entering passive mode (0,0,0,0,"; sstr << "227 Entering passive mode (0,0,0,0,";
sstr << (MyPassivePort >> 8) % 256; sstr << (MyPassivePort >> 8) % 256;
sstr << ","; sstr << ",";
sstr << MyPassivePort % 256; sstr << MyPassivePort % 256;
sstr << ").\n"; sstr << ").\n";
Result = sstr.str(); Result = sstr.str();
break; break;
} }
case 229: { case 229: {
std::stringstream sstr; std::stringstream sstr;
sstr << "229 Entering extended passive mode (|||"; sstr << "229 Entering extended passive mode (|||";
@ -360,12 +528,12 @@ std::string FTP::User::NumToMsg( int MsgNum ) {
Result = "250 Requested file action okay, completed.\n"; Result = "250 Requested file action okay, completed.\n";
break; break;
} }
case 2570: {//PWD case 2570: { //PWD
Result = "257 \"" + MyDir.PWD( ) + "\" selected as PWD\n"; Result = "257 \"" + MyDir.PWD() + "\" selected as PWD\n";
break; break;
} }
case 2571: {//MKD case 2571: { //MKD
Result = "257 \"" + MyDir.PWD( ) + "\" created\n"; Result = "257 \"" + MyDir.PWD() + "\" created\n";
break; break;
} }
case 331: { case 331: {

View file

@ -13,22 +13,23 @@
namespace FTP { namespace FTP {
static std::string FTPBasePath = "/tmp/mist/OnDemand/"; static std::string FTPBasePath = "/tmp/mist/OnDemand/";
enum Mode { enum Mode{
MODE_STREAM, MODE_STREAM,
};//FTP::Mode enumeration };
//FTP::Mode enumeration
enum Structure {
STRU_FILE, enum Structure{
STRU_RECORD, STRU_FILE, STRU_RECORD,
};//FTP::Structure enumeration };
//FTP::Structure enumeration
enum Type {
TYPE_ASCII_NONPRINT, enum Type{
TYPE_IMAGE_NONPRINT, TYPE_ASCII_NONPRINT, TYPE_IMAGE_NONPRINT,
};//FTP::Type enumeration };
//FTP::Type enumeration
enum Commands {
enum Commands{
CMD_NOCMD, CMD_NOCMD,
CMD_NOOP, CMD_NOOP,
CMD_USER, CMD_USER,
@ -51,18 +52,19 @@ namespace FTP {
CMD_MKD, CMD_MKD,
CMD_RNFR, CMD_RNFR,
CMD_RNTO, CMD_RNTO,
};//FTP::Commands enumeration };
//FTP::Commands enumeration
class User {
class User{
public: public:
User( Socket::Connection NewConnection, std::map<std::string,std::string> Credentials); User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials);
~User( ); ~User();
int ParseCommand( std::string Command ); int ParseCommand(std::string Command);
bool LoggedIn( ); bool LoggedIn();
std::string NumToMsg( int MsgNum ); std::string NumToMsg(int MsgNum);
Socket::Connection Conn; Socket::Connection Conn;
private: private:
std::map<std::string,std::string> AllCredentials; std::map<std::string, std::string> AllCredentials;
std::string USER; std::string USER;
std::string PASS; std::string PASS;
Mode MODE; Mode MODE;
@ -73,7 +75,8 @@ namespace FTP {
int MyPassivePort; int MyPassivePort;
Filesystem::Directory MyDir; Filesystem::Directory MyDir;
std::string RNFR; std::string RNFR;
std::vector< std::string > ActiveStreams; std::vector<std::string> ActiveStreams;
};//FTP::User class };
//FTP::User class
};//FTP Namespace
}//FTP Namespace

View file

@ -5,7 +5,9 @@
/// 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().
HTTP::Parser::Parser(){Clean();} HTTP::Parser::Parser(){
Clean();
}
/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage. /// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage.
void HTTP::Parser::Clean(){ void HTTP::Parser::Clean(){
@ -27,11 +29,13 @@ void HTTP::Parser::Clean(){
std::string & HTTP::Parser::BuildRequest(){ std::string & HTTP::Parser::BuildRequest(){
/// \todo Include GET/POST variable parsing? /// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
builder = method+" "+url+" "+protocol+"\r\n"; protocol = "HTTP/1.0";
for (it=headers.begin(); it != headers.end(); it++){ }
if ((*it).first != "" && (*it).second != ""){ builder = method + " " + url + " " + protocol + "\r\n";
builder += (*it).first + ": " + (*it).second + "\r\n"; for (it = headers.begin(); it != headers.end(); it++){
if (( *it).first != "" && ( *it).second != ""){
builder += ( *it).first + ": " + ( *it).second + "\r\n";
} }
} }
builder += "\r\n" + body; builder += "\r\n" + body;
@ -47,12 +51,14 @@ std::string & HTTP::Parser::BuildRequest(){
std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){
/// \todo Include GET/POST variable parsing? /// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
builder = protocol+" "+code+" "+message+"\r\n"; protocol = "HTTP/1.0";
for (it=headers.begin(); it != headers.end(); it++){ }
if ((*it).first != "" && (*it).second != ""){ builder = protocol + " " + code + " " + message + "\r\n";
if ((*it).first != "Content-Length" || (*it).second != "0"){ for (it = headers.begin(); it != headers.end(); it++){
builder += (*it).first + ": " + (*it).second + "\r\n"; if (( *it).first != "" && ( *it).second != ""){
if (( *it).first != "Content-Length" || ( *it).second != "0"){
builder += ( *it).first + ": " + ( *it).second + "\r\n";
} }
} }
} }
@ -67,7 +73,11 @@ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message)
void HTTP::Parser::Trim(std::string & s){ void HTTP::Parser::Trim(std::string & s){
size_t startpos = s.find_first_not_of(" \t"); size_t startpos = s.find_first_not_of(" \t");
size_t endpos = s.find_last_not_of(" \t"); size_t endpos = s.find_last_not_of(" \t");
if ((std::string::npos == startpos) || (std::string::npos == endpos)){s = "";}else{s = s.substr(startpos, endpos-startpos+1);} if ((std::string::npos == startpos) || (std::string::npos == endpos)){
s = "";
}else{
s = s.substr(startpos, endpos - startpos + 1);
}
} }
/// Function that sets the body of a response or request, along with the correct Content-Length header. /// Function that sets the body of a response or request, along with the correct Content-Length header.
@ -96,9 +106,13 @@ std::string HTTP::Parser::getUrl(){
} }
/// Returns header i, if set. /// Returns header i, if set.
std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} std::string HTTP::Parser::GetHeader(std::string i){
return headers[i];
}
/// Returns POST variable i, if set. /// Returns POST variable i, if set.
std::string HTTP::Parser::GetVar(std::string i){return vars[i];} std::string HTTP::Parser::GetVar(std::string i){
return vars[i];
}
/// Sets header i to string value v. /// Sets header i to string value v.
void HTTP::Parser::SetHeader(std::string i, std::string v){ void HTTP::Parser::SetHeader(std::string i, std::string v){
@ -110,7 +124,7 @@ void HTTP::Parser::SetHeader(std::string i, std::string v){
/// Sets header i to integer value v. /// Sets header i to integer value v.
void HTTP::Parser::SetHeader(std::string i, int v){ void HTTP::Parser::SetHeader(std::string i, int v){
Trim(i); Trim(i);
char val[23];//ints are never bigger than 22 chars as decimal char val[23]; //ints are never bigger than 22 chars as decimal
sprintf(val, "%i", v); sprintf(val, "%i", v);
headers[i] = val; headers[i] = val;
} }
@ -120,7 +134,7 @@ void HTTP::Parser::SetVar(std::string i, std::string v){
Trim(i); Trim(i);
Trim(v); Trim(v);
//only set if there is actually a key //only set if there is actually a key
if(!i.empty()){ if ( !i.empty()){
vars[i] = v; vars[i] = v;
} }
} }
@ -131,7 +145,7 @@ void HTTP::Parser::SetVar(std::string i, std::string v){
/// \return True if a whole request or response was read, false otherwise. /// \return True if a whole request or response was read, false otherwise.
bool HTTP::Parser::Read(std::string & strbuf){ bool HTTP::Parser::Read(std::string & strbuf){
return parse(strbuf); return parse(strbuf);
}//HTTPReader::Read } //HTTPReader::Read
#include <iostream> #include <iostream>
/// Attempt to read a whole HTTP response or request from a data buffer. /// Attempt to read a whole HTTP response or request from a data buffer.
@ -143,44 +157,54 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){
size_t f; size_t f;
std::string tmpA, tmpB, tmpC; std::string tmpA, tmpB, tmpC;
/// \todo Make this not resize HTTPbuffer in parts, but read all at once and then remove the entire request, like doxygen claims it does? /// \todo Make this not resize HTTPbuffer in parts, but read all at once and then remove the entire request, like doxygen claims it does?
while (!HTTPbuffer.empty()){ while ( !HTTPbuffer.empty()){
if (!seenHeaders){ if ( !seenHeaders){
f = HTTPbuffer.find('\n'); f = HTTPbuffer.find('\n');
if (f == std::string::npos) return false; if (f == std::string::npos) return false;
tmpA = HTTPbuffer.substr(0, f); tmpA = HTTPbuffer.substr(0, f);
if (f+1 == HTTPbuffer.size()){ if (f + 1 == HTTPbuffer.size()){
HTTPbuffer.clear(); HTTPbuffer.clear();
}else{ }else{
HTTPbuffer.erase(0, f+1); HTTPbuffer.erase(0, f + 1);
} }
while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));} while (tmpA.find('\r') != std::string::npos){
if (!seenReq){ tmpA.erase(tmpA.find('\r'));
}
if ( !seenReq){
seenReq = true; seenReq = true;
f = tmpA.find(' '); f = tmpA.find(' ');
if (f != std::string::npos){ if (f != std::string::npos){
method = tmpA.substr(0, f); tmpA.erase(0, f+1); method = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
f = tmpA.find(' '); f = tmpA.find(' ');
if (f != std::string::npos){ if (f != std::string::npos){
url = tmpA.substr(0, f); tmpA.erase(0, f+1); url = tmpA.substr(0, f);
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)); //parse GET variables
} }
}else{seenReq = false;} }else{
}else{seenReq = false;} seenReq = false;
}
}else{
seenReq = false;
}
}else{ }else{
if (tmpA.size() == 0){ if (tmpA.size() == 0){
seenHeaders = true; seenHeaders = true;
body.clear(); body.clear();
if (GetHeader("Content-Length") != ""){ if (GetHeader("Content-Length") != ""){
length = atoi(GetHeader("Content-Length").c_str()); length = atoi(GetHeader("Content-Length").c_str());
if (body.capacity() < length){body.reserve(length);} if (body.capacity() < length){
body.reserve(length);
}
} }
}else{ }else{
f = tmpA.find(':'); f = tmpA.find(':');
if (f == std::string::npos) continue; if (f == std::string::npos) continue;
tmpB = tmpA.substr(0, f); tmpB = tmpA.substr(0, f);
tmpC = tmpA.substr(f+1); tmpC = tmpA.substr(f + 1);
SetHeader(tmpB, tmpC); SetHeader(tmpB, tmpC);
} }
} }
@ -204,7 +228,7 @@ 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. /// Parses GET or POST-style variable data.
/// Saves to internal variable structure using HTTP::Parser::SetVar. /// Saves to internal variable structure using HTTP::Parser::SetVar.
@ -266,8 +290,12 @@ std::string HTTP::Parser::urlunescape(const std::string & in){
tmp += unhex(in[i]); tmp += unhex(in[i]);
} }
out += tmp; out += tmp;
} else { }else{
if (in[i] == '+'){out += ' ';}else{out += in[i];} if (in[i] == '+'){
out += ' ';
}else{
out += in[i];
}
} }
} }
return out; return out;
@ -276,15 +304,16 @@ std::string HTTP::Parser::urlunescape(const std::string & in){
/// Helper function for urlunescape. /// Helper function for urlunescape.
/// Takes a single char input and outputs its integer hex value. /// Takes a single char input and outputs its integer hex value.
int HTTP::Parser::unhex(char c){ int HTTP::Parser::unhex(char c){
return( c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 ); return (c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10);
} }
/// URLencodes std::string data. /// URLencodes std::string data.
std::string HTTP::Parser::urlencode(const std::string &c){ std::string HTTP::Parser::urlencode(const std::string &c){
std::string escaped=""; std::string escaped = "";
int max = c.length(); int max = c.length();
for(int i=0; i<max; i++){ for (int i = 0; i < max; i++){
if (('0'<=c[i] && c[i]<='9') || ('a'<=c[i] && c[i]<='z') || ('A'<=c[i] && c[i]<='Z') || (c[i]=='~' || c[i]=='!' || c[i]=='*' || c[i]=='(' || c[i]==')' || c[i]=='\'')){ if (('0' <= c[i] && c[i] <= '9') || ('a' <= c[i] && c[i] <= 'z') || ('A' <= c[i] && c[i] <= 'Z')
|| (c[i] == '~' || c[i] == '!' || c[i] == '*' || c[i] == '(' || c[i] == ')' || c[i] == '\'')){
escaped.append( &c[i], 1); escaped.append( &c[i], 1);
}else{ }else{
escaped.append("%"); escaped.append("%");
@ -297,14 +326,14 @@ std::string HTTP::Parser::urlencode(const std::string &c){
/// Helper function for urlescape. /// Helper function for urlescape.
/// Encodes a character as two hex digits. /// Encodes a character as two hex digits.
std::string HTTP::Parser::hex(char dec){ std::string HTTP::Parser::hex(char dec){
char dig1 = (dec&0xF0)>>4; char dig1 = (dec & 0xF0) >> 4;
char dig2 = (dec&0x0F); char dig2 = (dec & 0x0F);
if (dig1<= 9) dig1+=48; if (dig1 <= 9) dig1 += 48;
if (10<= dig1 && dig1<=15) dig1+=97-10; if (10 <= dig1 && dig1 <= 15) dig1 += 97 - 10;
if (dig2<= 9) dig2+=48; if (dig2 <= 9) dig2 += 48;
if (10<= dig2 && dig2<=15) dig2+=97-10; if (10 <= dig2 && dig2 <= 15) dig2 += 97 - 10;
std::string r; std::string r;
r.append(&dig1, 1); r.append( &dig1, 1);
r.append(&dig2, 1); r.append( &dig2, 1);
return r; return r;
} }

View file

@ -8,7 +8,7 @@
#include <stdio.h> #include <stdio.h>
/// Holds all HTTP processing related code. /// Holds all HTTP processing related code.
namespace HTTP{ namespace HTTP {
/// 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:
@ -45,5 +45,7 @@ namespace HTTP{
void Trim(std::string & s); void Trim(std::string & s);
static int unhex(char c); static int unhex(char c);
static std::string hex(char dec); static std::string hex(char dec);
};//HTTP::Parser class };
};//HTTP namespace //HTTP::Parser class
}//HTTP namespace

View file

@ -7,7 +7,6 @@
#include <stdint.h> //for uint64_t #include <stdint.h> //for uint64_t
#include <string.h> //for memcpy #include <string.h> //for memcpy
#include <arpa/inet.h> //for htonl #include <arpa/inet.h> //for htonl
int JSON::Value::c2hex(int c){ int JSON::Value::c2hex(int c){
if (c >= '0' && c <= '9') return c - '0'; if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10;
@ -15,7 +14,6 @@ int JSON::Value::c2hex(int c){
return 0; return 0;
} }
std::string JSON::Value::read_string(int separator, std::istream & fromstream){ std::string JSON::Value::read_string(int separator, std::istream & fromstream){
std::string out; std::string out;
bool escaped = false; bool escaped = false;
@ -27,17 +25,28 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){
} }
if (escaped){ if (escaped){
switch (c){ switch (c){
case 'b': out += '\b'; break; case 'b':
case 'f': out += '\f'; break; out += '\b';
case 'n': out += '\n'; break; break;
case 'r': out += '\r'; break; case 'f':
case 't': out += '\t'; break; out += '\f';
case 'u':{ break;
int d1 = fromstream.get(); case 'n':
int d2 = fromstream.get(); out += '\n';
int d3 = fromstream.get(); break;
int d4 = fromstream.get(); case 'r':
c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); out += '\r';
break;
case 't':
out += '\t';
break;
case 'u': {
int d1 = fromstream.get();
int d2 = fromstream.get();
int d3 = fromstream.get();
int d4 = fromstream.get();
c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16);
break;
} }
default: default:
out += (char)c; out += (char)c;
@ -58,14 +67,30 @@ std::string JSON::Value::string_escape(std::string val){
std::string out = "\""; std::string out = "\"";
for (unsigned int i = 0; i < val.size(); ++i){ for (unsigned int i = 0; i < val.size(); ++i){
switch (val[i]){ switch (val[i]){
case '"': out += "\\\""; break; case '"':
case '\\': out += "\\\\"; break; out += "\\\"";
case '\n': out += "\\n"; break; break;
case '\b': out += "\\b"; break; case '\\':
case '\f': out += "\\f"; break; out += "\\\\";
case '\r': out += "\\r"; break; break;
case '\t': out += "\\t"; break; case '\n':
default: out += val[i]; out += "\\n";
break;
case '\b':
out += "\\b";
break;
case '\f':
out += "\\f";
break;
case '\r':
out += "\\r";
break;
case '\t':
out += "\\t";
break;
default:
out += val[i];
break;
} }
} }
out += "\""; out += "\"";
@ -76,9 +101,15 @@ std::string JSON::Value::string_escape(std::string val){
void JSON::Value::skipToEnd(std::istream & fromstream){ void JSON::Value::skipToEnd(std::istream & fromstream){
while (fromstream.good()){ while (fromstream.good()){
char peek = fromstream.peek(); char peek = fromstream.peek();
if (peek == ','){return;} if (peek == ','){
if (peek == ']'){return;} return;
if (peek == '}'){return;} }
if (peek == ']'){
return;
}
if (peek == '}'){
return;
}
peek = fromstream.get(); peek = fromstream.get();
} }
} }
@ -101,7 +132,7 @@ JSON::Value::Value(std::istream & fromstream){
c = fromstream.get(); c = fromstream.get();
myType = OBJECT; myType = OBJECT;
break; break;
case '[':{ case '[': {
reading_array = true; reading_array = true;
c = fromstream.get(); c = fromstream.get();
myType = ARRAY; myType = ARRAY;
@ -114,13 +145,13 @@ JSON::Value::Value(std::istream & fromstream){
case '\'': case '\'':
case '"': case '"':
c = fromstream.get(); c = fromstream.get();
if (!reading_object){ if ( !reading_object){
myType = STRING; myType = STRING;
strVal = read_string(c, fromstream); strVal = read_string(c, fromstream);
return; return;
}else{ }else{
std::string tmpstr = read_string(c, fromstream); std::string tmpstr = read_string(c, fromstream);
(*this)[tmpstr] = JSON::Value(fromstream); ( *this)[tmpstr] = JSON::Value(fromstream);
} }
break; break;
case '0': case '0':
@ -139,18 +170,22 @@ JSON::Value::Value(std::istream & fromstream){
intVal += c - '0'; intVal += c - '0';
break; break;
case ',': case ',':
if (!reading_object && !reading_array) return; if ( !reading_object && !reading_array) return;
c = fromstream.get(); c = fromstream.get();
if (reading_array){ if (reading_array){
append(JSON::Value(fromstream)); append(JSON::Value(fromstream));
} }
break; break;
case '}': case '}':
if (reading_object){c = fromstream.get();} if (reading_object){
c = fromstream.get();
}
return; return;
break; break;
case ']': case ']':
if (reading_array){c = fromstream.get();} if (reading_array){
c = fromstream.get();
}
return; return;
break; break;
case 't': case 't':
@ -174,7 +209,7 @@ JSON::Value::Value(std::istream & fromstream){
return; return;
break; break;
default: default:
c = fromstream.get();//ignore this character c = fromstream.get(); //ignore this character
continue; continue;
break; break;
} }
@ -185,12 +220,14 @@ JSON::Value::Value(std::istream & fromstream){
JSON::Value::Value(const std::string & val){ JSON::Value::Value(const std::string & val){
myType = STRING; myType = STRING;
strVal = val; strVal = val;
intVal = 0;
} }
/// Sets this JSON::Value to the given string. /// Sets this JSON::Value to the given string.
JSON::Value::Value(const char * val){ JSON::Value::Value(const char * val){
myType = STRING; myType = STRING;
strVal = val; strVal = val;
intVal = 0;
} }
/// Sets this JSON::Value to the given integer. /// Sets this JSON::Value to the given integer.
@ -202,14 +239,24 @@ JSON::Value::Value(long long int val){
/// Compares a JSON::Value to another for equality. /// Compares a JSON::Value to another for equality.
bool JSON::Value::operator==(const JSON::Value & rhs) const{ bool JSON::Value::operator==(const JSON::Value & rhs) const{
if (myType != rhs.myType) return false; if (myType != rhs.myType) return false;
if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;} if (myType == INTEGER || myType == BOOL){
if (myType == STRING){return strVal == rhs.strVal;} return intVal == rhs.intVal;
if (myType == EMPTY){return true;} }
if (myType == STRING){
return strVal == rhs.strVal;
}
if (myType == EMPTY){
return true;
}
if (myType == OBJECT){ if (myType == OBJECT){
if (objVal.size() != rhs.objVal.size()) return false; if (objVal.size() != rhs.objVal.size()) return false;
for (std::map<std::string, Value>::const_iterator it = objVal.begin(); it != objVal.end(); ++it){ for (std::map<std::string, Value>::const_iterator it = objVal.begin(); it != objVal.end(); ++it){
if (!rhs.isMember(it->first)){return false;} if ( !rhs.isMember(it->first)){
if (it->second != rhs.objVal.find(it->first)->second){return false;} return false;
}
if (it->second != rhs.objVal.find(it->first)->second){
return false;
}
} }
return true; return true;
} }
@ -217,7 +264,9 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{
if (arrVal.size() != rhs.arrVal.size()) return false; if (arrVal.size() != rhs.arrVal.size()) return false;
int i = 0; int i = 0;
for (std::deque<Value>::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){ for (std::deque<Value>::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){
if (*it != rhs.arrVal[i]){return false;} if ( *it != rhs.arrVal[i]){
return false;
}
i++; i++;
} }
return true; return true;
@ -227,7 +276,7 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{
/// Compares a JSON::Value to another for equality. /// Compares a JSON::Value to another for equality.
bool JSON::Value::operator!=(const JSON::Value & rhs) const{ bool JSON::Value::operator!=(const JSON::Value & rhs) const{
return !((*this) == rhs); return !(( *this) == rhs);
} }
/// Sets this JSON::Value to the given boolean. /// Sets this JSON::Value to the given boolean.
@ -248,7 +297,7 @@ JSON::Value & JSON::Value::operator=(const std::string &rhs){
/// Sets this JSON::Value to the given string. /// Sets this JSON::Value to the given string.
JSON::Value & JSON::Value::operator=(const char * rhs){ JSON::Value & JSON::Value::operator=(const char * rhs){
return ((*this) = (std::string)rhs); return (( *this) = (std::string)rhs);
} }
/// Sets this JSON::Value to the given integer. /// Sets this JSON::Value to the given integer.
@ -261,12 +310,12 @@ JSON::Value & JSON::Value::operator=(const long long int &rhs){
/// Sets this JSON::Value to the given integer. /// Sets this JSON::Value to the given integer.
JSON::Value & JSON::Value::operator=(const int &rhs){ JSON::Value & JSON::Value::operator=(const int &rhs){
return ((*this) = (long long int)rhs); return (( *this) = (long long int)rhs);
} }
/// Sets this JSON::Value to the given integer. /// Sets this JSON::Value to the given integer.
JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ JSON::Value & JSON::Value::operator=(const unsigned int &rhs){
return ((*this) = (long long int)rhs); return (( *this) = (long long int)rhs);
} }
/// Automatic conversion to long long int - returns 0 if not convertable. /// Automatic conversion to long long int - returns 0 if not convertable.
@ -280,7 +329,6 @@ JSON::Value::operator long long int(){
return 0; return 0;
} }
/// Automatic conversion to std::string. /// Automatic conversion to std::string.
/// Returns the raw string value if available, otherwise calls toString(). /// Returns the raw string value if available, otherwise calls toString().
JSON::Value::operator std::string(){ JSON::Value::operator std::string(){
@ -298,29 +346,40 @@ JSON::Value::operator std::string(){
/// Automatic conversion to bool. /// Automatic conversion to bool.
/// Returns true if there is anything meaningful stored into this value. /// Returns true if there is anything meaningful stored into this value.
JSON::Value::operator bool(){ JSON::Value::operator bool(){
if (myType == STRING){return strVal != "";} if (myType == STRING){
if (myType == INTEGER){return intVal != 0;} return strVal != "";
if (myType == BOOL){return intVal != 0;} }
if (myType == OBJECT){return size() > 0;} if (myType == INTEGER){
if (myType == ARRAY){return size() > 0;} return intVal != 0;
if (myType == EMPTY){return false;} }
return false;//unimplemented? should never happen... if (myType == BOOL){
return intVal != 0;
}
if (myType == OBJECT){
return size() > 0;
}
if (myType == ARRAY){
return size() > 0;
}
if (myType == EMPTY){
return false;
}
return false; //unimplemented? should never happen...
} }
/// Explicit conversion to std::string. /// Explicit conversion to std::string.
const std::string JSON::Value::asString(){ const std::string JSON::Value::asString(){
return (std::string)*this; return (std::string) *this;
} }
/// Explicit conversion to long long int. /// Explicit conversion to long long int.
const long long int JSON::Value::asInt(){ const long long int JSON::Value::asInt(){
return (long long int)*this; return (long long int) *this;
} }
/// Explicit conversion to bool. /// Explicit conversion to bool.
const bool JSON::Value::asBool(){ const bool JSON::Value::asBool(){
return (bool)*this; return (bool) *this;
} }
/// Retrieves or sets the JSON::Value at this position in the object. /// Retrieves or sets the JSON::Value at this position in the object.
/// Converts destructively to object if not already an object. /// Converts destructively to object if not already an object.
JSON::Value & JSON::Value::operator[](const std::string i){ JSON::Value & JSON::Value::operator[](const std::string i){
@ -362,15 +421,19 @@ std::string JSON::Value::toPacked(){
if (isInt() || isNull() || isBool()){ if (isInt() || isNull() || isBool()){
r += 0x01; r += 0x01;
uint64_t numval = intVal; uint64_t numval = intVal;
r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); r += *(((char*) &numval) + 7);
r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); r += *(((char*) &numval) + 6);
r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); r += *(((char*) &numval) + 5);
r += *(((char*)&numval)+1); r += *(((char*)&numval)); r += *(((char*) &numval) + 4);
r += *(((char*) &numval) + 3);
r += *(((char*) &numval) + 2);
r += *(((char*) &numval) + 1);
r += *(((char*) &numval));
} }
if (isString()){ if (isString()){
r += 0x02; r += 0x02;
r += strVal.size() / (256*256*256); r += strVal.size() / (256 * 256 * 256);
r += strVal.size() / (256*256); r += strVal.size() / (256 * 256);
r += strVal.size() / 256; r += strVal.size() / 256;
r += strVal.size() % 256; r += strVal.size() % 256;
r += strVal; r += strVal;
@ -385,7 +448,9 @@ std::string JSON::Value::toPacked(){
r += it->second.toPacked(); r += it->second.toPacked();
} }
} }
r += (char)0x0; r += (char)0x0; r += (char)0xEE; r += (char)0x0;
r += (char)0x0;
r += (char)0xEE;
strVal.clear(); strVal.clear();
} }
if (isArray()){ if (isArray()){
@ -393,11 +458,14 @@ std::string JSON::Value::toPacked(){
for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){ for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){
r += it->toPacked(); r += it->toPacked();
} }
r += (char)0x0; r += (char)0x0; r += (char)0xEE; r += (char)0x0;
r += (char)0x0;
r += (char)0xEE;
} }
return r; return r;
};//toPacked }
;
//toPacked
/// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header.
/// Non-object-types will print an error to stderr and return an empty string. /// Non-object-types will print an error to stderr and return an empty string.
@ -422,14 +490,13 @@ std::string & JSON::Value::toNetPacked(){
} }
//insert the packet length at bytes 4-7 //insert the packet length at bytes 4-7
unsigned int size = htonl(packed.size()); unsigned int size = htonl(packed.size());
memcpy((void*)(strVal.c_str() + 4), (void*)&size, 4); memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4);
//copy the rest of the string //copy the rest of the string
memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size());
} }
return strVal; return strVal;
} }
/// Converts this JSON::Value to valid JSON notation and returns it. /// Converts this JSON::Value to valid JSON notation and returns it.
/// Makes absolutely no attempts to pretty-print anything. :-) /// Makes absolutely no attempts to pretty-print anything. :-)
std::string JSON::Value::toString(){ std::string JSON::Value::toString(){
@ -449,7 +516,9 @@ std::string JSON::Value::toString(){
if (arrVal.size() > 0){ if (arrVal.size() > 0){
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
tmp += it->toString(); tmp += it->toString();
if (it + 1 != ArrEnd()){tmp += ",";} if (it + 1 != ArrEnd()){
tmp += ",";
}
} }
} }
tmp += "]"; tmp += "]";
@ -464,7 +533,9 @@ std::string JSON::Value::toString(){
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
tmp2 += "\"" + it2->first + "\":"; tmp2 += "\"" + it2->first + "\":";
tmp2 += it2->second.toString(); tmp2 += it2->second.toString();
if (it2 != it3){tmp2 += ",";} if (it2 != it3){
tmp2 += ",";
}
} }
} }
tmp2 += "}"; tmp2 += "}";
@ -475,7 +546,7 @@ std::string JSON::Value::toString(){
default: default:
return "null"; return "null";
} }
return "null";//should never get here... return "null"; //should never get here...
} }
/// Converts this JSON::Value to valid JSON notation and returns it. /// Converts this JSON::Value to valid JSON notation and returns it.
@ -491,7 +562,7 @@ std::string JSON::Value::toPrettyString(int indentation){
case STRING: { case STRING: {
for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){ for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){
if (strVal[i] < 32 || strVal[i] > 125){ if (strVal[i] < 32 || strVal[i] > 125){
return JSON::Value((long long int)strVal.size()).asString()+" bytes of binary data"; return JSON::Value((long long int)strVal.size()).asString() + " bytes of binary data";
} }
} }
return string_escape(strVal); return string_escape(strVal);
@ -501,10 +572,12 @@ std::string JSON::Value::toPrettyString(int indentation){
if (arrVal.size() > 0){ if (arrVal.size() > 0){
std::string tmp = "[\n"; std::string tmp = "[\n";
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
tmp += std::string(indentation+2, ' ')+it->toPrettyString(indentation+2); tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2);
if (it + 1 != ArrEnd()){tmp += ",\n";} if (it + 1 != ArrEnd()){
tmp += ",\n";
}
} }
tmp += "\n"+std::string(indentation, ' ')+"]"; tmp += "\n" + std::string(indentation, ' ') + "]";
return tmp; return tmp;
}else{ }else{
return "[]"; return "[]";
@ -517,11 +590,13 @@ std::string JSON::Value::toPrettyString(int indentation){
ObjIter it3 = ObjEnd(); ObjIter it3 = ObjEnd();
--it3; --it3;
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
tmp2 += std::string(indentation+2, ' ')+"\"" + it2->first + "\":"; tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":";
tmp2 += it2->second.toPrettyString(indentation+2); tmp2 += it2->second.toPrettyString(indentation + 2);
if (it2 != it3){tmp2 += ",\n";} if (it2 != it3){
tmp2 += ",\n";
}
} }
tmp2 += "\n"+std::string(indentation, ' ')+"}"; tmp2 += "\n" + std::string(indentation, ' ') + "}";
return tmp2; return tmp2;
}else{ }else{
return "{}"; return "{}";
@ -532,7 +607,7 @@ std::string JSON::Value::toPrettyString(int indentation){
default: default:
return "null"; return "null";
} }
return "null";//should never get here... return "null"; //should never get here...
} }
/// Appends the given value to the end of this JSON::Value array. /// Appends the given value to the end of this JSON::Value array.
@ -563,11 +638,15 @@ void JSON::Value::prepend(const JSON::Value & rhs){
/// given size. /// given size.
void JSON::Value::shrink(unsigned int size){ void JSON::Value::shrink(unsigned int size){
if (myType == ARRAY){ if (myType == ARRAY){
while (arrVal.size() > size){arrVal.pop_front();} while (arrVal.size() > size){
arrVal.pop_front();
}
return; return;
} }
if (myType == OBJECT){ if (myType == OBJECT){
while (objVal.size() > size){objVal.erase(objVal.begin());} while (objVal.size() > size){
objVal.erase(objVal.begin());
}
return; return;
} }
} }
@ -671,62 +750,66 @@ JSON::Value JSON::fromFile(std::string filename){
/// \param i Current parsing position in the raw data (defaults to 0). /// \param i Current parsing position in the raw data (defaults to 0).
/// \returns A single JSON::Value, parsed from the raw data. /// \returns A single JSON::Value, parsed from the raw data.
JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i){ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i){
#if DEBUG >= 10 #if DEBUG >= 10
fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i);
#endif #endif
switch (data[i]){ switch (data[i]){
case 0x01:{//integer case 0x01: { //integer
unsigned char tmpdbl[8]; unsigned char tmpdbl[8];
tmpdbl[7] = data[i+1]; tmpdbl[7] = data[i + 1];
tmpdbl[6] = data[i+2]; tmpdbl[6] = data[i + 2];
tmpdbl[5] = data[i+3]; tmpdbl[5] = data[i + 3];
tmpdbl[4] = data[i+4]; tmpdbl[4] = data[i + 4];
tmpdbl[3] = data[i+5]; tmpdbl[3] = data[i + 5];
tmpdbl[2] = data[i+6]; tmpdbl[2] = data[i + 6];
tmpdbl[1] = data[i+7]; tmpdbl[1] = data[i + 7];
tmpdbl[0] = data[i+8]; tmpdbl[0] = data[i + 8];
i+=9;//skip 8(an uint64_t)+1 forwards i += 9; //skip 8(an uint64_t)+1 forwards
uint64_t * d = (uint64_t*)tmpdbl; uint64_t * d = (uint64_t*)tmpdbl;
return JSON::Value((long long int)*d); return JSON::Value((long long int) *d);
} break; }
case 0x02:{//string break;
unsigned int tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length case 0x02: { //string
std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data unsigned int tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length
i += tmpi + 5;//skip length+size+1 forwards std::string tmpstr = std::string((const char *)data + i + 5, (size_t)tmpi); //set the string data
i += tmpi + 5; //skip length+size+1 forwards
return JSON::Value(tmpstr); return JSON::Value(tmpstr);
} break; }
case 0xFF://also object break;
case 0xE0:{//object case 0xFF: //also object
case 0xE0: { //object
++i; ++i;
JSON::Value ret; JSON::Value ret;
while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE)
unsigned int tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length unsigned int tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length
std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data
i += tmpi + 2;//skip length+size forwards i += tmpi + 2; //skip length+size forwards
ret[tmpstr] = fromDTMI(data, len, i);//add content, recursively parsed, updating i, setting indice to tmpstr ret[tmpstr] = fromDTMI(data, len, i); //add content, recursively parsed, updating i, setting indice to tmpstr
} }
i += 3;//skip 0x0000EE i += 3; //skip 0x0000EE
return ret; return ret;
} break; }
case 0x0A:{//array break;
case 0x0A: { //array
JSON::Value ret; JSON::Value ret;
++i; ++i;
while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE)
ret.append(fromDTMI(data, len, i));//add content, recursively parsed, updating i ret.append(fromDTMI(data, len, i)); //add content, recursively parsed, updating i
} }
i += 3;//skip 0x0000EE i += 3; //skip 0x0000EE
return ret; return ret;
} break; }
break;
} }
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]);
#endif #endif
return JSON::Value(); return JSON::Value();
}//fromOneDTMI } //fromOneDTMI
/// Parses a std::string to a valid JSON::Value. /// Parses a std::string to a valid JSON::Value.
/// This function will find one DTMI object in the string and return it. /// This function will find one DTMI object in the string and return it.
JSON::Value JSON::fromDTMI(std::string data){ JSON::Value JSON::fromDTMI(std::string data){
unsigned int i = 0; unsigned int i = 0;
return fromDTMI((const unsigned char*)data.c_str(), data.size(), i); return fromDTMI((const unsigned char*)data.c_str(), data.size(), i);
}//fromDTMI } //fromDTMI

View file

@ -7,19 +7,24 @@
#include <istream> #include <istream>
//empty definition of DTSC::Stream so it can be a friend. //empty definition of DTSC::Stream so it can be a friend.
namespace DTSC{class Stream;} namespace DTSC {
class Stream;
}
/// JSON-related classes and functions /// JSON-related classes and functions
namespace JSON{ namespace JSON {
/// Lists all types of JSON::Value. /// Lists all types of JSON::Value.
enum ValueType{ EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT }; enum ValueType{
EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT
};
class Value;//forward declaration for below typedef class Value;
//forward declaration for below typedef
typedef std::map<std::string, Value>::iterator ObjIter; typedef std::map<std::string, Value>::iterator ObjIter;
typedef std::deque<Value>::iterator ArrIter; typedef std::deque<Value>::iterator ArrIter;
/// A JSON::Value is either a string or an integer, but may also be an object, array or null. /// A JSON::Value is either a string or an integer, but may also be an object, array or null.
class Value{ class Value{
private: private:
@ -34,7 +39,7 @@ namespace JSON{
static void skipToEnd(std::istream & fromstream); static void skipToEnd(std::istream & fromstream);
public: public:
//friends //friends
friend class DTSC::Stream;//for access to strVal friend class DTSC::Stream; //for access to strVal
//constructors //constructors
Value(); Value();
Value(std::istream & fromstream); Value(std::istream & fromstream);
@ -91,5 +96,5 @@ namespace JSON{
Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i); Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i);
Value fromString(std::string json); Value fromString(std::string json);
Value fromFile(std::string filename); Value fromFile(std::string filename);
}; }

File diff suppressed because it is too large Load diff

289
lib/mp4.h
View file

@ -9,36 +9,36 @@
#include "json.h" #include "json.h"
/// Contains all MP4 format related code. /// Contains all MP4 format related code.
namespace MP4{ namespace MP4 {
class Box { class Box{
public: public:
Box(char * datapointer = 0, bool manage = true); Box(char * datapointer = 0, bool manage = true);
~Box(); ~Box();
std::string getType(); std::string getType();
bool isType( char* boxType ); bool isType(const char* boxType);
bool read(std::string & newData); bool read(std::string & newData);
long long int boxedSize(); long long int boxedSize();
long long int payloadSize(); long long int payloadSize();
char * asBox(); char * asBox();
char * payload(); char * payload();
void clear(); void clear();
std::string toPrettyString( int indent = 0 ); std::string toPrettyString(int indent = 0);
protected: protected:
//integer functions //integer functions
void setInt8( char newData, size_t index ); void setInt8(char newData, size_t index);
char getInt8( size_t index ); char getInt8(size_t index);
void setInt16( short newData, size_t index ); void setInt16(short newData, size_t index);
short getInt16( size_t index ); short getInt16(size_t index);
void setInt24( long newData, size_t index ); void setInt24(long newData, size_t index);
long getInt24( size_t index ); long getInt24(size_t index);
void setInt32( long newData, size_t index ); void setInt32(long newData, size_t index);
long getInt32( size_t index ); long getInt32(size_t index);
void setInt64( long long int newData, size_t index ); void setInt64(long long int newData, size_t index);
long long int getInt64( size_t index ); long long int getInt64(size_t index);
//string functions //string functions
void setString(std::string newData, size_t index ); void setString(std::string newData, size_t index);
void setString(char* newData, size_t size, size_t index ); void setString(char* newData, size_t size, size_t index);
char * getString(size_t index); char * getString(size_t index);
size_t getStringLen(size_t index); size_t getStringLen(size_t index);
//box functions //box functions
@ -51,59 +51,63 @@ namespace MP4{
char * data; ///< Holds the data of this box char * data; ///< Holds the data of this box
int data_size; ///< Currently reserved size int data_size; ///< Currently reserved size
bool managed; ///< If false, will not attempt to resize/free the data pointer. bool managed; ///< If false, will not attempt to resize/free the data pointer.
int payloadOffset;///<The offset of the payload with regards to the data int payloadOffset; ///<The offset of the payload with regards to the data
};//Box Class };
//Box Class
struct afrt_runtable { struct afrt_runtable{
long firstFragment; long firstFragment;
long long int firstTimestamp; long long int firstTimestamp;
long duration; long duration;
long discontinuity; long discontinuity;
};//fragmentRun };
//fragmentRun
/// AFRT Box class /// AFRT Box class
class AFRT : public Box { class AFRT: public Box{
public: public:
AFRT(); AFRT();
void setVersion( char newVersion ); void setVersion(char newVersion);
long getVersion(); long getVersion();
void setUpdate( long newUpdate ); void setUpdate(long newUpdate);
long getUpdate(); long getUpdate();
void setTimeScale( long newScale ); void setTimeScale(long newScale);
long getTimeScale(); long getTimeScale();
long getQualityEntryCount(); long getQualityEntryCount();
void setQualityEntry( std::string & newQuality, long no ); void setQualityEntry(std::string & newQuality, long no);
const char * getQualityEntry( long no ); const char * getQualityEntry(long no);
long getFragmentRunCount(); long getFragmentRunCount();
void setFragmentRun( afrt_runtable newRun, long no ); void setFragmentRun(afrt_runtable newRun, long no);
afrt_runtable getFragmentRun( long no ); afrt_runtable getFragmentRun(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//AFRT Box };
//AFRT Box
struct asrt_runtable{ struct asrt_runtable{
long firstSegment; long firstSegment;
long fragmentsPerSegment; long fragmentsPerSegment;
}; };
/// ASRT Box class /// ASRT Box class
class ASRT : public Box { class ASRT: public Box{
public: public:
ASRT(); ASRT();
void setVersion( char newVersion ); void setVersion(char newVersion);
long getVersion(); long getVersion();
void setUpdate( long newUpdate ); void setUpdate(long newUpdate);
long getUpdate(); long getUpdate();
long getQualityEntryCount(); long getQualityEntryCount();
void setQualityEntry( std::string & newQuality, long no ); void setQualityEntry(std::string & newQuality, long no);
const char* getQualityEntry( long no ); const char* getQualityEntry(long no);
long getSegmentRunEntryCount(); long getSegmentRunEntryCount();
void setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ); void setSegmentRun(long firstSegment, long fragmentsPerSegment, long no);
asrt_runtable getSegmentRun( long no ); asrt_runtable getSegmentRun(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//ASRT Box };
//ASRT Box
/// ABST Box class /// ABST Box class
class ABST: public Box { class ABST: public Box{
public: public:
ABST(); ABST();
void setVersion(char newVersion); void setVersion(char newVersion);
@ -143,61 +147,65 @@ namespace MP4{
void setFragmentRunTable(AFRT & table, long no); void setFragmentRunTable(AFRT & table, long no);
AFRT & getFragmentRunTable(long no); AFRT & getFragmentRunTable(long no);
std::string toPrettyString(long indent = 0); std::string toPrettyString(long indent = 0);
};//ABST Box };
//ABST Box
class MFHD : public Box {
class MFHD: public Box{
public: public:
MFHD(); MFHD();
void setSequenceNumber( long newSequenceNumber ); void setSequenceNumber(long newSequenceNumber);
long getSequenceNumber(); long getSequenceNumber();
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//MFHD Box };
//MFHD Box
class MOOF : public Box {
class MOOF: public Box{
public: public:
MOOF(); MOOF();
long getContentCount(); long getContentCount();
void setContent(Box & newContent, long no); void setContent(Box & newContent, long no);
Box & getContent(long no); Box & getContent(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//MOOF Box };
//MOOF Box
class TRAF : public Box {
class TRAF: public Box{
public: public:
TRAF(); TRAF();
long getContentCount(); long getContentCount();
void setContent(Box & newContent, long no); void setContent(Box & newContent, long no);
Box & getContent(long no); Box & getContent(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//TRAF Box
struct trunSampleInformation {
long sampleDuration;
long sampleSize;
long sampleFlags;
long sampleOffset;
}; };
enum trunflags { //TRAF Box
trundataOffset = 0x00000001,
struct trunSampleInformation{
long sampleDuration;
long sampleSize;
long sampleFlags;
long sampleOffset;
};
enum trunflags{
trundataOffset = 0x00000001,
trunfirstSampleFlags = 0x00000004, trunfirstSampleFlags = 0x00000004,
trunsampleDuration = 0x00000100, trunsampleDuration = 0x00000100,
trunsampleSize = 0x00000200, trunsampleSize = 0x00000200,
trunsampleFlags = 0x00000400, trunsampleFlags = 0x00000400,
trunsampleOffsets = 0x00000800 trunsampleOffsets = 0x00000800
}; };
enum sampleflags { enum sampleflags{
noIPicture = 0x01000000, noIPicture = 0x01000000,
isIPicture = 0x02000000, isIPicture = 0x02000000,
noDisposable = 0x00400000, noDisposable = 0x00400000,
isDisposable = 0x00800000, isDisposable = 0x00800000,
isRedundant = 0x00100000, isRedundant = 0x00100000,
noRedundant = 0x00200000, noRedundant = 0x00200000,
noKeySample = 0x00010000, noKeySample = 0x00010000,
isKeySample = 0x00000000, isKeySample = 0x00000000,
MUST_BE_PRESENT = 0x1 MUST_BE_PRESENT = 0x1
}; };
std::string prettySampleFlags(long flag); std::string prettySampleFlags(long flag);
class TRUN : public Box { class TRUN: public Box{
public: public:
TRUN(); TRUN();
void setFlags(long newFlags); void setFlags(long newFlags);
@ -212,7 +220,7 @@ namespace MP4{
std::string toPrettyString(long indent = 0); std::string toPrettyString(long indent = 0);
}; };
enum tfhdflags { enum tfhdflags{
tfhdBaseOffset = 0x000001, tfhdBaseOffset = 0x000001,
tfhdSampleDesc = 0x000002, tfhdSampleDesc = 0x000002,
tfhdSampleDura = 0x000008, tfhdSampleDura = 0x000008,
@ -220,7 +228,7 @@ namespace MP4{
tfhdSampleFlag = 0x000020, tfhdSampleFlag = 0x000020,
tfhdNoDuration = 0x010000, tfhdNoDuration = 0x010000,
}; };
class TFHD : public Box { class TFHD: public Box{
public: public:
TFHD(); TFHD();
void setFlags(long newFlags); void setFlags(long newFlags);
@ -240,73 +248,74 @@ namespace MP4{
std::string toPrettyString(long indent = 0); std::string toPrettyString(long indent = 0);
}; };
struct afraentry { struct afraentry{
long long time; long long time;
long long offset; long long offset;
}; };
struct globalafraentry { struct globalafraentry{
long long time; long long time;
long segment; long segment;
long fragment; long fragment;
long long afraoffset; long long afraoffset;
long long offsetfromafra; long long offsetfromafra;
}; };
class AFRA : public Box { class AFRA: public Box{
public:
AFRA();
void setVersion(long newVersion);
long getVersion();
void setFlags(long newFlags);
long getFlags();
void setLongIDs(bool newVal);
bool getLongIDs();
void setLongOffsets(bool newVal);
bool getLongOffsets();
void setGlobalEntries(bool newVal);
bool getGlobalEntries();
void setTimeScale(long newVal);
long getTimeScale();
long getEntryCount();
void setEntry(afraentry newEntry, long no);
afraentry getEntry(long no);
long getGlobalEntryCount();
void setGlobalEntry(globalafraentry newEntry, long no);
globalafraentry getGlobalEntry(long no);
std::string toPrettyString(long indent = 0);
};
class AVCC : public Box {
public: public:
AVCC(); AFRA();
void setVersion( long newVersion ); void setVersion(long newVersion);
long getVersion( ); long getVersion();
void setProfile( long newProfile ); void setFlags(long newFlags);
long getProfile( ); long getFlags();
void setCompatibleProfiles( long newCompatibleProfiles ); void setLongIDs(bool newVal);
long getCompatibleProfiles( ); bool getLongIDs();
void setLevel( long newLevel ); void setLongOffsets(bool newVal);
long getLevel( ); bool getLongOffsets();
void setSPSNumber( long newSPSNumber ); void setGlobalEntries(bool newVal);
long getSPSNumber( ); bool getGlobalEntries();
void setSPS( std::string newSPS ); void setTimeScale(long newVal);
long getSPSLen( ); long getTimeScale();
char* getSPS( ); long getEntryCount();
void setPPSNumber( long newPPSNumber ); void setEntry(afraentry newEntry, long no);
long getPPSNumber( ); afraentry getEntry(long no);
void setPPS( std::string newPPS ); long getGlobalEntryCount();
long getPPSLen( ); void setGlobalEntry(globalafraentry newEntry, long no);
char* getPPS( ); globalafraentry getGlobalEntry(long no);
std::string asAnnexB( );
void setPayload( std::string newPayload );
std::string toPrettyString(long indent = 0); std::string toPrettyString(long indent = 0);
}; };
class SDTP : public Box { class AVCC: public Box{
public:
AVCC();
void setVersion(long newVersion);
long getVersion();
void setProfile(long newProfile);
long getProfile();
void setCompatibleProfiles(long newCompatibleProfiles);
long getCompatibleProfiles();
void setLevel(long newLevel);
long getLevel();
void setSPSNumber(long newSPSNumber);
long getSPSNumber();
void setSPS(std::string newSPS);
long getSPSLen();
char* getSPS();
void setPPSNumber(long newPPSNumber);
long getPPSNumber();
void setPPS(std::string newPPS);
long getPPSLen();
char* getPPS();
std::string asAnnexB();
void setPayload(std::string newPayload);
std::string toPrettyString(long indent = 0);
};
class SDTP: public Box{
public: public:
SDTP(); SDTP();
void setVersion( long newVersion ); void setVersion(long newVersion);
long getVersion( ); long getVersion();
void setValue( long newValue, size_t index ); void setValue(long newValue, size_t index);
long getValue( size_t index ); long getValue(size_t index);
}; };
}; }

View file

@ -25,12 +25,14 @@ bool Util::Procs::handler_set = false;
/// Used internally to capture child signals and update plist. /// Used internally to capture child signals and update plist.
void Util::Procs::childsig_handler(int signum){ void Util::Procs::childsig_handler(int signum){
if (signum != SIGCHLD){return;} if (signum != SIGCHLD){
int status;
pid_t ret = waitpid(-1, &status, WNOHANG);
if (ret == 0){//ignore, would block otherwise
return; return;
}else if(ret < 0){ }
int status;
pid_t ret = waitpid( -1, &status, WNOHANG);
if (ret == 0){ //ignore, would block otherwise
return;
}else if (ret < 0){
#if DEBUG >= 3 #if DEBUG >= 3
std::cerr << "SIGCHLD received, but no child died"; std::cerr << "SIGCHLD received, but no child died";
#endif #endif
@ -41,27 +43,29 @@ void Util::Procs::childsig_handler(int signum){
exitcode = WEXITSTATUS(status); exitcode = WEXITSTATUS(status);
}else if (WIFSIGNALED(status)){ }else if (WIFSIGNALED(status)){
exitcode = -WTERMSIG(status); exitcode = -WTERMSIG(status);
}else{/* not possible */return;} }else{/* not possible */
return;
}
#if DEBUG >= 1 #if DEBUG >= 1
std::string pname = plist[ret]; std::string pname = plist[ret];
#endif #endif
plist.erase(ret); plist.erase(ret);
#if DEBUG >= 1 #if DEBUG >= 1
if (isActive(pname)){ if (isActive(pname)){
Stop(pname); Stop(pname);
}else{ } else{
//can this ever happen? //can this ever happen?
std::cerr << "Process " << pname << " fully terminated." << std::endl; std::cerr << "Process " << pname << " fully terminated." << std::endl;
} }
#endif #endif
if (exitHandlers.count(ret) > 0){ if (exitHandlers.count(ret) > 0){
TerminationNotifier tn = exitHandlers[ret]; TerminationNotifier tn = exitHandlers[ret];
exitHandlers.erase(ret); exitHandlers.erase(ret);
#if DEBUG >= 2 #if DEBUG >= 2
std::cerr << "Calling termination handler for " << pname << std::endl; std::cerr << "Calling termination handler for " << pname << std::endl;
#endif #endif
tn(ret, exitcode); tn(ret, exitcode);
} }
} }
@ -84,12 +88,14 @@ void Util::Procs::runCmd(std::string & cmd){
++i; ++i;
args[i] = tmp2; args[i] = tmp2;
} }
if (i == 20){args[20] = 0;} if (i == 20){
args[20] = 0;
}
//execute the command //execute the command
execvp(args[0], args); execvp(args[0], args);
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl; std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl;
#endif #endif
_exit(42); _exit(42);
} }
@ -98,11 +104,13 @@ void Util::Procs::runCmd(std::string & cmd){
/// \arg name Name for this process - only used internally. /// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for this process. /// \arg cmd Commandline for this process.
pid_t Util::Procs::Start(std::string name, std::string cmd){ pid_t Util::Procs::Start(std::string name, std::string cmd){
if (isActive(name)){return getPid(name);} if (isActive(name)){
if (!handler_set){ return getPid(name);
}
if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
sigemptyset(&new_action.sa_mask); sigemptyset( &new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGCHLD, &new_action, NULL); sigaction(SIGCHLD, &new_action, NULL);
handler_set = true; handler_set = true;
@ -112,14 +120,14 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){
runCmd(cmd); runCmd(cmd);
}else{ }else{
if (ret > 0){ if (ret > 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl;
#endif #endif
plist.insert(std::pair<pid_t, std::string>(ret, name)); plist.insert(std::pair<pid_t, std::string>(ret, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
return 0; return 0;
} }
} }
@ -132,20 +140,22 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){
/// \arg cmd Commandline for sub (sending) process. /// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for main (receiving) process. /// \arg cmd2 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
if (isActive(name)){return getPid(name);} if (isActive(name)){
if (!handler_set){ return getPid(name);
}
if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
sigemptyset(&new_action.sa_mask); sigemptyset( &new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGCHLD, &new_action, NULL); sigaction(SIGCHLD, &new_action, NULL);
handler_set = true; handler_set = true;
} }
int pfildes[2]; int pfildes[2];
if (pipe(pfildes) == -1){ if (pipe(pfildes) == -1){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
#endif #endif
return 0; return 0;
} }
@ -153,7 +163,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
pid_t ret = fork(); pid_t ret = fork();
if (ret == 0){ if (ret == 0){
close(pfildes[0]); close(pfildes[0]);
dup2(pfildes[1],STDOUT_FILENO); dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]); close(pfildes[1]);
dup2(devnull, STDIN_FILENO); dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
@ -162,9 +172,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
if (ret > 0){ if (ret > 0){
plist.insert(std::pair<pid_t, std::string>(ret, name)); plist.insert(std::pair<pid_t, std::string>(ret, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
return 0; return 0;
@ -174,21 +184,21 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
pid_t ret2 = fork(); pid_t ret2 = fork();
if (ret2 == 0){ if (ret2 == 0){
close(pfildes[1]); close(pfildes[1]);
dup2(pfildes[0],STDIN_FILENO); dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]); close(pfildes[0]);
dup2(devnull, STDOUT_FILENO); dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
runCmd(cmd2); runCmd(cmd2);
}else{ }else{
if (ret2 > 0){ if (ret2 > 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl;
#endif #endif
plist.insert(std::pair<pid_t, std::string>(ret2, name)); plist.insert(std::pair<pid_t, std::string>(ret2, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
Stop(name); Stop(name);
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
@ -207,11 +217,13 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
/// \arg cmd2 Commandline for sub (middle) process. /// \arg cmd2 Commandline for sub (middle) process.
/// \arg cmd3 Commandline for main (receiving) process. /// \arg cmd3 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){
if (isActive(name)){return getPid(name);} if (isActive(name)){
if (!handler_set){ return getPid(name);
}
if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
sigemptyset(&new_action.sa_mask); sigemptyset( &new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGCHLD, &new_action, NULL); sigaction(SIGCHLD, &new_action, NULL);
handler_set = true; handler_set = true;
@ -220,23 +232,23 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
int pfildes[2]; int pfildes[2];
int pfildes2[2]; int pfildes2[2];
if (pipe(pfildes) == -1){ if (pipe(pfildes) == -1){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
#endif #endif
return 0; return 0;
} }
if (pipe(pfildes2) == -1){ if (pipe(pfildes2) == -1){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
#endif #endif
return 0; return 0;
} }
int devnull = open("/dev/null", O_RDWR); int devnull = open("/dev/null", O_RDWR);
pid_t ret = fork(); pid_t ret = fork();
if (ret == 0){ if (ret == 0){
close(pfildes[0]); close(pfildes[0]);
dup2(pfildes[1],STDOUT_FILENO); dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]); close(pfildes[1]);
dup2(devnull, STDIN_FILENO); dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
@ -247,9 +259,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
if (ret > 0){ if (ret > 0){
plist.insert(std::pair<pid_t, std::string>(ret, name)); plist.insert(std::pair<pid_t, std::string>(ret, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
close(pfildes2[1]); close(pfildes2[1]);
@ -257,27 +269,27 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
return 0; return 0;
} }
} }
pid_t ret2 = fork(); pid_t ret2 = fork();
if (ret2 == 0){ if (ret2 == 0){
close(pfildes[1]); close(pfildes[1]);
close(pfildes2[0]); close(pfildes2[0]);
dup2(pfildes[0],STDIN_FILENO); dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]); close(pfildes[0]);
dup2(pfildes2[1],STDOUT_FILENO); dup2(pfildes2[1], STDOUT_FILENO);
close(pfildes2[1]); close(pfildes2[1]);
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
runCmd(cmd2); runCmd(cmd2);
}else{ }else{
if (ret2 > 0){ if (ret2 > 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl;
#endif #endif
plist.insert(std::pair<pid_t, std::string>(ret2, name)); plist.insert(std::pair<pid_t, std::string>(ret2, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
Stop(name); Stop(name);
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
@ -294,21 +306,21 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
close(pfildes2[1]); close(pfildes2[1]);
dup2(pfildes2[0],STDIN_FILENO); dup2(pfildes2[0], STDIN_FILENO);
close(pfildes2[0]); close(pfildes2[0]);
dup2(devnull, STDOUT_FILENO); dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
runCmd(cmd3); runCmd(cmd3);
}else{ }else{
if (ret3 > 0){ if (ret3 > 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl;
#endif #endif
plist.insert(std::pair<pid_t, std::string>(ret3, name)); plist.insert(std::pair<pid_t, std::string>(ret3, name));
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
#endif #endif
Stop(name); Stop(name);
close(pfildes[1]); close(pfildes[1]);
close(pfildes[0]); close(pfildes[0]);
@ -317,7 +329,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
return 0; return 0;
} }
} }
return ret3; return ret3;
} }
@ -329,118 +341,153 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
/// \arg fdout Same as fdin, but for stdout. /// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr. /// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){
if (isActive(name)){return getPid(name);} if (isActive(name)){
return getPid(name);
}
pid_t pid; pid_t pid;
int pipein[2], pipeout[2], pipeerr[2]; int pipein[2], pipeout[2], pipeerr[2];
if (!handler_set){ if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
sigemptyset(&new_action.sa_mask); sigemptyset( &new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGCHLD, &new_action, NULL); sigaction(SIGCHLD, &new_action, NULL);
handler_set = true; handler_set = true;
} }
if (fdin && *fdin == -1 && pipe(pipein) < 0){ if (fdin && *fdin == -1 && pipe(pipein) < 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Pipe (in) creation failed for " << name << std::endl; std::cerr << "Pipe (in) creation failed for " << name << std::endl;
#endif #endif
return 0; return 0;
} }
if (fdout && *fdout == -1 && pipe(pipeout) < 0) { if (fdout && *fdout == -1 && pipe(pipeout) < 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Pipe (out) creation failed for " << name << std::endl; std::cerr << "Pipe (out) creation failed for " << name << std::endl;
#endif #endif
if (*fdin == -1){close(pipein[0]);close(pipein[1]);} if ( *fdin == -1){
close(pipein[0]);
close(pipein[1]);
}
return 0; return 0;
} }
if (fderr && *fderr == -1 && pipe(pipeerr) < 0) { if (fderr && *fderr == -1 && pipe(pipeerr) < 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Pipe (err) creation failed for " << name << std::endl; std::cerr << "Pipe (err) creation failed for " << name << std::endl;
#endif #endif
if (*fdin == -1){close(pipein [0]);close(pipein [1]);} if ( *fdin == -1){
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
close(pipein[1]);
}
if ( *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
return 0; return 0;
} }
int devnull = -1; int devnull = -1;
if (!fdin || !fdout || !fderr){ if ( !fdin || !fdout || !fderr){
devnull = open("/dev/null", O_RDWR); devnull = open("/dev/null", O_RDWR);
if (devnull == -1){ if (devnull == -1){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl; std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl;
#endif #endif
if (*fdin == -1){close(pipein [0]);close(pipein [1]);} if ( *fdin == -1){
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
if (*fderr == -1){close(pipeerr[0]);close(pipeerr[1]);} close(pipein[1]);
}
if ( *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
if ( *fderr == -1){
close(pipeerr[0]);
close(pipeerr[1]);
}
return 0; return 0;
} }
} }
pid = fork(); pid = fork();
if (pid == 0){//child if (pid == 0){ //child
if (!fdin){ if ( !fdin){
dup2(devnull, STDIN_FILENO); dup2(devnull, STDIN_FILENO);
}else if (*fdin == -1){ }else if ( *fdin == -1){
close(pipein[1]);// close unused write end close(pipein[1]); // close unused write end
dup2(pipein[0], STDIN_FILENO); dup2(pipein[0], STDIN_FILENO);
close(pipein[0]); close(pipein[0]);
}else if (*fdin != STDIN_FILENO){ }else if ( *fdin != STDIN_FILENO){
dup2(*fdin, STDIN_FILENO); dup2( *fdin, STDIN_FILENO);
close(*fdin); close( *fdin);
} }
if (!fdout){ if ( !fdout){
dup2(devnull, STDOUT_FILENO); dup2(devnull, STDOUT_FILENO);
}else if (*fdout == -1){ }else if ( *fdout == -1){
close(pipeout[0]);// close unused read end close(pipeout[0]); // close unused read end
dup2(pipeout[1], STDOUT_FILENO); dup2(pipeout[1], STDOUT_FILENO);
close(pipeout[1]); close(pipeout[1]);
}else if (*fdout != STDOUT_FILENO){ }else if ( *fdout != STDOUT_FILENO){
dup2(*fdout, STDOUT_FILENO); dup2( *fdout, STDOUT_FILENO);
close(*fdout); close( *fdout);
} }
if (!fderr){ if ( !fderr){
dup2(devnull, STDERR_FILENO); dup2(devnull, STDERR_FILENO);
}else if (*fderr == -1){ }else if ( *fderr == -1){
close(pipeerr[0]);// close unused read end close(pipeerr[0]); // close unused read end
dup2(pipeerr[1], STDERR_FILENO); dup2(pipeerr[1], STDERR_FILENO);
close(pipeerr[1]); close(pipeerr[1]);
}else if (*fderr != STDERR_FILENO){ }else if ( *fderr != STDERR_FILENO){
dup2(*fderr, STDERR_FILENO); dup2( *fderr, STDERR_FILENO);
close(*fderr); close( *fderr);
}
if (devnull != -1){
close(devnull);
} }
if (devnull != -1){close(devnull);}
execvp(argv[0], argv); execvp(argv[0], argv);
#if DEBUG >= 1 #if DEBUG >= 1
perror("execvp failed"); perror("execvp failed");
#endif #endif
exit(42); exit(42);
} else if (pid == -1){ }else if (pid == -1){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Failed to fork for pipe: " << name << std::endl; std::cerr << "Failed to fork for pipe: " << name << std::endl;
#endif #endif
if (fdin && *fdin == -1){close(pipein [0]);close(pipein [1]);} if (fdin && *fdin == -1){
if (fdout && *fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
if (fderr && *fderr == -1){close(pipeerr[0]);close(pipeerr[1]);} close(pipein[1]);
if (devnull != -1){close(devnull);} }
if (fdout && *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
if (fderr && *fderr == -1){
close(pipeerr[0]);
close(pipeerr[1]);
}
if (devnull != -1){
close(devnull);
}
return 0; return 0;
} else{//parent }else{ //parent
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Piped process " << name << " started"; std::cerr << "Piped process " << name << " started";
if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin );
if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout);
if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr);
if (devnull != -1) std::cerr << " null=" << devnull; if (devnull != -1) std::cerr << " null=" << devnull;
std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; std::cerr << ", PID " << pid << ": " << argv[0] << std::endl;
#endif #endif
if (devnull != -1){close(devnull);} if (devnull != -1){
if (fdin && *fdin == -1){ close(devnull);
close(pipein[0]);// close unused end end }
if (fdin && *fdin == -1){
close(pipein[0]); // close unused end end
*fdin = pipein[1]; *fdin = pipein[1];
} }
if (fdout && *fdout == -1){ if (fdout && *fdout == -1){
close(pipeout[1]);// close unused write end close(pipeout[1]); // close unused write end
*fdout = pipeout[0]; *fdout = pipeout[0];
} }
if (fderr && *fderr == -1){ if (fderr && *fderr == -1){
close(pipeerr[1]);// close unused write end close(pipeerr[1]); // close unused write end
*fderr = pipeerr[0]; *fderr = pipeerr[0];
} }
plist.insert(std::pair<pid_t, std::string>(pid, name)); plist.insert(std::pair<pid_t, std::string>(pid, name));
@ -448,7 +495,6 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
return pid; return pid;
} }
/// Stops the named process, if running. /// Stops the named process, if running.
/// \arg name (Internal) name of process to stop /// \arg name (Internal) name of process to stop
void Util::Procs::Stop(std::string name){ void Util::Procs::Stop(std::string name){
@ -456,7 +502,9 @@ void Util::Procs::Stop(std::string name){
while (isActive(name)){ while (isActive(name)){
Stop(getPid(name)); Stop(getPid(name));
max--; max--;
if (max <= 0){return;} if (max <= 0){
return;
}
} }
} }
@ -472,20 +520,22 @@ void Util::Procs::Stop(pid_t name){
void Util::Procs::StopAll(){ void Util::Procs::StopAll(){
std::map<pid_t, std::string>::iterator it; std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++){ for (it = plist.begin(); it != plist.end(); it++){
Stop((*it).first); Stop(( *it).first);
} }
} }
/// Returns the number of active child processes. /// Returns the number of active child processes.
int Util::Procs::Count(){ int Util::Procs::Count(){
return plist.size(); return plist.size();
} }
/// Returns true if a process by this name is currently active. /// Returns true if a process by this name is currently active.
bool Util::Procs::isActive(std::string name){ bool Util::Procs::isActive(std::string name){
std::map<pid_t, std::string>::iterator it; std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++){ for (it = plist.begin(); it != plist.end(); it++){
if ((*it).second == name){return true;} if (( *it).second == name){
return true;
}
} }
return false; return false;
} }
@ -500,7 +550,9 @@ bool Util::Procs::isActive(pid_t name){
pid_t Util::Procs::getPid(std::string name){ pid_t Util::Procs::getPid(std::string name){
std::map<pid_t, std::string>::iterator it; std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++){ for (it = plist.begin(); it != plist.end(); it++){
if ((*it).second == name){return (*it).first;} if (( *it).second == name){
return ( *it).first;
}
} }
return 0; return 0;
} }

View file

@ -7,7 +7,7 @@
#include <map> #include <map>
/// Contains utility code, not directly related to streaming media /// Contains utility code, not directly related to streaming media
namespace Util{ namespace Util {
typedef void (*TerminationNotifier)(pid_t pid, int exitCode); typedef void (*TerminationNotifier)(pid_t pid, int exitCode);
@ -35,4 +35,4 @@ namespace Util{
static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier); static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier);
}; };
}; }

View file

@ -7,7 +7,7 @@
char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake
std::string RTMPStream::handshake_in; ///< Input for the handshake. std::string RTMPStream::handshake_in; ///< Input for the handshake.
std::string RTMPStream::handshake_out;///< Output for the handshake. std::string RTMPStream::handshake_out; ///< Output for the handshake.
unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_rec_max = 128;
unsigned int RTMPStream::chunk_snd_max = 128; unsigned int RTMPStream::chunk_snd_max = 128;
@ -39,19 +39,17 @@ std::map<unsigned int, RTMPStream::Chunk> RTMPStream::Chunk::lastrecv;
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
uint8_t genuineFMSKey[] = { uint8_t genuineFMSKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72,
0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, // Genuine Adobe Flash Media Server 001 0x76, // Genuine Adobe Flash Media Server 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 68
}; // 68
uint8_t genuineFPKey[] = { uint8_t genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x50, 0x6c, 0x61,
0x50, 0x6c, 0x61, 0x79, // Genuine Adobe Flash Player 001 0x79, // Genuine Adobe Flash Player 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 62
}; // 62
inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){
if (scheme == 0){ if (scheme == 0){
@ -69,7 +67,7 @@ inline uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){
} }
} }
class DHWrapper { class DHWrapper{
private: private:
int32_t _bitsCount; int32_t _bitsCount;
DH *_pDH; DH *_pDH;
@ -89,7 +87,7 @@ class DHWrapper {
bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength);
}; };
DHWrapper::DHWrapper(int32_t bitsCount) { DHWrapper::DHWrapper(int32_t bitsCount){
_bitsCount = bitsCount; _bitsCount = bitsCount;
_pDH = 0; _pDH = 0;
_pSharedKey = 0; _pSharedKey = 0;
@ -97,119 +95,172 @@ DHWrapper::DHWrapper(int32_t bitsCount) {
_peerPublickey = 0; _peerPublickey = 0;
} }
DHWrapper::~DHWrapper() { DHWrapper::~DHWrapper(){
Cleanup(); Cleanup();
} }
bool DHWrapper::Initialize() { bool DHWrapper::Initialize(){
Cleanup(); Cleanup();
_pDH = DH_new(); _pDH = DH_new();
if (!_pDH){Cleanup(); return false;} if ( !_pDH){
Cleanup();
return false;
}
_pDH->p = BN_new(); _pDH->p = BN_new();
if (!_pDH->p){Cleanup(); return false;} if ( !_pDH->p){
Cleanup();
return false;
}
_pDH->g = BN_new(); _pDH->g = BN_new();
if (!_pDH->g){Cleanup(); return false;} if ( !_pDH->g){
if (BN_hex2bn(&_pDH->p, P1024) == 0){Cleanup(); return false;} Cleanup();
if (BN_set_word(_pDH->g, 2) != 1){Cleanup(); return false;} return false;
}
if (BN_hex2bn( &_pDH->p, P1024) == 0){
Cleanup();
return false;
}
if (BN_set_word(_pDH->g, 2) != 1){
Cleanup();
return false;
}
_pDH->length = _bitsCount; _pDH->length = _bitsCount;
if (DH_generate_key(_pDH) != 1){Cleanup(); return false;} if (DH_generate_key(_pDH) != 1){
Cleanup();
return false;
}
return true; return true;
} }
bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
return false;
}
return CopyKey(_pDH->pub_key, pDst, dstLength); return CopyKey(_pDH->pub_key, pDst, dstLength);
} }
bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
return false;
}
return CopyKey(_pDH->priv_key, pDst, dstLength); return CopyKey(_pDH->priv_key, pDst, dstLength);
} }
bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length){
if (!_pDH){return false;} if ( !_pDH){
if (_sharedKeyLength != 0 || _pSharedKey){return false;} return false;
}
if (_sharedKeyLength != 0 || _pSharedKey){
return false;
}
_sharedKeyLength = DH_size(_pDH); _sharedKeyLength = DH_size(_pDH);
if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){return false;} if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){
return false;
}
_pSharedKey = new uint8_t[_sharedKeyLength]; _pSharedKey = new uint8_t[_sharedKeyLength];
_peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0);
if (!_peerPublickey){return false;} if ( !_peerPublickey){
return false;
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){return false;} }
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){
return false;
}
return true; return true;
} }
bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
if (dstLength != _sharedKeyLength){return false;} return false;
}
if (dstLength != _sharedKeyLength){
return false;
}
memcpy(pDst, _pSharedKey, _sharedKeyLength); memcpy(pDst, _pSharedKey, _sharedKeyLength);
return true; return true;
} }
void DHWrapper::Cleanup() { void DHWrapper::Cleanup(){
if (_pDH){ if (_pDH){
if (_pDH->p){BN_free(_pDH->p); _pDH->p = 0;} if (_pDH->p){
if (_pDH->g){BN_free(_pDH->g); _pDH->g = 0;} BN_free(_pDH->p);
DH_free(_pDH); _pDH = 0; _pDH->p = 0;
}
if (_pDH->g){
BN_free(_pDH->g);
_pDH->g = 0;
}
DH_free(_pDH);
_pDH = 0;
}
if (_pSharedKey){
delete[] _pSharedKey;
_pSharedKey = 0;
} }
if (_pSharedKey){delete[] _pSharedKey; _pSharedKey = 0;}
_sharedKeyLength = 0; _sharedKeyLength = 0;
if (_peerPublickey){BN_free(_peerPublickey); _peerPublickey = 0;} if (_peerPublickey){
BN_free(_peerPublickey);
_peerPublickey = 0;
}
} }
bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength){
int32_t keySize = BN_num_bytes(pNum); int32_t keySize = BN_num_bytes(pNum);
if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){return false;} if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){
if (BN_bn2bin(pNum, pDst) != keySize){return false;} return false;
}
if (BN_bn2bin(pNum, pDst) != keySize){
return false;
}
return true; return true;
} }
void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut){
uint8_t digest[SHA256_DIGEST_LENGTH]; uint8_t digest[SHA256_DIGEST_LENGTH];
unsigned int digestLen = 0; unsigned int digestLen = 0;
HMAC_CTX ctx; HMAC_CTX ctx;
HMAC_CTX_init(&ctx); HMAC_CTX_init( &ctx);
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0);
HMAC_Update(&ctx, pubKeyIn, 128); HMAC_Update( &ctx, pubKeyIn, 128);
HMAC_Final(&ctx, digest, &digestLen); HMAC_Final( &ctx, digest, &digestLen);
HMAC_CTX_cleanup(&ctx); HMAC_CTX_cleanup( &ctx);
RC4_set_key(rc4keyOut, 16, digest); RC4_set_key(rc4keyOut, 16, digest);
HMAC_CTX_init(&ctx); HMAC_CTX_init( &ctx);
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0);
HMAC_Update(&ctx, pubKeyOut, 128); HMAC_Update( &ctx, pubKeyOut, 128);
HMAC_Final(&ctx, digest, &digestLen); HMAC_Final( &ctx, digest, &digestLen);
HMAC_CTX_cleanup(&ctx); HMAC_CTX_cleanup( &ctx);
RC4_set_key(rc4keyIn, 16, digest); RC4_set_key(rc4keyIn, 16, digest);
} }
void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult){
unsigned int digestLen; unsigned int digestLen;
HMAC_CTX ctx; HMAC_CTX ctx;
HMAC_CTX_init(&ctx); HMAC_CTX_init( &ctx);
HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), 0); HMAC_Init_ex( &ctx, (unsigned char*)pKey, keyLength, EVP_sha256(), 0);
HMAC_Update(&ctx, (unsigned char *) pData, dataLength); HMAC_Update( &ctx, (unsigned char *)pData, dataLength);
HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); HMAC_Final( &ctx, (unsigned char *)pResult, &digestLen);
HMAC_CTX_cleanup(&ctx); HMAC_CTX_cleanup( &ctx);
} }
bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme){
uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
uint8_t *pTempBuffer = new uint8_t[1536 - 32]; uint8_t *pTempBuffer = new uint8_t[1536 - 32];
memcpy(pTempBuffer, pBuffer, clientDigestOffset); memcpy(pTempBuffer, pBuffer, clientDigestOffset);
memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32);
uint8_t *pTempHash = new uint8_t[512]; uint8_t *pTempHash = new uint8_t[512];
HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0);
#if DEBUG >= 4 #if DEBUG >= 4
fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed");
#endif #endif
delete[] pTempBuffer; delete[] pTempBuffer;
delete[] pTempHash; delete[] pTempHash;
return result; return result;
@ -226,23 +277,25 @@ std::string & RTMPStream::Chunk::Pack(){
unsigned char chtype = 0x00; unsigned char chtype = 0x00;
if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){
if (msg_stream_id == prev.msg_stream_id){ if (msg_stream_id == prev.msg_stream_id){
chtype = 0x40;//do not send msg_stream_id chtype = 0x40; //do not send msg_stream_id
if (len == prev.len){ if (len == prev.len){
if (msg_type_id == prev.msg_type_id){ if (msg_type_id == prev.msg_type_id){
chtype = 0x80;//do not send len and msg_type_id chtype = 0x80; //do not send len and msg_type_id
if (timestamp == prev.timestamp){ if (timestamp == prev.timestamp){
chtype = 0xC0;//do not send timestamp chtype = 0xC0; //do not send timestamp
} }
} }
} }
} }
//override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel //override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel
if (timestamp < prev.timestamp){chtype = 0x00;} if (timestamp < prev.timestamp){
chtype = 0x00;
}
} }
if (cs_id <= 63){ if (cs_id <= 63){
output += (unsigned char)(chtype | cs_id); output += (unsigned char)(chtype | cs_id);
}else{ }else{
if (cs_id <= 255+64){ if (cs_id <= 255 + 64){
output += (unsigned char)(chtype | 0); output += (unsigned char)(chtype | 0);
output += (unsigned char)(cs_id - 64); output += (unsigned char)(cs_id - 64);
}else{ }else{
@ -259,7 +312,10 @@ std::string & RTMPStream::Chunk::Pack(){
}else{ }else{
tmpi = timestamp - prev.timestamp; tmpi = timestamp - prev.timestamp;
} }
if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} if (tmpi >= 0x00ffffff){
ntime = tmpi;
tmpi = 0x00ffffff;
}
output += (unsigned char)((tmpi >> 16) & 0xff); output += (unsigned char)((tmpi >> 16) & 0xff);
output += (unsigned char)((tmpi >> 8) & 0xff); output += (unsigned char)((tmpi >> 8) & 0xff);
output += (unsigned char)(tmpi & 0xff); output += (unsigned char)(tmpi & 0xff);
@ -275,8 +331,8 @@ std::string & RTMPStream::Chunk::Pack(){
//msg stream id //msg stream id
output += (unsigned char)(msg_stream_id % 256); output += (unsigned char)(msg_stream_id % 256);
output += (unsigned char)(msg_stream_id / 256); output += (unsigned char)(msg_stream_id / 256);
output += (unsigned char)(msg_stream_id / (256*256)); output += (unsigned char)(msg_stream_id / (256 * 256));
output += (unsigned char)(msg_stream_id / (256*256*256)); output += (unsigned char)(msg_stream_id / (256 * 256 * 256));
} }
} }
} }
@ -290,14 +346,16 @@ std::string & RTMPStream::Chunk::Pack(){
len_left = 0; len_left = 0;
while (len_left < len){ while (len_left < len){
tmpi = len - len_left; tmpi = len - len_left;
if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} if (tmpi > RTMPStream::chunk_snd_max){
tmpi = RTMPStream::chunk_snd_max;
}
output.append(data, len_left, tmpi); output.append(data, len_left, tmpi);
len_left += tmpi; len_left += tmpi;
if (len_left < len){ if (len_left < len){
if (cs_id <= 63){ if (cs_id <= 63){
output += (unsigned char)(0xC0 + cs_id); output += (unsigned char)(0xC0 + cs_id);
}else{ }else{
if (cs_id <= 255+64){ if (cs_id <= 255 + 64){
output += (unsigned char)(0xC0); output += (unsigned char)(0xC0);
output += (unsigned char)(cs_id - 64); output += (unsigned char)(cs_id - 64);
}else{ }else{
@ -311,10 +369,11 @@ std::string & RTMPStream::Chunk::Pack(){
lastsend[cs_id] = *this; lastsend[cs_id] = *this;
RTMPStream::snd_cnt += output.size(); RTMPStream::snd_cnt += output.size();
return output; return output;
}//SendChunk } //SendChunk
/// Default contructor, creates an empty chunk with all values initialized to zero. /// Default constructor, creates an empty chunk with all values initialized to zero.
RTMPStream::Chunk::Chunk(){ RTMPStream::Chunk::Chunk(){
headertype = 0;
cs_id = 0; cs_id = 0;
timestamp = 0; timestamp = 0;
len = 0; len = 0;
@ -323,7 +382,7 @@ RTMPStream::Chunk::Chunk(){
msg_type_id = 0; msg_type_id = 0;
msg_stream_id = 0; msg_stream_id = 0;
data = ""; data = "";
}//constructor } //constructor
/// Packs up a chunk with the given arguments as properties. /// Packs up a chunk with the given arguments as properties.
std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){
@ -337,7 +396,7 @@ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_i
ch.msg_stream_id = msg_stream_id; ch.msg_stream_id = msg_stream_id;
ch.data = data; ch.data = data;
return ch.Pack(); return ch.Pack();
}//constructor } //constructor
/// Packs up a chunk with media contents. /// Packs up a chunk with media contents.
/// \param msg_type_id Type number of the media, as per FLV standard. /// \param msg_type_id Type number of the media, as per FLV standard.
@ -346,7 +405,7 @@ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_i
/// \param ts Timestamp of the media data, relative to current system time. /// \param ts Timestamp of the media data, relative to current system time.
std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){
static RTMPStream::Chunk ch; static RTMPStream::Chunk ch;
ch.cs_id = msg_type_id+42; ch.cs_id = msg_type_id + 42;
ch.timestamp = ts; ch.timestamp = ts;
ch.len = len; ch.len = len;
ch.real_len = len; ch.real_len = len;
@ -355,7 +414,7 @@ std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * d
ch.msg_stream_id = 1; ch.msg_stream_id = 1;
ch.data = std::string((char*)data, (size_t)len); ch.data = std::string((char*)data, (size_t)len);
return ch.Pack(); return ch.Pack();
}//SendMedia } //SendMedia
/// Packs up a chunk with media contents. /// Packs up a chunk with media contents.
/// \param tag FLV::Tag with media to send. /// \param tag FLV::Tag with media to send.
@ -363,14 +422,14 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag){
static RTMPStream::Chunk ch; static RTMPStream::Chunk ch;
ch.cs_id = ((unsigned char)tag.data[0]); ch.cs_id = ((unsigned char)tag.data[0]);
ch.timestamp = tag.tagTime(); ch.timestamp = tag.tagTime();
ch.len = tag.len-15; ch.len = tag.len - 15;
ch.real_len = tag.len-15; ch.real_len = tag.len - 15;
ch.len_left = 0; ch.len_left = 0;
ch.msg_type_id = (unsigned char)tag.data[0]; ch.msg_type_id = (unsigned char)tag.data[0];
ch.msg_stream_id = 1; ch.msg_stream_id = 1;
ch.data = std::string(tag.data+11, (size_t)(tag.len-15)); ch.data = std::string(tag.data + 11, (size_t)(tag.len - 15));
return ch.Pack(); return ch.Pack();
}//SendMedia } //SendMedia
/// Packs up a chunk for a control message with 1 argument. /// Packs up a chunk for a control message with 1 argument.
std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){
@ -385,7 +444,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){
ch.data.resize(4); ch.data.resize(4);
*(int*)((char*)ch.data.c_str()) = htonl(data); *(int*)((char*)ch.data.c_str()) = htonl(data);
return ch.Pack(); return ch.Pack();
}//SendCTL } //SendCTL
/// Packs up a chunk for a control message with 2 arguments. /// Packs up a chunk for a control message with 2 arguments.
std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){
@ -401,7 +460,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigne
*(unsigned int*)((char*)ch.data.c_str()) = htonl(data); *(unsigned int*)((char*)ch.data.c_str()) = htonl(data);
ch.data[4] = data2; ch.data[4] = data2;
return ch.Pack(); return ch.Pack();
}//SendCTL } //SendCTL
/// Packs up a chunk for a user control message with 1 argument. /// Packs up a chunk for a user control message with 1 argument.
std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){
@ -414,11 +473,11 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){
ch.msg_type_id = 4; ch.msg_type_id = 4;
ch.msg_stream_id = 0; ch.msg_stream_id = 0;
ch.data.resize(6); ch.data.resize(6);
*(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); *(unsigned int*)(((char*)ch.data.c_str()) + 2) = htonl(data);
ch.data[0] = 0; ch.data[0] = 0;
ch.data[1] = type; ch.data[1] = type;
return ch.Pack(); return ch.Pack();
}//SendUSR } //SendUSR
/// Packs up a chunk for a user control message with 2 arguments. /// Packs up a chunk for a user control message with 2 arguments.
std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){
@ -431,13 +490,12 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne
ch.msg_type_id = 4; ch.msg_type_id = 4;
ch.msg_stream_id = 0; ch.msg_stream_id = 0;
ch.data.resize(10); ch.data.resize(10);
*(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); *(unsigned int*)(((char*)ch.data.c_str()) + 2) = htonl(data);
*(unsigned int*)(((char*)ch.data.c_str())+6) = htonl(data2); *(unsigned int*)(((char*)ch.data.c_str()) + 6) = htonl(data2);
ch.data[0] = 0; ch.data[0] = 0;
ch.data[1] = type; ch.data[1] = type;
return ch.Pack(); return ch.Pack();
}//SendUSR } //SendUSR
/// Parses the argument string into the current chunk. /// Parses the argument string into the current chunk.
/// Tries to read a whole chunk, removing data from the input string as it reads. /// Tries to read a whole chunk, removing data from the input string as it reads.
@ -448,21 +506,21 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne
/// \warning This function will destroy the current data in this chunk! /// \warning This function will destroy the current data in this chunk!
/// \returns True if a whole chunk could be read, false otherwise. /// \returns True if a whole chunk could be read, false otherwise.
bool RTMPStream::Chunk::Parse(std::string & indata){ bool RTMPStream::Chunk::Parse(std::string & indata){
gettimeofday(&RTMPStream::lastrec, 0); gettimeofday( &RTMPStream::lastrec, 0);
unsigned int i = 0; unsigned int i = 0;
if (indata.size() < 1) return false;//need at least a byte if (indata.size() < 1) return false; //need at least a byte
unsigned char chunktype = indata[i++]; unsigned char chunktype = indata[i++ ];
//read the chunkstream ID properly //read the chunkstream ID properly
switch (chunktype & 0x3F){ switch (chunktype & 0x3F){
case 0: case 0:
if (indata.size() < 2) return false;//need at least 2 bytes to continue if (indata.size() < 2) return false; //need at least 2 bytes to continue
cs_id = indata[i++] + 64; cs_id = indata[i++ ] + 64;
break; break;
case 1: case 1:
if (indata.size() < 3) return false;//need at least 3 bytes to continue if (indata.size() < 3) return false; //need at least 3 bytes to continue
cs_id = indata[i++] + 64; cs_id = indata[i++ ] + 64;
cs_id += indata[i++] * 256; cs_id += indata[i++ ] * 256;
break; break;
default: default:
cs_id = chunktype & 0x3F; cs_id = chunktype & 0x3F;
@ -475,48 +533,58 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
headertype = chunktype & 0xC0; headertype = chunktype & 0xC0;
switch (headertype){ switch (headertype){
case 0x00: case 0x00:
if (indata.size() < i+11) return false; //can't read whole header if (indata.size() < i + 11) return false; //can't read whole header
timestamp = indata[i++]*256*256; timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++]*256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++]; timestamp += indata[i++ ];
len = indata[i++]*256*256; len = indata[i++ ] * 256 * 256;
len += indata[i++]*256; len += indata[i++ ] * 256;
len += indata[i++]; len += indata[i++ ];
len_left = 0; len_left = 0;
msg_type_id = indata[i++]; msg_type_id = indata[i++ ];
msg_stream_id = indata[i++]; msg_stream_id = indata[i++ ];
msg_stream_id += indata[i++]*256; msg_stream_id += indata[i++ ] * 256;
msg_stream_id += indata[i++]*256*256; msg_stream_id += indata[i++ ] * 256 * 256;
msg_stream_id += indata[i++]*256*256*256; msg_stream_id += indata[i++ ] * 256 * 256 * 256;
break; break;
case 0x40: case 0x40:
if (indata.size() < i+7) return false; //can't read whole header if (indata.size() < i + 7) return false; //can't read whole header
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
timestamp = indata[i++]*256*256; fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");
timestamp += indata[i++]*256; }
timestamp += indata[i++]; timestamp = indata[i++ ] * 256 * 256;
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} timestamp += indata[i++ ] * 256;
len = indata[i++]*256*256; timestamp += indata[i++ ];
len += indata[i++]*256; if (timestamp != 0x00ffffff){
len += indata[i++]; timestamp += prev.timestamp;
}
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0; len_left = 0;
msg_type_id = indata[i++]; msg_type_id = indata[i++ ];
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0x80: case 0x80:
if (indata.size() < i+3) return false; //can't read whole header if (indata.size() < i + 3) return false; //can't read whole header
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
timestamp = indata[i++]*256*256; fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");
timestamp += indata[i++]*256; }
timestamp += indata[i++]; timestamp = indata[i++ ] * 256 * 256;
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
msg_type_id = prev.msg_type_id; msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0xC0: case 0xC0:
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");
}
timestamp = prev.timestamp; timestamp = prev.timestamp;
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
@ -537,11 +605,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
} }
//read extended timestamp, if neccesary //read extended timestamp, if neccesary
if (timestamp == 0x00ffffff){ if (timestamp == 0x00ffffff){
if (indata.size() < i+4) return false; //can't read whole header if (indata.size() < i + 4) return false; //can't read whole header
timestamp = indata[i++]*256*256*256; timestamp = indata[i++ ] * 256 * 256 * 256;
timestamp += indata[i++]*256*256; timestamp += indata[i++ ] * 256 * 256;
timestamp += indata[i++]*256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++]; timestamp += indata[i++ ];
} }
//read data if length > 0, and allocate it //read data if length > 0, and allocate it
@ -551,11 +619,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
}else{ }else{
data = ""; data = "";
} }
if (indata.size() < i+real_len) return false;//can't read all data (yet) if (indata.size() < i + real_len) return false; //can't read all data (yet)
data.append(indata, i, real_len); data.append(indata, i, real_len);
indata = indata.substr(i+real_len); indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this; lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i+real_len; RTMPStream::rec_cnt += i + real_len;
if (len_left == 0){ if (len_left == 0){
return true; return true;
}else{ }else{
@ -563,12 +631,12 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
} }
}else{ }else{
data = ""; data = "";
indata = indata.substr(i+real_len); indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this; lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i+real_len; RTMPStream::rec_cnt += i + real_len;
return true; return true;
} }
}//Parse } //Parse
/// Parses the argument string into the current chunk. /// Parses the argument string into the current chunk.
/// Tries to read a whole chunk, removing data from the input as it reads. /// Tries to read a whole chunk, removing data from the input as it reads.
@ -579,77 +647,95 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
/// \warning This function will destroy the current data in this chunk! /// \warning This function will destroy the current data in this chunk!
/// \returns True if a whole chunk could be read, false otherwise. /// \returns True if a whole chunk could be read, false otherwise.
bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
gettimeofday(&RTMPStream::lastrec, 0); gettimeofday( &RTMPStream::lastrec, 0);
unsigned int i = 0; unsigned int i = 0;
if (!buffer.available(3)){return false;}//we want at least 3 bytes if ( !buffer.available(3)){
return false;
} //we want at least 3 bytes
std::string indata = buffer.copy(3); std::string indata = buffer.copy(3);
unsigned char chunktype = indata[i++]; unsigned char chunktype = indata[i++ ];
//read the chunkstream ID properly //read the chunkstream ID properly
switch (chunktype & 0x3F){ switch (chunktype & 0x3F){
case 0: case 0:
cs_id = indata[i++] + 64; cs_id = indata[i++ ] + 64;
break; break;
case 1: case 1:
cs_id = indata[i++] + 64; cs_id = indata[i++ ] + 64;
cs_id += indata[i++] * 256; cs_id += indata[i++ ] * 256;
break; break;
default: default:
cs_id = chunktype & 0x3F; cs_id = chunktype & 0x3F;
break; break;
} }
RTMPStream::Chunk prev = lastrecv[cs_id]; RTMPStream::Chunk prev = lastrecv[cs_id];
//process the rest of the header, for each chunk type //process the rest of the header, for each chunk type
headertype = chunktype & 0xC0; headertype = chunktype & 0xC0;
switch (headertype){ switch (headertype){
case 0x00: case 0x00:
if (!buffer.available(i+11)){return false;} //can't read whole header if ( !buffer.available(i + 11)){
indata = buffer.copy(i+11); return false;
timestamp = indata[i++]*256*256; } //can't read whole header
timestamp += indata[i++]*256; indata = buffer.copy(i + 11);
timestamp += indata[i++]; timestamp = indata[i++ ] * 256 * 256;
len = indata[i++]*256*256; timestamp += indata[i++ ] * 256;
len += indata[i++]*256; timestamp += indata[i++ ];
len += indata[i++]; len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0; len_left = 0;
msg_type_id = indata[i++]; msg_type_id = indata[i++ ];
msg_stream_id = indata[i++]; msg_stream_id = indata[i++ ];
msg_stream_id += indata[i++]*256; msg_stream_id += indata[i++ ] * 256;
msg_stream_id += indata[i++]*256*256; msg_stream_id += indata[i++ ] * 256 * 256;
msg_stream_id += indata[i++]*256*256*256; msg_stream_id += indata[i++ ] * 256 * 256 * 256;
break; break;
case 0x40: case 0x40:
if (!buffer.available(i+7)){return false;} //can't read whole header if ( !buffer.available(i + 7)){
indata = buffer.copy(i+7); return false;
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} } //can't read whole header
timestamp = indata[i++]*256*256; indata = buffer.copy(i + 7);
timestamp += indata[i++]*256; if (prev.msg_type_id == 0){
timestamp += indata[i++]; fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} }
len = indata[i++]*256*256; timestamp = indata[i++ ] * 256 * 256;
len += indata[i++]*256; timestamp += indata[i++ ] * 256;
len += indata[i++]; timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0; len_left = 0;
msg_type_id = indata[i++]; msg_type_id = indata[i++ ];
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0x80: case 0x80:
if (!buffer.available(i+3)){return false;} //can't read whole header if ( !buffer.available(i + 3)){
indata = buffer.copy(i+3); return false;
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} } //can't read whole header
timestamp = indata[i++]*256*256; indata = buffer.copy(i + 3);
timestamp += indata[i++]*256; if (prev.msg_type_id == 0){
timestamp += indata[i++]; fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} }
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
msg_type_id = prev.msg_type_id; msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0xC0: case 0xC0:
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");
}
timestamp = prev.timestamp; timestamp = prev.timestamp;
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
@ -670,39 +756,43 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
} }
//read extended timestamp, if neccesary //read extended timestamp, if neccesary
if (timestamp == 0x00ffffff){ if (timestamp == 0x00ffffff){
if (!buffer.available(i+4)){return false;} //can't read timestamp if ( !buffer.available(i + 4)){
indata = buffer.copy(i+4); return false;
timestamp = indata[i++]*256*256*256; } //can't read timestamp
timestamp += indata[i++]*256*256; indata = buffer.copy(i + 4);
timestamp += indata[i++]*256; timestamp = indata[i++ ] * 256 * 256 * 256;
timestamp += indata[i++]; timestamp += indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
} }
//read data if length > 0, and allocate it //read data if length > 0, and allocate it
if (real_len > 0){ if (real_len > 0){
if (!buffer.available(i+real_len)){return false;}//can't read all data (yet) if ( !buffer.available(i + real_len)){
buffer.remove(i);//remove the header return false;
} //can't read all data (yet)
buffer.remove(i); //remove the header
if (prev.len_left > 0){ if (prev.len_left > 0){
data = prev.data + buffer.remove(real_len);//append the data and remove from buffer data = prev.data + buffer.remove(real_len); //append the data and remove from buffer
}else{ }else{
data = buffer.remove(real_len);//append the data and remove from buffer data = buffer.remove(real_len); //append the data and remove from buffer
} }
lastrecv[cs_id] = *this; lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i+real_len; RTMPStream::rec_cnt += i + real_len;
if (len_left == 0){ if (len_left == 0){
return true; return true;
}else{ }else{
return Parse(buffer); return Parse(buffer);
} }
}else{ }else{
buffer.remove(i);//remove the header buffer.remove(i); //remove the header
data = ""; data = "";
indata = indata.substr(i+real_len); indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this; lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i+real_len; RTMPStream::rec_cnt += i + real_len;
return true; return true;
} }
}//Parse } //Parse
/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out.
/// After calling this function, don't forget to read and ignore 1536 extra bytes, /// After calling this function, don't forget to read and ignore 1536 extra bytes,
@ -718,21 +808,23 @@ bool RTMPStream::doHandshake(){
RTMPStream::rec_cnt += 1537; RTMPStream::rec_cnt += 1537;
//Build S1 Packet //Build S1 Packet
*((uint32_t*)Server) = 0;//time zero *((uint32_t*)Server) = 0; //time zero
*(((uint32_t*)(Server+4))) = htonl(0x01020304);//version 1 2 3 4 *(((uint32_t*)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4
for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%16];}//"random" data for (int i = 8; i < 3072; ++i){
Server[i] = versionstring[i % 16];
} //"random" data
bool encrypted = (Version == 6); bool encrypted = (Version == 6);
#if DEBUG >= 4 #if DEBUG >= 4
fprintf(stderr, "Handshake version is %hhi\n", Version); fprintf(stderr, "Handshake version is %hhi\n", Version);
#endif #endif
uint8_t _validationScheme = 5; uint8_t _validationScheme = 5;
if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 0)) _validationScheme = 0;
if (ValidateClientScheme(Client, 1)) _validationScheme = 1; if (ValidateClientScheme(Client, 1)) _validationScheme = 1;
#if DEBUG >= 4 #if DEBUG >= 4
fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off");
#endif #endif
//FIRST 1536 bytes from server response //FIRST 1536 bytes from server response
//compute DH key position //compute DH key position
@ -741,19 +833,19 @@ bool RTMPStream::doHandshake(){
//generate DH key //generate DH key
DHWrapper dhWrapper(1024); DHWrapper dhWrapper(1024);
if (!dhWrapper.Initialize()) return false; if ( !dhWrapper.Initialize()) return false;
if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; if ( !dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false;
if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; if ( !dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false;
if (encrypted) { if (encrypted){
uint8_t secretKey[128]; uint8_t secretKey[128];
if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; if ( !dhWrapper.CopySharedKey(secretKey, sizeof(secretKey))) return false;
RC4_KEY _pKeyIn; RC4_KEY _pKeyIn;
RC4_KEY _pKeyOut; RC4_KEY _pKeyOut;
InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut); InitRC4Encryption(secretKey, (uint8_t*) &Client[clientDHOffset], (uint8_t*) &Server[serverDHOffset], &_pKeyIn, &_pKeyOut);
uint8_t data[1536]; uint8_t data[1536];
RC4(&_pKeyIn, 1536, data, data); RC4( &_pKeyIn, 1536, data, data);
RC4(&_pKeyOut, 1536, data, data); RC4( &_pKeyOut, 1536, data, data);
} }
//generate the digest //generate the digest
uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme);
@ -776,7 +868,7 @@ bool RTMPStream::doHandshake(){
delete[] pTempHash; delete[] pTempHash;
delete[] pLastHash; delete[] pLastHash;
//DONE BUILDING THE RESPONSE ***// //DONE BUILDING THE RESPONSE ***//
Server[-1] = Version; Server[ -1] = Version;
RTMPStream::snd_cnt += 3073; RTMPStream::snd_cnt += 3073;
return true; return true;
} }

View file

@ -11,12 +11,12 @@
#include "socket.h" #include "socket.h"
//forward declaration of FLV::Tag to avoid circular dependencies. //forward declaration of FLV::Tag to avoid circular dependencies.
namespace FLV{ namespace FLV {
class Tag; class Tag;
}; }
/// Contains all functions and classes needed for RTMP connections. /// Contains all functions and classes needed for RTMP connections.
namespace RTMPStream{ namespace RTMPStream {
extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk. extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk.
extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk. extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk.
@ -50,7 +50,8 @@ namespace RTMPStream{
private: private:
static std::map<unsigned int, Chunk> lastsend; static std::map<unsigned int, Chunk> lastsend;
static std::map<unsigned int, Chunk> lastrecv; static std::map<unsigned int, Chunk> lastrecv;
};//RTMPStream::Chunk };
//RTMPStream::Chunk
std::string & SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); std::string & SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data);
std::string & SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); std::string & SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts);
@ -66,4 +67,4 @@ namespace RTMPStream{
extern std::string handshake_out; extern std::string handshake_out;
/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out.
bool doHandshake(); bool doHandshake();
};//RTMPStream namespace } //RTMPStream namespace

View file

@ -16,7 +16,6 @@
#define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB #define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB
#include <iostream>//temporary for debugging #include <iostream>//temporary for debugging
std::string uint2string(unsigned int i){ std::string uint2string(unsigned int i){
std::stringstream st; std::stringstream st;
st << i; st << i;
@ -27,7 +26,9 @@ std::string uint2string(unsigned int i){
/// The back is popped as long as it is empty, first - this way this function is /// The back is popped as long as it is empty, first - this way this function is
/// guaranteed to return 0 if the buffer is empty. /// guaranteed to return 0 if the buffer is empty.
unsigned int Socket::Buffer::size(){ unsigned int Socket::Buffer::size(){
while (data.size() > 0 && data.back().empty()){data.pop_back();} while (data.size() > 0 && data.back().empty()){
data.pop_back();
}
return data.size(); return data.size();
} }
@ -45,10 +46,12 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
j = i; j = i;
while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){ while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){
j++; j++;
if (newdata[j-1] == '\n'){break;} if (newdata[j - 1] == '\n'){
break;
}
} }
if (i != j){ if (i != j){
data.push_front(std::string(newdata+i, (size_t)(j - i))); data.push_front(std::string(newdata + i, (size_t)(j - i)));
i = j; i = j;
}else{ }else{
break; break;
@ -63,8 +66,10 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
bool Socket::Buffer::available(unsigned int count){ bool Socket::Buffer::available(unsigned int count){
unsigned int i = 0; unsigned int i = 0;
for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){ for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){
i += (*it).size(); i += ( *it).size();
if (i >= count){return true;} if (i >= count){
return true;
}
} }
return false; return false;
} }
@ -72,18 +77,20 @@ bool Socket::Buffer::available(unsigned int count){
/// Removes count bytes from the buffer, returning them by value. /// Removes count bytes from the buffer, returning them by value.
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::remove(unsigned int count){ std::string Socket::Buffer::remove(unsigned int count){
if (!available(count)){return "";} if ( !available(count)){
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
ret.reserve(count); ret.reserve(count);
for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){
if (i + (*it).size() < count){ if (i + ( *it).size() < count){
ret.append(*it); ret.append( *it);
i += (*it).size(); i += ( *it).size();
(*it).clear(); ( *it).clear();
}else{ }else{
ret.append(*it, 0, count - i); ret.append( *it, 0, count - i);
(*it).erase(0, count - i); ( *it).erase(0, count - i);
break; break;
} }
} }
@ -93,15 +100,17 @@ std::string Socket::Buffer::remove(unsigned int count){
/// Copies count bytes from the buffer, returning them by value. /// Copies count bytes from the buffer, returning them by value.
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::copy(unsigned int count){ std::string Socket::Buffer::copy(unsigned int count){
if (!available(count)){return "";} if ( !available(count)){
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){
if (i + (*it).size() < count){ if (i + ( *it).size() < count){
ret.append(*it); ret.append( *it);
i += (*it).size(); i += ( *it).size();
}else{ }else{
ret.append(*it, 0, count - i); ret.append( *it, 0, count - i);
break; break;
} }
} }
@ -118,7 +127,6 @@ std::string & Socket::Buffer::get(){
} }
} }
/// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection.
/// \param sockNo Integer representing the socket to convert. /// \param sockNo Integer representing the socket to convert.
Socket::Connection::Connection(int sockNo){ Socket::Connection::Connection(int sockNo){
@ -130,7 +138,7 @@ Socket::Connection::Connection(int sockNo){
conntime = Util::epoch(); conntime = Util::epoch();
Error = false; Error = false;
Blocking = false; Blocking = false;
}//Socket::Connection basic constructor } //Socket::Connection basic constructor
/// Simulate a socket using two file descriptors. /// Simulate a socket using two file descriptors.
/// \param write The filedescriptor to write to. /// \param write The filedescriptor to write to.
@ -144,7 +152,7 @@ Socket::Connection::Connection(int write, int read){
conntime = Util::epoch(); conntime = Util::epoch();
Error = false; Error = false;
Blocking = false; Blocking = false;
}//Socket::Connection basic constructor } //Socket::Connection basic constructor
/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. /// Create a new disconnected base socket. This is a basic constructor for placeholder purposes.
/// A socket created like this is always disconnected and should/could be overwritten at some point. /// A socket created like this is always disconnected and should/could be overwritten at some point.
@ -157,12 +165,12 @@ Socket::Connection::Connection(){
conntime = Util::epoch(); conntime = Util::epoch();
Error = false; Error = false;
Blocking = false; Blocking = false;
}//Socket::Connection basic constructor } //Socket::Connection basic constructor
/// Internally used call to make an file descriptor blocking or not. /// Internally used call to make an file descriptor blocking or not.
void setFDBlocking(int FD, bool blocking){ void setFDBlocking(int FD, bool blocking){
int flags = fcntl(FD, F_GETFL, 0); int flags = fcntl(FD, F_GETFL, 0);
if (!blocking){ if ( !blocking){
flags |= O_NONBLOCK; flags |= O_NONBLOCK;
}else{ }else{
flags &= !O_NONBLOCK; flags &= !O_NONBLOCK;
@ -172,44 +180,57 @@ void setFDBlocking(int FD, bool blocking){
/// Set this socket to be blocking (true) or nonblocking (false). /// Set this socket to be blocking (true) or nonblocking (false).
void Socket::Connection::setBlocking(bool blocking){ void Socket::Connection::setBlocking(bool blocking){
if (sock >=0){setFDBlocking(sock, blocking);} if (sock >= 0){
if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);} setFDBlocking(sock, blocking);
if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);} }
if (pipes[0] >= 0){
setFDBlocking(pipes[0], blocking);
}
if (pipes[1] >= 0){
setFDBlocking(pipes[1], blocking);
}
} }
/// Close connection. The internal socket is closed and then set to -1. /// Close connection. The internal socket is closed and then set to -1.
/// If the connection is already closed, nothing happens. /// If the connection is already closed, nothing happens.
void Socket::Connection::close(){ void Socket::Connection::close(){
if (connected()){ if (connected()){
#if DEBUG >= 6 #if DEBUG >= 6
fprintf(stderr, "Socket closed.\n"); fprintf(stderr, "Socket closed.\n");
#endif #endif
if (sock != -1){ if (sock != -1){
shutdown(sock, SHUT_RDWR); shutdown(sock, SHUT_RDWR);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR){} while (::close(sock) != 0 && errno == EINTR){
}
sock = -1; sock = -1;
} }
if (pipes[0] != -1){ if (pipes[0] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[0]) != 0 && errno == EINTR){} while (::close(pipes[0]) != 0 && errno == EINTR){
}
pipes[0] = -1; pipes[0] = -1;
} }
if (pipes[1] != -1){ if (pipes[1] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[1]) != 0 && errno == EINTR){} while (::close(pipes[1]) != 0 && errno == EINTR){
}
pipes[1] = -1; pipes[1] = -1;
} }
} }
}//Socket::Connection::close } //Socket::Connection::close
/// Returns internal socket number. /// Returns internal socket number.
int Socket::Connection::getSocket(){return sock;} int Socket::Connection::getSocket(){
return sock;
}
/// Returns a string describing the last error that occured. /// Returns a string describing the last error that occured.
/// Simply calls strerror(errno) - not very reliable! /// Simply calls strerror(errno) - not very reliable!
/// \todo Improve getError at some point to be more reliable and only report socket errors. /// \todo Improve getError at some point to be more reliable and only report socket errors.
std::string Socket::Connection::getError(){return strerror(errno);} std::string Socket::Connection::getError(){
return strerror(errno);
}
/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. /// Create a new Unix Socket. This socket will (try to) connect to the given address right away.
/// \param address String containing the location of the Unix socket to connect to. /// \param address String containing the location of the Unix socket to connect to.
@ -219,9 +240,9 @@ Socket::Connection::Connection(std::string address, bool nonblock){
pipes[1] = -1; pipes[1] = -1;
sock = socket(PF_UNIX, SOCK_STREAM, 0); sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0){ if (sock < 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno));
#endif #endif
return; return;
} }
Error = false; Error = false;
@ -231,8 +252,8 @@ Socket::Connection::Connection(std::string address, bool nonblock){
conntime = Util::epoch(); conntime = Util::epoch();
sockaddr_un addr; sockaddr_un addr;
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, address.c_str(), address.size()+1); strncpy(addr.sun_path, address.c_str(), address.size() + 1);
int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); int r = connect(sock, (sockaddr*) &addr, sizeof(addr));
if (r == 0){ if (r == 0){
if (nonblock){ if (nonblock){
int flags = fcntl(sock, F_GETFL, 0); int flags = fcntl(sock, F_GETFL, 0);
@ -240,12 +261,12 @@ Socket::Connection::Connection(std::string address, bool nonblock){
fcntl(sock, F_SETFL, flags); fcntl(sock, F_SETFL, flags);
} }
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno));
#endif #endif
close(); close();
} }
}//Socket::Connection Unix Contructor } //Socket::Connection Unix Contructor
/// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. /// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away.
/// \param host String containing the hostname to connect to. /// \param host String containing the hostname to connect to.
@ -263,7 +284,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
std::stringstream ss; std::stringstream ss;
ss << port; ss << port;
memset(&hints, 0, sizeof(struct addrinfo)); memset( &hints, 0, sizeof(struct addrinfo));
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;
@ -273,25 +294,29 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
hints.ai_next = 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){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, gai_strerror(s)); fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, gai_strerror(s));
#endif #endif
close(); close();
return; return;
} }
for (rp = result; rp != NULL; rp = rp->ai_next) { for (rp = result; rp != NULL; rp = rp->ai_next){
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0){continue;} if (sock < 0){
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} continue;
}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){
break;
}
::close(sock); ::close(sock);
} }
freeaddrinfo(result); freeaddrinfo(result);
if (rp == 0){ if (rp == 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno)); fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno));
#endif #endif
close(); close();
}else{ }else{
if (nonblock){ if (nonblock){
@ -300,7 +325,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
fcntl(sock, F_SETFL, flags); fcntl(sock, F_SETFL, flags);
} }
} }
}//Socket::Connection TCP Contructor } //Socket::Connection TCP Contructor
/// Returns the connected-state for this socket. /// Returns the connected-state for this socket.
/// Note that this function might be slightly behind the real situation. /// Note that this function might be slightly behind the real situation.
@ -345,8 +370,8 @@ bool Socket::Connection::spool(){
/// Returns true if new data was received, false otherwise. /// Returns true if new data was received, false otherwise.
bool Socket::Connection::flush(){ bool Socket::Connection::flush(){
while (upbuffer.size() > 0 && connected()){ while (upbuffer.size() > 0 && connected()){
if (!iwrite(upbuffer.get())){ if ( !iwrite(upbuffer.get())){
Util::sleep(10);//sleep 10ms Util::sleep(10); //sleep 10ms
} }
} }
/// \todo Provide better mechanism to prevent overbuffering. /// \todo Provide better mechanism to prevent overbuffering.
@ -357,7 +382,6 @@ bool Socket::Connection::flush(){
} }
} }
/// Returns a reference to the download buffer. /// Returns a reference to the download buffer.
Socket::Buffer & Socket::Connection::Received(){ Socket::Buffer & Socket::Connection::Received(){
return downbuffer; return downbuffer;
@ -368,17 +392,17 @@ Socket::Buffer & Socket::Connection::Received(){
/// Any data that could not be send will block until it can be send or the connection is severed. /// Any data that could not be send will block until it can be send or the connection is severed.
void Socket::Connection::SendNow(const char * data, size_t len){ void Socket::Connection::SendNow(const char * data, size_t len){
while (upbuffer.size() > 0 && connected()){ while (upbuffer.size() > 0 && connected()){
if (!iwrite(upbuffer.get())){ if ( !iwrite(upbuffer.get())){
Util::sleep(1);//sleep 1ms if buffer full Util::sleep(1); //sleep 1ms if buffer full
} }
} }
int i = iwrite(data, len); int i = iwrite(data, len);
while (i < len && connected()){ while (i < len && connected()){
int j = iwrite(data+i, len-i); int j = iwrite(data + i, len - i);
if (j > 0){ if (j > 0){
i += j; i += j;
}else{ }else{
Util::sleep(1);//sleep 1ms and retry Util::sleep(1); //sleep 1ms and retry
} }
} }
} }
@ -390,7 +414,9 @@ void Socket::Connection::SendNow(const char * data, size_t len){
/// This means this function is blocking if the socket is, but nonblocking otherwise. /// This means this function is blocking if the socket is, but nonblocking otherwise.
void Socket::Connection::Send(const char * data, size_t len){ void Socket::Connection::Send(const char * data, size_t len){
while (upbuffer.size() > 0){ while (upbuffer.size() > 0){
if (!iwrite(upbuffer.get())){break;} if ( !iwrite(upbuffer.get())){
break;
}
} }
if (upbuffer.size() > 0){ if (upbuffer.size() > 0){
upbuffer.append(data, len); upbuffer.append(data, len);
@ -442,7 +468,9 @@ void Socket::Connection::Send(std::string & data){
/// \param len Amount of bytes to write. /// \param len Amount of bytes to write.
/// \returns The amount of bytes actually written. /// \returns The amount of bytes actually written.
int Socket::Connection::iwrite(const void * buffer, int len){ int Socket::Connection::iwrite(const void * buffer, int len){
if (!connected() || len < 1){return 0;} if ( !connected() || len < 1){
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = send(sock, buffer, len, 0); r = send(sock, buffer, len, 0);
@ -457,9 +485,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){
default: default:
if (errno != EPIPE){ if (errno != EPIPE){
Error = true; Error = true;
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno));
#endif #endif
} }
close(); close();
return 0; return 0;
@ -471,7 +499,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){
} }
up += r; up += r;
return r; return r;
}//Socket::Connection::iwrite } //Socket::Connection::iwrite
/// Incremental read call. This function tries to read len bytes to the buffer from the socket, /// Incremental read call. This function tries to read len bytes to the buffer from the socket,
/// returning the amount of bytes it actually read. /// returning the amount of bytes it actually read.
@ -479,7 +507,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){
/// \param len Amount of bytes to read. /// \param len Amount of bytes to read.
/// \returns The amount of bytes actually read. /// \returns The amount of bytes actually read.
int Socket::Connection::iread(void * buffer, int len){ int Socket::Connection::iread(void * buffer, int len){
if (!connected() || len < 1){return 0;} if ( !connected() || len < 1){
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = recv(sock, buffer, len, 0); r = recv(sock, buffer, len, 0);
@ -494,9 +524,9 @@ int Socket::Connection::iread(void * buffer, int len){
default: default:
if (errno != EPIPE){ if (errno != EPIPE){
Error = true; Error = true;
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno));
#endif #endif
} }
close(); close();
return 0; return 0;
@ -508,7 +538,7 @@ int Socket::Connection::iread(void * buffer, int len){
} }
down += r; down += r;
return r; return r;
}//Socket::Connection::iread } //Socket::Connection::iread
/// Read call that is compatible with Socket::Buffer. /// Read call that is compatible with Socket::Buffer.
/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), /// Data is read using iread (which is nonblocking if the Socket::Connection itself is),
@ -518,10 +548,12 @@ int Socket::Connection::iread(void * buffer, int len){
bool Socket::Connection::iread(Buffer & buffer){ bool Socket::Connection::iread(Buffer & buffer){
char cbuffer[BUFFER_BLOCKSIZE]; char cbuffer[BUFFER_BLOCKSIZE];
int num = iread(cbuffer, BUFFER_BLOCKSIZE); int num = iread(cbuffer, BUFFER_BLOCKSIZE);
if (num < 1){return false;} if (num < 1){
return false;
}
buffer.append(cbuffer, num); buffer.append(cbuffer, num);
return true; return true;
}//iread } //iread
/// Incremental write call that is compatible with std::string. /// Incremental write call that is compatible with std::string.
/// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is), /// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is),
@ -529,12 +561,16 @@ bool Socket::Connection::iread(Buffer & buffer){
/// \param buffer std::string to remove data from. /// \param buffer std::string to remove data from.
/// \return True if more data was sent, false otherwise. /// \return True if more data was sent, false otherwise.
bool Socket::Connection::iwrite(std::string & buffer){ bool Socket::Connection::iwrite(std::string & buffer){
if (buffer.size() < 1){return false;} if (buffer.size() < 1){
return false;
}
int tmp = iwrite((void*)buffer.c_str(), buffer.size()); int tmp = iwrite((void*)buffer.c_str(), buffer.size());
if (tmp < 1){return false;} if (tmp < 1){
return false;
}
buffer = buffer.substr(tmp); buffer = buffer.substr(tmp);
return true; return true;
}//iwrite } //iwrite
/// Gets hostname for connection, if available. /// Gets hostname for connection, if available.
std::string Socket::Connection::getHost(){ std::string Socket::Connection::getHost(){
@ -549,13 +585,13 @@ void Socket::Connection::setHost(std::string host){
/// Returns true if these sockets are the same socket. /// Returns true if these sockets are the same socket.
/// Does not check the internal stats - only the socket itself. /// Does not check the internal stats - only the socket itself.
bool Socket::Connection::operator== (const Connection &B) const{ bool Socket::Connection::operator==(const Connection &B) const{
return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1]; return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1];
} }
/// Returns true if these sockets are not the same socket. /// Returns true if these sockets are not the same socket.
/// Does not check the internal stats - only the socket itself. /// Does not check the internal stats - only the socket itself.
bool Socket::Connection::operator!= (const Connection &B) const{ bool Socket::Connection::operator!=(const Connection &B) const{
return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1]; return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1];
} }
@ -568,7 +604,7 @@ Socket::Connection::operator bool() const{
/// Create a new base Server. The socket is never connected, and a placeholder for later connections. /// Create a new base Server. The socket is never connected, and a placeholder for later connections.
Socket::Server::Server(){ Socket::Server::Server(){
sock = -1; sock = -1;
}//Socket::Server base Constructor } //Socket::Server base Constructor
/// Create a new TCP Server. The socket is immediately bound and set to listen. /// Create a new TCP Server. The socket is immediately bound and set to listen.
/// A maximum of 100 connections will be accepted between accept() calls. /// A maximum of 100 connections will be accepted between accept() calls.
@ -577,11 +613,11 @@ Socket::Server::Server(){
/// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces).
/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking).
Socket::Server::Server(int port, std::string hostname, bool nonblock){ Socket::Server::Server(int port, std::string hostname, bool nonblock){
if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ if ( !IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){
fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno));
sock = -1; sock = -1;
} }
}//Socket::Server TCP Constructor } //Socket::Server TCP Constructor
/// Attempt to bind an IPv6 socket. /// Attempt to bind an IPv6 socket.
/// \param port The TCP port to listen on /// \param port The TCP port to listen on
@ -591,9 +627,9 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){
bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){
sock = socket(AF_INET6, SOCK_STREAM, 0); sock = socket(AF_INET6, SOCK_STREAM, 0);
if (sock < 0){ if (sock < 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno));
#endif #endif
return false; return false;
} }
int on = 1; int on = 1;
@ -605,31 +641,31 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){
} }
struct sockaddr_in6 addr; struct sockaddr_in6 addr;
addr.sin6_family = AF_INET6; addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);//set port addr.sin6_port = htons(port); //set port
if (hostname == "0.0.0.0" || hostname.length() == 0){ if (hostname == "0.0.0.0" || hostname.length() == 0){
addr.sin6_addr = in6addr_any; addr.sin6_addr = in6addr_any;
}else{ }else{
inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr);//set interface, 0.0.0.0 (default) is all inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr); //set interface, 0.0.0.0 (default) is all
} }
int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind int ret = bind(sock, (sockaddr*) &addr, sizeof(addr)); //do the actual bind
if (ret == 0){ if (ret == 0){
ret = listen(sock, 100);//start listening, backlog of 100 allowed ret = listen(sock, 100); //start listening, backlog of 100 allowed
if (ret == 0){ if (ret == 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port); fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port);
#endif #endif
return true; return true;
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno)); fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno));
#endif #endif
close(); close();
return false; return false;
} }
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno));
#endif #endif
close(); close();
return false; return false;
} }
@ -643,9 +679,9 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){
bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){
sock = socket(AF_INET, SOCK_STREAM, 0); sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){ if (sock < 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno));
#endif #endif
return false; return false;
} }
int on = 1; int on = 1;
@ -657,31 +693,31 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){
} }
struct sockaddr_in addr4; struct sockaddr_in addr4;
addr4.sin_family = AF_INET; addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);//set port addr4.sin_port = htons(port); //set port
if (hostname == "0.0.0.0" || hostname.length() == 0){ if (hostname == "0.0.0.0" || hostname.length() == 0){
addr4.sin_addr.s_addr = INADDR_ANY; addr4.sin_addr.s_addr = INADDR_ANY;
}else{ }else{
inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr); //set interface, 0.0.0.0 (default) is all
} }
int ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind int ret = bind(sock, (sockaddr*) &addr4, sizeof(addr4)); //do the actual bind
if (ret == 0){ if (ret == 0){
ret = listen(sock, 100);//start listening, backlog of 100 allowed ret = listen(sock, 100); //start listening, backlog of 100 allowed
if (ret == 0){ if (ret == 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port); fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port);
#endif #endif
return true; return true;
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno)); fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno));
#endif #endif
close(); close();
return false; return false;
} }
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno));
#endif #endif
close(); close();
return false; return false;
} }
@ -697,9 +733,9 @@ Socket::Server::Server(std::string address, bool nonblock){
unlink(address.c_str()); unlink(address.c_str());
sock = socket(AF_UNIX, SOCK_STREAM, 0); sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0){ if (sock < 0){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno));
#endif #endif
return; return;
} }
if (nonblock){ if (nonblock){
@ -709,38 +745,40 @@ Socket::Server::Server(std::string address, bool nonblock){
} }
sockaddr_un addr; sockaddr_un addr;
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, address.c_str(), address.size()+1); strncpy(addr.sun_path, address.c_str(), address.size() + 1);
int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); int ret = bind(sock, (sockaddr*) &addr, sizeof(addr));
if (ret == 0){ if (ret == 0){
ret = listen(sock, 100);//start listening, backlog of 100 allowed ret = listen(sock, 100); //start listening, backlog of 100 allowed
if (ret == 0){ if (ret == 0){
return; return;
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno));
#endif #endif
close(); close();
return; return;
} }
}else{ }else{
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno));
#endif #endif
close(); close();
return; return;
} }
}//Socket::Server Unix Constructor } //Socket::Server Unix Constructor
/// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection. /// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection.
/// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this. /// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this.
/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). /// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking).
/// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. /// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances.
Socket::Connection Socket::Server::accept(bool nonblock){ Socket::Connection Socket::Server::accept(bool nonblock){
if (sock < 0){return Socket::Connection(-1);} if (sock < 0){
return Socket::Connection( -1);
}
struct sockaddr_in6 addrinfo; struct sockaddr_in6 addrinfo;
socklen_t len = sizeof(addrinfo); socklen_t len = sizeof(addrinfo);
static char addrconv[INET6_ADDRSTRLEN]; static char addrconv[INET6_ADDRSTRLEN];
int r = ::accept(sock, (sockaddr*)&addrinfo, &len); int r = ::accept(sock, (sockaddr*) &addrinfo, &len);
//set the socket to be nonblocking, if requested. //set the socket to be nonblocking, if requested.
//we could do this through accept4 with a flag, but that call is non-standard... //we could do this through accept4 with a flag, but that call is non-standard...
if ((r >= 0) && nonblock){ if ((r >= 0) && nonblock){
@ -751,29 +789,29 @@ Socket::Connection Socket::Server::accept(bool nonblock){
Socket::Connection tmp(r); Socket::Connection tmp(r);
if (r < 0){ if (r < 0){
if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){
#if DEBUG >= 1 #if DEBUG >= 1
fprintf(stderr, "Error during accept - closing server socket.\n"); fprintf(stderr, "Error during accept - closing server socket.\n");
#endif #endif
close(); close();
} }
}else{ }else{
if (addrinfo.sin6_family == AF_INET6){ if (addrinfo.sin6_family == AF_INET6){
tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN);
#if DEBUG >= 6 #if DEBUG >= 6
fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str()); fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str());
#endif #endif
} }
if (addrinfo.sin6_family == AF_INET){ if (addrinfo.sin6_family == AF_INET){
tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*) &addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN);
#if DEBUG >= 6 #if DEBUG >= 6
fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str()); fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str());
#endif #endif
} }
if (addrinfo.sin6_family == AF_UNIX){ if (addrinfo.sin6_family == AF_UNIX){
#if DEBUG >= 6 #if DEBUG >= 6
tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path;
fprintf(stderr,"Unix socket, no address\n"); fprintf(stderr,"Unix socket, no address\n");
#endif #endif
tmp.remotehost = "UNIX_SOCKET"; tmp.remotehost = "UNIX_SOCKET";
} }
} }
@ -784,15 +822,16 @@ Socket::Connection Socket::Server::accept(bool nonblock){
/// If the connection is already closed, nothing happens. /// If the connection is already closed, nothing happens.
void Socket::Server::close(){ void Socket::Server::close(){
if (connected()){ if (connected()){
#if DEBUG >= 6 #if DEBUG >= 6
fprintf(stderr, "ServerSocket closed.\n"); fprintf(stderr, "ServerSocket closed.\n");
#endif #endif
shutdown(sock, SHUT_RDWR); shutdown(sock, SHUT_RDWR);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR){} while (::close(sock) != 0 && errno == EINTR){
}
sock = -1; sock = -1;
} }
}//Socket::Server::close } //Socket::Server::close
/// Returns the connected-state for this socket. /// Returns the connected-state for this socket.
/// Note that this function might be slightly behind the real situation. /// Note that this function might be slightly behind the real situation.
@ -801,7 +840,9 @@ void Socket::Server::close(){
/// \returns True if socket is connected, false otherwise. /// \returns True if socket is connected, false otherwise.
bool Socket::Server::connected() const{ bool Socket::Server::connected() const{
return (sock >= 0); return (sock >= 0);
}//Socket::Server::connected } //Socket::Server::connected
/// Returns internal socket number. /// Returns internal socket number.
int Socket::Server::getSocket(){return sock;} int Socket::Server::getSocket(){
return sock;
}

View file

@ -17,10 +17,12 @@
#include <deque> #include <deque>
//for being friendly with Socket::Connection down below //for being friendly with Socket::Connection down below
namespace Buffer{class user;}; namespace Buffer {
class user;
}
///Holds Socket tools. ///Holds Socket tools.
namespace Socket{ namespace Socket {
/// A buffer made out of std::string objects that can be efficiently read from and written to. /// A buffer made out of std::string objects that can be efficiently read from and written to.
class Buffer{ class Buffer{
@ -34,7 +36,8 @@ namespace Socket{
bool available(unsigned int count); bool available(unsigned int count);
std::string remove(unsigned int count); std::string remove(unsigned int count);
std::string copy(unsigned int count); std::string copy(unsigned int count);
};//Buffer };
//Buffer
/// This class is for easy communicating through sockets, either TCP or Unix. /// This class is for easy communicating through sockets, either TCP or Unix.
class Connection{ class Connection{
@ -51,7 +54,7 @@ namespace Socket{
int iwrite(const void * buffer, int len); ///< Incremental write call. int iwrite(const void * buffer, int len); ///< Incremental write call.
bool iread(Buffer & buffer); ///< Incremental write call that is compatible with Socket::Buffer. bool iread(Buffer & buffer); ///< Incremental write call that is compatible with Socket::Buffer.
bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string.
public: public:
//friends //friends
friend class ::Buffer::user; friend class ::Buffer::user;
//constructors //constructors
@ -86,8 +89,8 @@ namespace Socket{
bool Error; ///< Set to true if a socket error happened. bool Error; ///< Set to true if a socket error happened.
bool Blocking; ///< Set to true if a socket is currently or wants to be blocking. bool Blocking; ///< Set to true if a socket is currently or wants to be blocking.
//overloaded operators //overloaded operators
bool operator== (const Connection &B) const; bool operator==(const Connection &B) const;
bool operator!= (const Connection &B) const; bool operator!=(const Connection &B) const;
operator bool() const; operator bool() const;
}; };
@ -97,7 +100,7 @@ namespace Socket{
int sock; ///< Internally saved socket number. int sock; ///< Internally saved socket number.
bool IPv6bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv6 socket bool IPv6bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv6 socket
bool IPv4bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv4 socket bool IPv4bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv4 socket
public: public:
Server(); ///< Create a new base Server. Server(); ///< Create a new base Server.
Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server. Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server.
Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server. Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server.
@ -106,5 +109,5 @@ namespace Socket{
void close(); ///< Close connection. void close(); ///< Close connection.
int getSocket(); ///< Returns internal socket number. int getSocket(); ///< Returns internal socket number.
}; };
}; }

View file

@ -18,18 +18,21 @@
/// that character is deleted. The original string is modified. /// that character is deleted. The original string is modified.
void Util::Stream::sanitizeName(std::string & streamname){ void Util::Stream::sanitizeName(std::string & streamname){
//strip anything that isn't numbers, digits or underscores //strip anything that isn't numbers, digits or underscores
for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){
if (*i == '?'){streamname.erase(i, streamname.end()); break;} if ( *i == '?'){
if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ streamname.erase(i, streamname.end());
break;
}
if ( !isalpha( *i) && !isdigit( *i) && *i != '_'){
streamname.erase(i); streamname.erase(i);
}else{ }else{
*i=tolower(*i); *i = tolower( *i);
} }
} }
} }
Socket::Connection Util::Stream::getLive(std::string streamname){ Socket::Connection Util::Stream::getLive(std::string streamname){
return Socket::Connection("/tmp/mist/stream_"+streamname); return Socket::Connection("/tmp/mist/stream_" + streamname);
} }
/// Starts a process for a VoD stream. /// Starts a process for a VoD stream.
@ -49,20 +52,20 @@ Socket::Connection Util::Stream::getStream(std::string streamname){
JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist");
if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"].isMember(streamname)){
if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){ if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl; std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl;
#endif #endif
return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString()); return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString());
}else{ }else{
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Opening live stream " << streamname << std::endl; std::cerr << "Opening live stream " << streamname << std::endl;
#endif #endif
return Socket::Connection("/tmp/mist/stream_"+streamname); return Socket::Connection("/tmp/mist/stream_" + streamname);
} }
} }
#if DEBUG >= 4 #if DEBUG >= 4
std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl;
#endif #endif
return Socket::Connection(); return Socket::Connection();
} }
@ -73,7 +76,7 @@ Socket::Connection Util::Stream::getStream(std::string streamname){
/// If the /tmp/mist directory doesn't exist yet, this will create it. /// If the /tmp/mist directory doesn't exist yet, this will create it.
Socket::Server Util::Stream::makeLive(std::string streamname){ Socket::Server Util::Stream::makeLive(std::string streamname){
sanitizeName(streamname); sanitizeName(streamname);
std::string loc = "/tmp/mist/stream_"+streamname; std::string loc = "/tmp/mist/stream_" + streamname;
//attempt to create the /tmp/mist directory if it doesn't exist already. //attempt to create the /tmp/mist directory if it doesn't exist already.
//ignore errors - we catch all problems in the Socket::Server creation already //ignore errors - we catch all problems in the Socket::Server creation already
mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO);

View file

@ -5,7 +5,7 @@
#include <string> #include <string>
#include "socket.h" #include "socket.h"
namespace Util{ namespace Util {
class Stream{ class Stream{
public: public:
static void sanitizeName(std::string & streamname); static void sanitizeName(std::string & streamname);

View file

@ -4,22 +4,25 @@
#include "timing.h" #include "timing.h"
#include <sys/time.h>//for gettimeofday #include <sys/time.h>//for gettimeofday
#include <time.h>//for time and nanosleep #include <time.h>//for time and nanosleep
/// Sleeps for the indicated amount of milliseconds or longer. /// Sleeps for the indicated amount of milliseconds or longer.
void Util::sleep(int ms){ void Util::sleep(int ms){
if (ms < 0){return;} if (ms < 0){
if (ms > 10000){return;} return;
}
if (ms > 10000){
return;
}
struct timespec T; struct timespec T;
T.tv_sec = ms/1000; T.tv_sec = ms / 1000;
T.tv_nsec = 1000000*(ms%1000); T.tv_nsec = 1000000 * (ms % 1000);
nanosleep(&T, 0); nanosleep( &T, 0);
} }
/// Gets the current time in milliseconds. /// Gets the current time in milliseconds.
long long int Util::getMS(){ long long int Util::getMS(){
struct timespec t; struct timespec t;
clock_gettime(CLOCK_REALTIME, &t); clock_gettime(CLOCK_REALTIME, &t);
return ((long long int)t.tv_sec) * 1000 + t.tv_nsec/1000000; return ((long long int)t.tv_sec) * 1000 + t.tv_nsec / 1000000;
} }
/// Gets the amount of seconds since 01/01/1970. /// Gets the amount of seconds since 01/01/1970.

View file

@ -3,8 +3,8 @@
#pragma once #pragma once
namespace Util{ namespace Util {
void sleep(int ms); ///< Sleeps for the indicated amount of milliseconds or longer. void sleep(int ms); ///< Sleeps for the indicated amount of milliseconds or longer.
long long int getMS(); ///< Gets the current time in milliseconds. long long int getMS(); ///< Gets the current time in milliseconds.
long long int epoch(); ///< Gets the amount of seconds since 01/01/1970. long long int epoch(); ///< Gets the amount of seconds since 01/01/1970.
}; }