Global cleanups and standardization of code style.
This commit is contained in:
parent
51a9b4162c
commit
38ef8704f8
33 changed files with 4322 additions and 2824 deletions
1027
lib/amf.cpp
1027
lib/amf.cpp
File diff suppressed because it is too large
Load diff
25
lib/amf.h
25
lib/amf.h
|
@ -4,14 +4,13 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
//#include <string.h>
|
||||
#include <string>
|
||||
|
||||
/// 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.
|
||||
enum obj0type {
|
||||
enum obj0type{
|
||||
AMF0_NUMBER = 0x00,
|
||||
AMF0_BOOL = 0x01,
|
||||
AMF0_STRING = 0x02,
|
||||
|
@ -34,7 +33,7 @@ namespace AMF{
|
|||
};
|
||||
|
||||
/// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use.
|
||||
enum obj3type {
|
||||
enum obj3type{
|
||||
AMF3_UNDEFINED = 0x00,
|
||||
AMF3_NULL = 0x01,
|
||||
AMF3_FALSE = 0x02,
|
||||
|
@ -50,10 +49,10 @@ namespace AMF{
|
|||
AMF3_BYTES = 0x0C,
|
||||
AMF3_DDV_CONTAINER = 0xFF
|
||||
};
|
||||
|
||||
|
||||
/// Recursive class that holds AMF0 objects.
|
||||
/// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type.
|
||||
class Object {
|
||||
class Object{
|
||||
public:
|
||||
std::string Indice();
|
||||
obj0type GetType();
|
||||
|
@ -78,7 +77,8 @@ namespace AMF{
|
|||
std::string strval; ///< Holds this objects string 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).
|
||||
};//AMFType
|
||||
};
|
||||
//AMFType
|
||||
|
||||
/// Parses a C-string to a valid AMF::Object.
|
||||
Object parse(const unsigned char * data, unsigned int len);
|
||||
|
@ -89,7 +89,7 @@ namespace AMF{
|
|||
|
||||
/// Recursive class that holds AMF3 objects.
|
||||
/// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type.
|
||||
class Object3 {
|
||||
class Object3{
|
||||
public:
|
||||
std::string Indice();
|
||||
obj3type GetType();
|
||||
|
@ -117,13 +117,14 @@ namespace AMF{
|
|||
double dblval; ///< Holds this objects double 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).
|
||||
};//AMFType
|
||||
|
||||
};
|
||||
//AMFType
|
||||
|
||||
/// Parses a C-string to a valid AMF::Object3.
|
||||
Object3 parse3(const unsigned char * data, unsigned int len);
|
||||
/// Parses a std::string to a valid AMF::Object3.
|
||||
Object3 parse3(std::string data);
|
||||
/// 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);
|
||||
|
||||
};//AMF namespace
|
||||
|
||||
} //AMF namespace
|
||||
|
|
42
lib/auth.cpp
42
lib/auth.cpp
|
@ -6,35 +6,21 @@
|
|||
#include "auth.h"
|
||||
#include "base64.h"
|
||||
|
||||
namespace Secure{
|
||||
namespace Secure {
|
||||
|
||||
static unsigned char __gbv2keypub_der[] = {
|
||||
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c,
|
||||
0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb,
|
||||
0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f,
|
||||
0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74,
|
||||
0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49,
|
||||
0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a,
|
||||
0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2,
|
||||
0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6,
|
||||
0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2,
|
||||
0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38,
|
||||
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 char __gbv2keypub_der[] = {0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
|
||||
0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, 0x7d, 0x73, 0xc6, 0xe6, 0xfb,
|
||||
0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, 0x69, 0xbf, 0x26, 0x79, 0xc7,
|
||||
0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, 0x5d, 0xac, 0x6a, 0x94, 0xb1,
|
||||
0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, 0x16, 0xa2, 0x8e, 0x89, 0x12,
|
||||
0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, 0xba, 0x63, 0xc9, 0xcc, 0xee,
|
||||
0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, 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
|
||||
|
||||
/// Attempts to load the GBv2 public key.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace Secure{
|
||||
namespace Secure {
|
||||
class Auth{
|
||||
private:
|
||||
void * pubkey; ///< Holds the public key.
|
||||
|
|
|
@ -3,62 +3,84 @@
|
|||
/// Needed for base64_encode function
|
||||
const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/// Helper for base64_decode function
|
||||
inline bool Base64::is_base64(unsigned char c) {
|
||||
/// Helper for base64_decode function
|
||||
inline bool Base64::is_base64(unsigned char 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.
|
||||
/// \param input Plaintext data to encode.
|
||||
/// \returns Base64 encoded data.
|
||||
std::string Base64::encode(std::string const input) {
|
||||
std::string Base64::encode(std::string const input){
|
||||
std::string ret;
|
||||
unsigned int in_len = input.size();
|
||||
char quad[4], triple[3];
|
||||
unsigned int i, x, n = 3;
|
||||
for (x = 0; x < in_len; x = x + 3){
|
||||
if ((in_len - x) / 3 == 0){n = (in_len - x) % 3;}
|
||||
for (i=0; i < 3; i++){triple[i] = '0';}
|
||||
for (i=0; i < n; i++){triple[i] = input[x + i];}
|
||||
if ((in_len - x) / 3 == 0){
|
||||
n = (in_len - x) % 3;
|
||||
}
|
||||
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[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[3] = chars[triple[2] & 0x3F]; // 3F = 111111
|
||||
if (n < 3){quad[3] = '=';}
|
||||
if (n < 2){quad[2] = '=';}
|
||||
for(i=0; i < 4; i++){ret += quad[i];}
|
||||
if (n < 3){
|
||||
quad[3] = '=';
|
||||
}
|
||||
if (n < 2){
|
||||
quad[2] = '=';
|
||||
}
|
||||
for (i = 0; i < 4; i++){
|
||||
ret += quad[i];
|
||||
}
|
||||
}
|
||||
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.
|
||||
/// \param encoded_string Base64 encoded data to decode.
|
||||
/// \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 i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++){char_array_4[i] = chars.find(char_array_4[i]);}
|
||||
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])){
|
||||
char_array_4[i++ ] = encoded_string[in_];
|
||||
in_++;
|
||||
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[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];
|
||||
for (i = 0; (i < 3); i++){ret += char_array_3[i];}
|
||||
for (i = 0; (i < 3); i++){
|
||||
ret += char_array_3[i];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
if (i) {
|
||||
for (j = i; j <4; j++){char_array_4[j] = 0;}
|
||||
for (j = 0; j <4; j++){char_array_4[j] = chars.find(char_array_4[j]);}
|
||||
if (i){
|
||||
for (j = i; j < 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[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];
|
||||
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;
|
||||
}
|
||||
|
|
141
lib/config.cpp
141
lib/config.cpp
|
@ -56,14 +56,18 @@ Util::Config::Config(std::string cmd, std::string version){
|
|||
///\endcode
|
||||
void Util::Config::addOption(std::string optname, JSON::Value 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].removeMember("default");
|
||||
}
|
||||
long_count = 0;
|
||||
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
|
||||
if (it->second.isMember("long")){long_count++;}
|
||||
if (it->second.isMember("long_off")){long_count++;}
|
||||
if (it->second.isMember("long")){
|
||||
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;
|
||||
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
|
||||
unsigned int current = 0;
|
||||
if (it->second.isMember("long")){current += it->second["long"].asString().size() + 4;}
|
||||
if (it->second.isMember("short")){current += it->second["short"].asString().size() + 3;}
|
||||
if (current > longest){longest = current;}
|
||||
if (it->second.isMember("long")){
|
||||
current += it->second["long"].asString().size() + 4;
|
||||
}
|
||||
if (it->second.isMember("short")){
|
||||
current += it->second["short"].asString().size() + 3;
|
||||
}
|
||||
if (current > longest){
|
||||
longest = current;
|
||||
}
|
||||
current = 0;
|
||||
if (it->second.isMember("long_off")){current += it->second["long_off"].asString().size() + 4;}
|
||||
if (it->second.isMember("short_off")){current += it->second["short_off"].asString().size() + 3;}
|
||||
if (current > longest){longest = current;}
|
||||
if (it->second.isMember("long_off")){
|
||||
current += it->second["long_off"].asString().size() + 4;
|
||||
}
|
||||
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")){
|
||||
current = it->first.size() + 3;
|
||||
if (current > longest){longest = current;}
|
||||
if (current > longest){
|
||||
longest = current;
|
||||
}
|
||||
args[it->second["arg_num"].asInt()] = it->first;
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +126,9 @@ void Util::Config::printHelp(std::ostream & output){
|
|||
f = "-" + it->second["short"].asString();
|
||||
}
|
||||
}
|
||||
while (f.size() < longest){f.append(" ");}
|
||||
while (f.size() < longest){
|
||||
f.append(" ");
|
||||
}
|
||||
if (it->second.isMember("arg")){
|
||||
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
|
||||
}else{
|
||||
|
@ -126,7 +146,9 @@ void Util::Config::printHelp(std::ostream & output){
|
|||
f = "-" + it->second["short_off"].asString();
|
||||
}
|
||||
}
|
||||
while (f.size() < longest){f.append(" ");}
|
||||
while (f.size() < longest){
|
||||
f.append(" ");
|
||||
}
|
||||
if (it->second.isMember("arg")){
|
||||
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
|
||||
}else{
|
||||
|
@ -135,40 +157,49 @@ void Util::Config::printHelp(std::ostream & output){
|
|||
}
|
||||
if (it->second.isMember("arg_num")){
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Parses commandline arguments.
|
||||
/// Calls exit if an unknown option is encountered, printing a help message.
|
||||
void Util::Config::parseArgs(int argc, char ** argv){
|
||||
int opt = 0;
|
||||
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 arg_count = 0;
|
||||
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
|
||||
if (it->second.isMember("short")){
|
||||
shortopts += it->second["short"].asString();
|
||||
if (it->second.isMember("arg")){shortopts += ":";}
|
||||
if (it->second.isMember("arg")){
|
||||
shortopts += ":";
|
||||
}
|
||||
}
|
||||
if (it->second.isMember("short_off")){
|
||||
shortopts += it->second["short_off"].asString();
|
||||
if (it->second.isMember("arg")){shortopts += ":";}
|
||||
if (it->second.isMember("arg")){
|
||||
shortopts += ":";
|
||||
}
|
||||
}
|
||||
if (it->second.isMember("long")){
|
||||
longOpts[long_i].name = it->second["long"].asString().c_str();
|
||||
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++;
|
||||
}
|
||||
if (it->second.isMember("long_off")){
|
||||
longOpts[long_i].name = it->second["long_off"].asString().c_str();
|
||||
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++;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}//commandline options parser
|
||||
free(longOpts);//free the long options array
|
||||
long_i = 1;//re-use long_i as an argument counter
|
||||
while (optind < argc){//parse all remaining options, ignoring anything unexpected.
|
||||
} //commandline options parser
|
||||
free(longOpts); //free the long options array
|
||||
long_i = 1; //re-use long_i as an argument counter
|
||||
while (optind < argc){ //parse all remaining options, ignoring anything unexpected.
|
||||
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
|
||||
if (it->second.isMember("arg_num") && it->second["arg_num"].asInt() == long_i){
|
||||
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.
|
||||
/// 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){
|
||||
if (!vals.isMember(optname)){
|
||||
if ( !vals.isMember(optname)){
|
||||
std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl;
|
||||
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());
|
||||
}
|
||||
if (asArray){
|
||||
return vals[optname]["value"];
|
||||
}else{
|
||||
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;
|
||||
new_action.sa_handler = signal_handler;
|
||||
sigemptyset (&new_action.sa_mask);
|
||||
sigemptyset( &new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGINT, &new_action, NULL);
|
||||
sigaction(SIGHUP, &new_action, NULL);
|
||||
|
@ -288,41 +319,53 @@ void Util::Config::activate(){
|
|||
/// signal, and ignores all other signals.
|
||||
void Util::Config::signal_handler(int 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 SIGTERM:
|
||||
is_active = false;
|
||||
break;
|
||||
case SIGCHLD://when a child dies, reap it.
|
||||
case SIGCHLD: //when a child dies, reap it.
|
||||
wait(0);
|
||||
break;
|
||||
default: //other signals are ignored
|
||||
break;
|
||||
}
|
||||
}//signal_handler
|
||||
} //signal_handler
|
||||
|
||||
/// Adds the default connector options to this Util::Config object.
|
||||
void Util::Config::addConnectorOptions(int port){
|
||||
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);
|
||||
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("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
|
||||
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("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.
|
||||
std::string Util::getMyPath(){
|
||||
char 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;
|
||||
size_t slash = tPath.rfind('/');
|
||||
if (slash == std::string::npos){
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -330,20 +373,20 @@ std::string Util::getMyPath(){
|
|||
void Util::setUser(std::string username){
|
||||
if (username != "root"){
|
||||
struct passwd * user_info = getpwnam(username.c_str());
|
||||
if (!user_info){
|
||||
#if DEBUG >= 1
|
||||
if ( !user_info){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str());
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}else{
|
||||
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());
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
#if DEBUG >= 3
|
||||
#if DEBUG >= 3
|
||||
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 redirect output to /dev/null
|
||||
void Util::Daemonize(){
|
||||
#if DEBUG >= 3
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Going into background mode...\n");
|
||||
#endif
|
||||
if (daemon(1, 0) < 0) {
|
||||
#if DEBUG >= 1
|
||||
#endif
|
||||
if (daemon(1, 0) < 0){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
10
lib/config.h
10
lib/config.h
|
@ -2,11 +2,16 @@
|
|||
/// Contains generic function headers for managing configuration.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef PACKAGE_VERSION
|
||||
#define PACKAGE_VERSION "unknown"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include "json.h"
|
||||
|
||||
/// Contains utility code, not directly related to streaming media
|
||||
namespace Util{
|
||||
namespace Util {
|
||||
|
||||
/// Deals with parsing configuration from commandline options.
|
||||
class Config{
|
||||
|
@ -40,4 +45,5 @@ namespace Util{
|
|||
/// Will turn the current process into a daemon.
|
||||
void Daemonize();
|
||||
|
||||
};
|
||||
}
|
||||
;
|
||||
|
|
25
lib/crypto.h
25
lib/crypto.h
|
@ -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);
|
200
lib/dtsc.cpp
200
lib/dtsc.cpp
|
@ -5,12 +5,12 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h> //for memcmp
|
||||
#include <arpa/inet.h> //for htonl/ntohl
|
||||
|
||||
char DTSC::Magic_Header[] = "DTSC";
|
||||
char DTSC::Magic_Packet[] = "DTPD";
|
||||
|
||||
/// Initializes a DTSC::Stream with only one packet buffer.
|
||||
DTSC::Stream::Stream(){
|
||||
datapointertype = DTSC::INVALID;
|
||||
datapointer = 0;
|
||||
buffercount = 1;
|
||||
}
|
||||
|
@ -18,8 +18,11 @@ DTSC::Stream::Stream(){
|
|||
/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers.
|
||||
/// The actual buffer count may not at all times be the requested amount.
|
||||
DTSC::Stream::Stream(unsigned int rbuffers){
|
||||
datapointertype = DTSC::INVALID;
|
||||
datapointer = 0;
|
||||
if (rbuffers < 1){rbuffers = 1;}
|
||||
if (rbuffers < 1){
|
||||
rbuffers = 1;
|
||||
}
|
||||
buffercount = rbuffers;
|
||||
}
|
||||
|
||||
|
@ -39,15 +42,19 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
|
|||
if (buffer.length() > 8){
|
||||
if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){
|
||||
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;
|
||||
metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i);
|
||||
buffer.erase(0, len+8);
|
||||
buffer.erase(0, len + 8);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){
|
||||
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());
|
||||
unsigned int i = 0;
|
||||
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")){
|
||||
std::string tmp = buffers.front()["datatype"].asString();
|
||||
if (tmp == "video"){datapointertype = VIDEO;}
|
||||
if (tmp == "audio"){datapointertype = AUDIO;}
|
||||
if (tmp == "meta"){datapointertype = META;}
|
||||
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;}
|
||||
if (tmp == "video"){
|
||||
datapointertype = VIDEO;
|
||||
}
|
||||
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();
|
||||
syncing = false;
|
||||
return true;
|
||||
}
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
if (!syncing){
|
||||
std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl;
|
||||
syncing = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
size_t magic_search = buffer.find(Magic_Packet);
|
||||
if (magic_search == std::string::npos){
|
||||
buffer.clear();
|
||||
|
@ -97,18 +114,22 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
|
|||
std::string header_bytes = buffer.copy(8);
|
||||
if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){
|
||||
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;
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){
|
||||
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());
|
||||
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);
|
||||
datapointertype = INVALID;
|
||||
if (buffers.front().isMember("data")){
|
||||
|
@ -118,22 +139,32 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
|
|||
}
|
||||
if (buffers.front().isMember("datatype")){
|
||||
std::string tmp = buffers.front()["datatype"].asString();
|
||||
if (tmp == "video"){datapointertype = VIDEO;}
|
||||
if (tmp == "audio"){datapointertype = AUDIO;}
|
||||
if (tmp == "meta"){datapointertype = META;}
|
||||
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;}
|
||||
if (tmp == "video"){
|
||||
datapointertype = VIDEO;
|
||||
}
|
||||
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();
|
||||
syncing = false;
|
||||
return true;
|
||||
}
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
if (!syncing){
|
||||
std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl;
|
||||
syncing = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
buffer.get().clear();
|
||||
}
|
||||
return false;
|
||||
|
@ -185,19 +216,30 @@ void DTSC::Stream::advanceRings(){
|
|||
std::deque<DTSC::Ring>::iterator dit;
|
||||
std::set<DTSC::Ring *>::iterator sit;
|
||||
for (sit = rings.begin(); sit != rings.end(); sit++){
|
||||
(*sit)->b++;
|
||||
if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;}
|
||||
if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;}
|
||||
( *sit)->b++;
|
||||
if (( *sit)->waiting){
|
||||
( *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++){
|
||||
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"))){
|
||||
keyframes.push_front(DTSC::Ring(0));
|
||||
}
|
||||
//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.
|
||||
|
@ -235,7 +277,9 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){
|
|||
/// Drops all Ring classes that have been given out.
|
||||
DTSC::Stream::~Stream(){
|
||||
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.
|
||||
|
@ -247,12 +291,12 @@ DTSC::File::File(std::string filename, bool create){
|
|||
fseek(F, 0, SEEK_SET);
|
||||
fwrite(DTSC::Magic_Header, 4, 1, F);
|
||||
memset(buffer, 0, 4);
|
||||
fwrite(buffer, 4, 1, F);//write 4 zero-bytes
|
||||
fwrite(buffer, 4, 1, F); //write 4 zero-bytes
|
||||
headerSize = 0;
|
||||
}else{
|
||||
F = fopen(filename.c_str(), "r+b");
|
||||
}
|
||||
if (!F){
|
||||
if ( !F){
|
||||
fprintf(stderr, "Could not open file %s\n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
@ -262,15 +306,15 @@ DTSC::File::File(std::string filename, bool create){
|
|||
if (fread(buffer, 4, 1, F) != 1){
|
||||
fseek(F, 4, SEEK_SET);
|
||||
memset(buffer, 0, 4);
|
||||
fwrite(buffer, 4, 1, F);//write 4 zero-bytes
|
||||
fwrite(buffer, 4, 1, F); //write 4 zero-bytes
|
||||
}else{
|
||||
uint32_t * ubuffer = (uint32_t *)buffer;
|
||||
headerSize = ntohl(ubuffer[0]);
|
||||
}
|
||||
readHeader(0);
|
||||
fseek(F, 8+headerSize, SEEK_SET);
|
||||
fseek(F, 8 + headerSize, SEEK_SET);
|
||||
currframe = 1;
|
||||
frames[1] = 8+headerSize;
|
||||
frames[1] = 8 + headerSize;
|
||||
msframes[1] = 0;
|
||||
}
|
||||
|
||||
|
@ -289,7 +333,7 @@ bool DTSC::File::writeHeader(std::string & header, bool force){
|
|||
headerSize = header.size();
|
||||
fseek(F, 8, SEEK_SET);
|
||||
int ret = fwrite(header.c_str(), headerSize, 1, F);
|
||||
fseek(F, 8+headerSize, SEEK_SET);
|
||||
fseek(F, 8 + headerSize, SEEK_SET);
|
||||
return (ret == 1);
|
||||
}
|
||||
|
||||
|
@ -299,13 +343,19 @@ long long int DTSC::File::addHeader(std::string & header){
|
|||
fseek(F, 0, SEEK_END);
|
||||
long long int writePos = ftell(F);
|
||||
int hSize = htonl(header.size());
|
||||
int ret = fwrite(DTSC::Magic_Header, 4, 1, F);//write header
|
||||
if (ret != 1){return 0;}
|
||||
ret = fwrite((void*)(&hSize), 4, 1, F);//write size
|
||||
if (ret != 1){return 0;}
|
||||
ret = fwrite(header.c_str(), header.size(), 1, F);//write contents
|
||||
if (ret != 1){return 0;}
|
||||
return writePos;//return position written at
|
||||
int ret = fwrite(DTSC::Magic_Header, 4, 1, F); //write header
|
||||
if (ret != 1){
|
||||
return 0;
|
||||
}
|
||||
ret = fwrite((void*)( &hSize), 4, 1, F); //write size
|
||||
if (ret != 1){
|
||||
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.
|
||||
|
@ -315,9 +365,9 @@ void DTSC::File::readHeader(int pos){
|
|||
fseek(F, pos, SEEK_SET);
|
||||
if (fread(buffer, 4, 1, F) != 1){
|
||||
if (feof(F)){
|
||||
#if DEBUG >= 4
|
||||
#if DEBUG >= 4
|
||||
fprintf(stderr, "End of file reached (H%i)\n", pos);
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
fprintf(stderr, "Could not read header (H%i)\n", pos);
|
||||
}
|
||||
|
@ -355,13 +405,13 @@ void DTSC::File::readHeader(int pos){
|
|||
if (metadata.isMember("keytime")){
|
||||
msframes.clear();
|
||||
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")){
|
||||
frames.clear();
|
||||
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);
|
||||
if (fread(buffer, 4, 1, F) != 1){
|
||||
if (feof(F)){
|
||||
#if DEBUG >= 4
|
||||
#if DEBUG >= 4
|
||||
fprintf(stderr, "End of file reached.\n");
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
fprintf(stderr, "Could not read header\n");
|
||||
}
|
||||
|
@ -409,13 +459,13 @@ void DTSC::File::seekNext(){
|
|||
if (frames[currframe] != lastreadpos){
|
||||
currframe++;
|
||||
currtime = jsonbuffer["time"].asInt();
|
||||
#if DEBUG >= 6
|
||||
#if DEBUG >= 6
|
||||
if (frames[currframe] != lastreadpos){
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
frames[currframe] = lastreadpos;
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
/// Returns true if successful, false otherwise.
|
||||
bool DTSC::File::seek_frame(int frameno){
|
||||
if (frames.count(frameno) > 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;
|
||||
#endif
|
||||
#endif
|
||||
currframe = frameno;
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
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 DEBUG >= 4
|
||||
#if DEBUG >= 4
|
||||
std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
while (currframe < frameno){
|
||||
seekNext();
|
||||
if (jsonbuffer.isNull()){return false;}
|
||||
if (jsonbuffer.isNull()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
seek_frame(frameno);
|
||||
return true;
|
||||
|
@ -468,16 +529,23 @@ bool DTSC::File::seek_time(int ms){
|
|||
currtime = 0;
|
||||
currframe = 1;
|
||||
for (it = msframes.begin(); it != msframes.end(); ++it){
|
||||
if (it->second > ms){break;}
|
||||
if (it->second > currtime){currtime = it->second; currframe = it->first;}
|
||||
if (it->second > ms){
|
||||
break;
|
||||
}
|
||||
if (it->second > currtime){
|
||||
currtime = it->second;
|
||||
currframe = it->first;
|
||||
}
|
||||
}
|
||||
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;
|
||||
#endif
|
||||
#endif
|
||||
while (currtime < ms){
|
||||
seekNext();
|
||||
if (jsonbuffer.isNull()){return false;}
|
||||
if (jsonbuffer.isNull()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (currtime > ms){
|
||||
return seek_frame(currframe - 1);
|
||||
|
|
18
lib/dtsc.h
18
lib/dtsc.h
|
@ -12,8 +12,6 @@
|
|||
#include "json.h"
|
||||
#include "socket.h"
|
||||
|
||||
|
||||
|
||||
/// Holds all DDVTECH Stream Container classes and parsers.
|
||||
///length (int, length in seconds, if available)
|
||||
///video:
|
||||
|
@ -50,10 +48,10 @@
|
|||
/// - nalu (int, if set, is a nalu)
|
||||
/// - 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)
|
||||
namespace DTSC{
|
||||
namespace DTSC {
|
||||
|
||||
/// This enum holds all possible datatypes for DTSC packets.
|
||||
enum datatype {
|
||||
enum datatype{
|
||||
AUDIO, ///< Stream Audio data
|
||||
VIDEO, ///< Stream Video data
|
||||
META, ///< Stream Metadata
|
||||
|
@ -91,12 +89,12 @@ namespace DTSC{
|
|||
FILE * F;
|
||||
unsigned long headerSize;
|
||||
char buffer[4];
|
||||
};//FileWriter
|
||||
|
||||
};
|
||||
//FileWriter
|
||||
|
||||
/// A part from the DTSC::Stream ringbuffer.
|
||||
/// Holds information about a buffer that will stay consistent
|
||||
class Ring {
|
||||
class Ring{
|
||||
public:
|
||||
Ring(unsigned int v);
|
||||
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.
|
||||
/// 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.
|
||||
class Stream {
|
||||
class Stream{
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
|
@ -125,7 +123,7 @@ namespace DTSC{
|
|||
Ring * getRing();
|
||||
unsigned int getTime();
|
||||
void dropRing(Ring * ptr);
|
||||
private:
|
||||
private:
|
||||
std::deque<JSON::Value> buffers;
|
||||
std::set<DTSC::Ring *> rings;
|
||||
std::deque<DTSC::Ring> keyframes;
|
||||
|
@ -134,4 +132,4 @@ namespace DTSC{
|
|||
datatype datapointertype;
|
||||
unsigned int buffercount;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,215 +1,265 @@
|
|||
#include "filesystem.h"
|
||||
|
||||
|
||||
Filesystem::Directory::Directory( std::string PathName, std::string BasePath ) {
|
||||
Filesystem::Directory::Directory(std::string PathName, std::string BasePath){
|
||||
MyBase = BasePath;
|
||||
if( PathName[0] == '/' ) { PathName.erase(0,1); }
|
||||
if( BasePath[BasePath.size()-1] != '/' ) { BasePath += "/"; }
|
||||
if (PathName[0] == '/'){
|
||||
PathName.erase(0, 1);
|
||||
}
|
||||
if (BasePath[BasePath.size() - 1] != '/'){
|
||||
BasePath += "/";
|
||||
}
|
||||
MyPath = PathName;
|
||||
FillEntries( );
|
||||
FillEntries();
|
||||
}
|
||||
|
||||
Filesystem::Directory::~Directory( ) { }
|
||||
Filesystem::Directory::~Directory(){
|
||||
}
|
||||
|
||||
void Filesystem::Directory::FillEntries( ) {
|
||||
fprintf( stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str() );
|
||||
void Filesystem::Directory::FillEntries(){
|
||||
fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str());
|
||||
ValidDir = true;
|
||||
struct stat StatBuf;
|
||||
Entries.clear();
|
||||
DIR * Dirp = opendir( (MyBase + MyPath).c_str() );
|
||||
if( !Dirp ) {
|
||||
DIR * Dirp = opendir((MyBase + MyPath).c_str());
|
||||
if ( !Dirp){
|
||||
ValidDir = false;
|
||||
} else {
|
||||
}else{
|
||||
dirent * entry;
|
||||
while( entry = readdir( Dirp ) ) {
|
||||
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) );
|
||||
while ((entry = readdir(Dirp))){
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
///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, "#Entries: %d\n", Entries.size() );
|
||||
fprintf(stderr, "Valid dir: %d\n", ValidDir);
|
||||
fprintf(stderr, "#Entries: %d\n", Entries.size());
|
||||
}
|
||||
|
||||
void Filesystem::Directory::Print( ) {
|
||||
if( !ValidDir ) {
|
||||
printf( "%s is not a valid directory\n", (MyBase + MyPath).c_str() );
|
||||
void Filesystem::Directory::Print(){
|
||||
if ( !ValidDir){
|
||||
printf("%s is not a valid directory\n", (MyBase + MyPath).c_str());
|
||||
return;
|
||||
}
|
||||
printf( "%s:\n", (MyBase + MyPath).c_str() );
|
||||
for( std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++ ) {
|
||||
printf( "\t%s\n", (*it).first.c_str() );
|
||||
printf("%s:\n", (MyBase + MyPath).c_str());
|
||||
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){
|
||||
printf("\t%s\n", ( *it).first.c_str());
|
||||
}
|
||||
printf( "\n" );
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::IsDir( ) {
|
||||
bool Filesystem::Directory::IsDir(){
|
||||
return ValidDir;
|
||||
}
|
||||
|
||||
std::string Filesystem::Directory::PWD( ) {
|
||||
std::string Filesystem::Directory::PWD(){
|
||||
return "/" + MyPath;
|
||||
}
|
||||
|
||||
std::string Filesystem::Directory::LIST( std::vector<std::string> ActiveStreams ) {
|
||||
FillEntries( );
|
||||
std::string Filesystem::Directory::LIST(std::vector<std::string> ActiveStreams){
|
||||
FillEntries();
|
||||
int MyPermissions;
|
||||
std::stringstream Converter;
|
||||
passwd* pwd;//For Username
|
||||
group* grp;//For Groupname
|
||||
tm* tm;//For time localisation
|
||||
char datestring[256];//For time localisation
|
||||
|
||||
passwd* pwd; //For Username
|
||||
group* grp; //For Groupname
|
||||
tm* tm; //For time localisation
|
||||
char datestring[256]; //For time localisation
|
||||
|
||||
std::string MyLoc = MyBase + MyPath;
|
||||
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() );
|
||||
fprintf( stderr, "%s active?: %d\n", (*it).first.c_str(), Active );
|
||||
fprintf( stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath] );
|
||||
fprintf( stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE );
|
||||
if( ( Active && ( MyVisible[MyPath] & S_ACTIVE ) ) || ( (!Active) && ( MyVisible[MyPath] & S_INACTIVE ) ) || ( ((*it).second.st_mode / 010000 ) == 4 ) ) {
|
||||
if( ((*it).second.st_mode / 010000) == 4 ) { Converter << 'd'; } else { Converter << '-'; }
|
||||
MyPermissions = ( ( (*it).second.st_mode % 010000 ) / 0100 );
|
||||
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 % 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 << '-'; }
|
||||
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());
|
||||
fprintf(stderr, "%s active?: %d\n", ( *it).first.c_str(), Active);
|
||||
fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]);
|
||||
fprintf(stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE);
|
||||
if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || (( !Active) && (MyVisible[MyPath] & S_INACTIVE)) || ((( *it).second.st_mode / 010000) == 4)){
|
||||
if ((( *it).second.st_mode / 010000) == 4){
|
||||
Converter << 'd';
|
||||
}else{
|
||||
Converter << '-';
|
||||
}
|
||||
MyPermissions = ((( *it).second.st_mode % 010000) / 0100);
|
||||
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 % 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 << (*it).second.st_nlink;
|
||||
Converter << ( *it).second.st_nlink;
|
||||
Converter << ' ';
|
||||
if( (pwd = getpwuid((*it).second.st_uid)) ) {
|
||||
if ((pwd = getpwuid(( *it).second.st_uid))){
|
||||
Converter << pwd->pw_name;
|
||||
} else {
|
||||
Converter << (*it).second.st_uid;
|
||||
}else{
|
||||
Converter << ( *it).second.st_uid;
|
||||
}
|
||||
Converter << ' ';
|
||||
if( (grp = getgrgid((*it).second.st_gid) ) ) {
|
||||
if ((grp = getgrgid(( *it).second.st_gid))){
|
||||
Converter << grp->gr_name;
|
||||
} else {
|
||||
Converter << (*it).second.st_gid;
|
||||
}else{
|
||||
Converter << ( *it).second.st_gid;
|
||||
}
|
||||
Converter << ' ';
|
||||
Converter << (*it).second.st_size;
|
||||
Converter << ( *it).second.st_size;
|
||||
Converter << ' ';
|
||||
tm = localtime(&((*it).second.st_mtime));
|
||||
tm = localtime( &(( *it).second.st_mtime));
|
||||
strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm);
|
||||
Converter << datestring;
|
||||
Converter << ' ';
|
||||
Converter << (*it).first;
|
||||
Converter << ( *it).first;
|
||||
Converter << '\n';
|
||||
}
|
||||
}
|
||||
return Converter.str();
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::CWD( std::string Path ) {
|
||||
if( Path[0] == '/' ) {
|
||||
Path.erase(0,1);
|
||||
bool Filesystem::Directory::CWD(std::string Path){
|
||||
if (Path[0] == '/'){
|
||||
Path.erase(0, 1);
|
||||
MyPath = Path;
|
||||
} else {
|
||||
if( MyPath != "" ) {
|
||||
}else{
|
||||
if (MyPath != ""){
|
||||
MyPath += "/";
|
||||
}
|
||||
MyPath += Path;
|
||||
}
|
||||
FillEntries();
|
||||
printf( "New Path: %s\n", MyPath.c_str() );
|
||||
if( MyPermissions.find( MyPath ) != MyPermissions.end() ) {
|
||||
printf( "\tPermissions: %d\n", MyPermissions[MyPath] );
|
||||
printf("New Path: %s\n", MyPath.c_str());
|
||||
if (MyPermissions.find(MyPath) != MyPermissions.end()){
|
||||
printf("\tPermissions: %d\n", MyPermissions[MyPath]);
|
||||
}
|
||||
return SimplifyPath( );
|
||||
return SimplifyPath();
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::CDUP( ) {
|
||||
return CWD( ".." );
|
||||
bool Filesystem::Directory::CDUP(){
|
||||
return CWD("..");
|
||||
}
|
||||
|
||||
std::string Filesystem::Directory::RETR( std::string Path ) {
|
||||
std::string Filesystem::Directory::RETR(std::string Path){
|
||||
std::string Result;
|
||||
std::string FileName;
|
||||
if( Path[0] == '/' ) {
|
||||
Path.erase(0,1);
|
||||
if (Path[0] == '/'){
|
||||
Path.erase(0, 1);
|
||||
FileName = MyBase + Path;
|
||||
} else {
|
||||
}else{
|
||||
FileName = MyBase + MyPath + "/" + Path;
|
||||
}
|
||||
std::ifstream File;
|
||||
File.open( FileName.c_str() );
|
||||
while( File.good() ) { Result += File.get(); }
|
||||
File.open(FileName.c_str());
|
||||
while (File.good()){
|
||||
Result += File.get();
|
||||
}
|
||||
File.close();
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Filesystem::Directory::STOR( std::string Path, std::string Data ) {
|
||||
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_STOR ) ) {
|
||||
void Filesystem::Directory::STOR(std::string Path, std::string Data){
|
||||
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_STOR)){
|
||||
std::string FileName;
|
||||
if( Path[0] == '/' ) {
|
||||
Path.erase(0,1);
|
||||
if (Path[0] == '/'){
|
||||
Path.erase(0, 1);
|
||||
FileName = MyBase + Path;
|
||||
} else {
|
||||
}else{
|
||||
FileName = MyBase + MyPath + "/" + Path;
|
||||
}
|
||||
std::ofstream File;
|
||||
File.open( FileName.c_str() );
|
||||
File.open(FileName.c_str());
|
||||
File << Data;
|
||||
File.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::SimplifyPath( ) {
|
||||
bool Filesystem::Directory::SimplifyPath(){
|
||||
MyPath += "/";
|
||||
fprintf( stderr, "MyPath: %s\n", MyPath.c_str() );
|
||||
fprintf(stderr, "MyPath: %s\n", MyPath.c_str());
|
||||
std::vector<std::string> TempPath;
|
||||
std::string TempString;
|
||||
for( std::string::iterator it = MyPath.begin(); it != MyPath.end(); it ++ ) {
|
||||
if( (*it) == '/' ) {
|
||||
if( TempString == ".." ) {
|
||||
if( !TempPath.size() ) {
|
||||
for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++){
|
||||
if (( *it) == '/'){
|
||||
if (TempString == ".."){
|
||||
if ( !TempPath.size()){
|
||||
return false;
|
||||
}
|
||||
TempPath.erase( (TempPath.end()-1) );
|
||||
} else if ( TempString != "." && TempString != "" ) {
|
||||
TempPath.push_back( TempString );
|
||||
TempPath.erase((TempPath.end() - 1));
|
||||
}else if (TempString != "." && TempString != ""){
|
||||
TempPath.push_back(TempString);
|
||||
}
|
||||
TempString = "";
|
||||
} else {
|
||||
TempString += (*it);
|
||||
}else{
|
||||
TempString += ( *it);
|
||||
}
|
||||
}
|
||||
MyPath = "";
|
||||
for( std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++ ) {
|
||||
MyPath += (*it);
|
||||
if( it != ( TempPath.end() - 1 ) ) { MyPath += "/"; }
|
||||
for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++){
|
||||
MyPath += ( *it);
|
||||
if (it != (TempPath.end() - 1)){
|
||||
MyPath += "/";
|
||||
}
|
||||
}
|
||||
if( MyVisible.find( MyPath ) == MyVisible.end() ) {
|
||||
if (MyVisible.find(MyPath) == MyVisible.end()){
|
||||
MyVisible[MyPath] = S_ALL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::DELE( std::string Path ) {
|
||||
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_DELE ) ) {
|
||||
bool Filesystem::Directory::DELE(std::string Path){
|
||||
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_DELE)){
|
||||
std::string FileName;
|
||||
if( Path[0] == '/' ) {
|
||||
Path.erase(0,1);
|
||||
if (Path[0] == '/'){
|
||||
Path.erase(0, 1);
|
||||
FileName = MyBase + Path;
|
||||
} else {
|
||||
}else{
|
||||
FileName = MyBase + MyPath + "/" + Path;
|
||||
}
|
||||
if( std::remove( FileName.c_str() ) ) {
|
||||
fprintf( stderr, "Removing file %s unsuccesfull\n", FileName.c_str() );
|
||||
if (std::remove(FileName.c_str())){
|
||||
fprintf(stderr, "Removing file %s unsuccesfull\n", FileName.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -217,39 +267,39 @@ bool Filesystem::Directory::DELE( std::string Path ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::MKD( std::string Path ) {
|
||||
bool Filesystem::Directory::MKD(std::string Path){
|
||||
std::string FileName;
|
||||
if( Path[0] == '/' ) {
|
||||
Path.erase(0,1);
|
||||
if (Path[0] == '/'){
|
||||
Path.erase(0, 1);
|
||||
FileName = MyBase + Path;
|
||||
} else {
|
||||
}else{
|
||||
FileName = MyBase + MyPath + "/" + Path;
|
||||
}
|
||||
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() );
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
MyVisible[FileName] = S_ALL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::Rename( std::string From, std::string To ) {
|
||||
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_RNFT ) ) {
|
||||
bool Filesystem::Directory::Rename(std::string From, std::string To){
|
||||
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_RNFT)){
|
||||
std::string FileFrom;
|
||||
if( From[0] == '/' ) {
|
||||
From.erase(0,1);
|
||||
if (From[0] == '/'){
|
||||
From.erase(0, 1);
|
||||
FileFrom = MyBase + From;
|
||||
} else {
|
||||
}else{
|
||||
FileFrom = MyBase + MyPath + "/" + From;
|
||||
}
|
||||
std::string FileTo;
|
||||
if( To[0] == '/' ) {
|
||||
if (To[0] == '/'){
|
||||
FileTo = MyBase + To;
|
||||
} else {
|
||||
}else{
|
||||
FileTo = MyBase + MyPath + "/" + To;
|
||||
}
|
||||
if( std::rename( FileFrom.c_str(), FileTo.c_str() ) ) {
|
||||
fprintf( stderr, "Renaming file %s to %s unsuccesfull\n", 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());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -257,17 +307,17 @@ bool Filesystem::Directory::Rename( std::string From, std::string To ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Filesystem::Directory::SetPermissions( std::string Path, char Permissions ) {
|
||||
void Filesystem::Directory::SetPermissions(std::string Path, char Permissions){
|
||||
MyPermissions[Path] = Permissions;
|
||||
}
|
||||
|
||||
bool Filesystem::Directory::HasPermission( char Permission ) {
|
||||
if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & Permission ) ) {
|
||||
bool Filesystem::Directory::HasPermission(char Permission){
|
||||
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & Permission)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Filesystem::Directory::SetVisibility( std::string Pathname, char Visible ) {
|
||||
void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible){
|
||||
MyVisible[Pathname] = Visible;
|
||||
}
|
||||
|
|
|
@ -21,49 +21,47 @@
|
|||
#include <errno.h>
|
||||
|
||||
namespace Filesystem {
|
||||
enum DIR_Permissions {
|
||||
P_LIST = 0x01,//List
|
||||
P_RETR = 0x02,//Retrieve
|
||||
P_STOR = 0x04,//Store
|
||||
P_RNFT = 0x08,//Rename From/To
|
||||
P_DELE = 0x10,//Delete
|
||||
P_MKD = 0x20,//Make directory
|
||||
P_RMD = 0x40,//Remove directory
|
||||
enum DIR_Permissions{
|
||||
P_LIST = 0x01, //List
|
||||
P_RETR = 0x02, //Retrieve
|
||||
P_STOR = 0x04, //Store
|
||||
P_RNFT = 0x08, //Rename From/To
|
||||
P_DELE = 0x10, //Delete
|
||||
P_MKD = 0x20, //Make directory
|
||||
P_RMD = 0x40, //Remove directory
|
||||
};
|
||||
|
||||
enum DIR_Show {
|
||||
S_NONE = 0x00,
|
||||
S_ACTIVE = 0x01,
|
||||
S_INACTIVE = 0x02,
|
||||
S_ALL = 0x03,
|
||||
|
||||
enum DIR_Show{
|
||||
S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03,
|
||||
};
|
||||
|
||||
class Directory {
|
||||
|
||||
class Directory{
|
||||
public:
|
||||
Directory( std::string PathName = "", std::string BasePath = ".");
|
||||
~Directory( );
|
||||
void Print( );
|
||||
bool IsDir( );
|
||||
std::string PWD( );
|
||||
std::string LIST( std::vector<std::string> ActiveStreams = std::vector<std::string>() );
|
||||
bool CWD( std::string Path );
|
||||
bool CDUP( );
|
||||
bool DELE( std::string Path );
|
||||
bool MKD( std::string Path );
|
||||
std::string RETR( std::string Path );
|
||||
void STOR( std::string Path, std::string Data );
|
||||
bool Rename( std::string From, std::string To );
|
||||
void SetPermissions( std::string PathName, char Permissions );
|
||||
bool HasPermission( char Permission );
|
||||
void SetVisibility( std::string Pathname, char Visible );
|
||||
Directory(std::string PathName = "", std::string BasePath = ".");
|
||||
~Directory();
|
||||
void Print();
|
||||
bool IsDir();
|
||||
std::string PWD();
|
||||
std::string LIST(std::vector<std::string> ActiveStreams = std::vector<std::string>());
|
||||
bool CWD(std::string Path);
|
||||
bool CDUP();
|
||||
bool DELE(std::string Path);
|
||||
bool MKD(std::string Path);
|
||||
std::string RETR(std::string Path);
|
||||
void STOR(std::string Path, std::string Data);
|
||||
bool Rename(std::string From, std::string To);
|
||||
void SetPermissions(std::string PathName, char Permissions);
|
||||
bool HasPermission(char Permission);
|
||||
void SetVisibility(std::string Pathname, char Visible);
|
||||
private:
|
||||
bool ValidDir;
|
||||
bool SimplifyPath( );
|
||||
void FillEntries( );
|
||||
bool SimplifyPath();
|
||||
void FillEntries();
|
||||
std::string MyBase;
|
||||
std::string MyPath;
|
||||
std::map< std::string, struct stat > Entries;
|
||||
std::map< std::string, char > MyPermissions;
|
||||
std::map< std::string, char > MyVisible;
|
||||
};//Directory Class
|
||||
};//Filesystem namespace
|
||||
std::map<std::string, struct stat> Entries;
|
||||
std::map<std::string, char> MyPermissions;
|
||||
std::map<std::string, char> MyVisible;
|
||||
};
|
||||
//Directory Class
|
||||
}//Filesystem namespace
|
||||
|
|
710
lib/flv_tag.cpp
710
lib/flv_tag.cpp
File diff suppressed because it is too large
Load diff
|
@ -7,10 +7,11 @@
|
|||
#include "json.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
//forward declaration of RTMPStream::Chunk to avoid circular dependencies.
|
||||
namespace RTMPStream{
|
||||
namespace RTMPStream {
|
||||
class Chunk;
|
||||
};
|
||||
}
|
||||
|
||||
/// This namespace holds all FLV-parsing related functionality.
|
||||
namespace FLV {
|
||||
|
@ -24,7 +25,7 @@ namespace 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.
|
||||
class Tag {
|
||||
class Tag{
|
||||
public:
|
||||
int len; ///< Actual length of tag.
|
||||
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.
|
||||
Tag(); ///< Constructor for a new, empty, 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.
|
||||
//loader functions
|
||||
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, uint64_t val);
|
||||
bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem);
|
||||
};//Tag
|
||||
};
|
||||
//Tag
|
||||
|
||||
};//FLV namespace
|
||||
}//FLV namespace
|
||||
|
|
488
lib/ftp.cpp
488
lib/ftp.cpp
|
@ -1,7 +1,8 @@
|
|||
#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;
|
||||
MyPassivePort = 0;
|
||||
USER = "";
|
||||
PASS = "";
|
||||
MODE = MODE_STREAM;
|
||||
|
@ -10,78 +11,150 @@ FTP::User::User( Socket::Connection NewConnection, std::map<std::string,std::str
|
|||
PORT = 20;
|
||||
RNFR = "";
|
||||
AllCredentials = Credentials;
|
||||
|
||||
MyDir = Filesystem::Directory( "", FTPBasePath );
|
||||
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( "Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR );
|
||||
MyDir.SetPermissions( "OnDemand", Filesystem::P_LIST | Filesystem::P_RETR );
|
||||
MyDir.SetPermissions( "Live", Filesystem::P_LIST );
|
||||
|
||||
MyDir.SetVisibility( "Converted", Filesystem::S_INACTIVE );
|
||||
MyDir.SetVisibility( "OnDemand", Filesystem::S_ACTIVE );
|
||||
|
||||
JSON::Value MyConfig = JSON::fromFile( "/tmp/mist/streamlist" );
|
||||
fprintf( stderr, "Streamamount: %d\n", MyConfig["streams"].size() );
|
||||
for( JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++ ) {
|
||||
std::string ThisStream = (*it).second["channel"]["URL"].toString();
|
||||
ThisStream.erase( ThisStream.begin() );
|
||||
ThisStream.erase( ThisStream.end() - 1 );
|
||||
while( ThisStream.find( '/' ) != std::string::npos ) {
|
||||
ThisStream.erase(0,ThisStream.find('/')+1);
|
||||
|
||||
MyDir = Filesystem::Directory("", FTPBasePath);
|
||||
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("Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR);
|
||||
MyDir.SetPermissions("OnDemand", Filesystem::P_LIST | Filesystem::P_RETR);
|
||||
MyDir.SetPermissions("Live", Filesystem::P_LIST);
|
||||
|
||||
MyDir.SetVisibility("Converted", Filesystem::S_INACTIVE);
|
||||
MyDir.SetVisibility("OnDemand", Filesystem::S_ACTIVE);
|
||||
|
||||
JSON::Value MyConfig = JSON::fromFile("/tmp/mist/streamlist");
|
||||
fprintf(stderr, "Streamamount: %d\n", MyConfig["streams"].size());
|
||||
for (JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++){
|
||||
std::string ThisStream = ( *it).second["channel"]["URL"].toString();
|
||||
ThisStream.erase(ThisStream.begin());
|
||||
ThisStream.erase(ThisStream.end() - 1);
|
||||
while (ThisStream.find('/') != std::string::npos){
|
||||
ThisStream.erase(0, ThisStream.find('/') + 1);
|
||||
}
|
||||
ActiveStreams.push_back( ThisStream );
|
||||
fprintf( stderr, "\t%s\n", ThisStream.c_str() );
|
||||
ActiveStreams.push_back(ThisStream);
|
||||
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;
|
||||
if( Command.substr(0,4) == "NOOP" ) { ThisCmd = CMD_NOOP; Command.erase(0,5); }
|
||||
if( Command.substr(0,4) == "USER" ) { ThisCmd = CMD_USER; Command.erase(0,5); }
|
||||
if( Command.substr(0,4) == "PASS" ) { ThisCmd = CMD_PASS; 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) == "RETR" ) { ThisCmd = CMD_RETR; 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 ) {
|
||||
if (Command.substr(0, 4) == "NOOP"){
|
||||
ThisCmd = CMD_NOOP;
|
||||
Command.erase(0, 5);
|
||||
}
|
||||
if (Command.substr(0, 4) == "USER"){
|
||||
ThisCmd = CMD_USER;
|
||||
Command.erase(0, 5);
|
||||
}
|
||||
if (Command.substr(0, 4) == "PASS"){
|
||||
ThisCmd = CMD_PASS;
|
||||
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) == "RETR"){
|
||||
ThisCmd = CMD_RETR;
|
||||
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: {
|
||||
return 200;//Command okay.
|
||||
return 200; //Command okay.
|
||||
break;
|
||||
}
|
||||
case CMD_USER: {
|
||||
USER = "";
|
||||
PASS = "";
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if (Command == ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
USER = Command;
|
||||
return 331;//User name okay, need password.
|
||||
return 331; //User name okay, need password.
|
||||
break;
|
||||
}
|
||||
case CMD_PASS: {
|
||||
if( USER == "" ) { return 503; }//Bad sequence of commands
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if (USER == ""){
|
||||
return 503;
|
||||
} //Bad sequence of commands
|
||||
if (Command == ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
PASS = Command;
|
||||
if( !LoggedIn( ) ) {
|
||||
if ( !LoggedIn()){
|
||||
USER = "";
|
||||
PASS ="";
|
||||
return 530;//Not logged in.
|
||||
PASS = "";
|
||||
return 530; //Not logged in.
|
||||
}
|
||||
return 230;
|
||||
break;
|
||||
|
@ -89,135 +162,184 @@ int FTP::User::ParseCommand( std::string Command ) {
|
|||
case CMD_LIST: {
|
||||
std::cout << "Listening on :" << MyPassivePort << "\n";
|
||||
Socket::Connection Connected = Passive.accept();
|
||||
if( Connected.connected() ) {
|
||||
Conn.Send( "125 Data connection already open; transfer starting.\n" );
|
||||
} else {
|
||||
Conn.Send( "150 File status okay; about to open data connection.\n" );
|
||||
if (Connected.connected()){
|
||||
Conn.Send("125 Data connection already open; transfer starting.\n");
|
||||
}else{
|
||||
Conn.Send("150 File status okay; about to open data connection.\n");
|
||||
}
|
||||
while( !Connected.connected() ) {
|
||||
while ( !Connected.connected()){
|
||||
Connected = Passive.accept();
|
||||
}
|
||||
fprintf( stderr, "Sending LIST information\n" );
|
||||
std::string tmpstr = MyDir.LIST( ActiveStreams );
|
||||
Connected.Send( tmpstr );
|
||||
Connected.close( );
|
||||
fprintf(stderr, "Sending LIST information\n");
|
||||
std::string tmpstr = MyDir.LIST(ActiveStreams);
|
||||
Connected.Send(tmpstr);
|
||||
Connected.close();
|
||||
return 226;
|
||||
break;
|
||||
}
|
||||
case CMD_QUIT: {
|
||||
return 221;//Service closing control connection. Logged out if appropriate.
|
||||
return 221; //Service closing control connection. Logged out if appropriate.
|
||||
break;
|
||||
}
|
||||
case CMD_PORT: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
PORT = atoi( Command.c_str() );
|
||||
return 200;//Command okay.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
if (Command == ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
PORT = atoi(Command.c_str());
|
||||
return 200; //Command okay.
|
||||
break;
|
||||
}
|
||||
case CMD_EPSV: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
MyPassivePort = (rand() % 9999);
|
||||
std::cout << ":" << MyPassivePort << "\n";
|
||||
Passive = Socket::Server(MyPassivePort,"0.0.0.0",true);
|
||||
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
|
||||
return 229;
|
||||
break;
|
||||
}
|
||||
case CMD_PASV: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
MyPassivePort = (rand() % 9999) + 49152;
|
||||
std::cout << ":" << MyPassivePort << "\n";
|
||||
Passive = Socket::Server(MyPassivePort,"0.0.0.0",true);
|
||||
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
|
||||
return 227;
|
||||
break;
|
||||
}
|
||||
case CMD_RETR: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( !MyDir.HasPermission( Filesystem::P_RETR ) ) { return 550; }//Access denied.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //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";
|
||||
Socket::Connection Connected = Passive.accept();
|
||||
if( Connected.connected() ) {
|
||||
Conn.Send( "125 Data connection already open; transfer starting.\n" );
|
||||
} else {
|
||||
Conn.Send( "150 File status okay; about to open data connection.\n" );
|
||||
if (Connected.connected()){
|
||||
Conn.Send("125 Data connection already open; transfer starting.\n");
|
||||
}else{
|
||||
Conn.Send("150 File status okay; about to open data connection.\n");
|
||||
}
|
||||
while( !Connected.connected() ) {
|
||||
while ( !Connected.connected()){
|
||||
Connected = Passive.accept();
|
||||
}
|
||||
fprintf( stderr, "Sending RETR information\n" );
|
||||
std::string tmpstr = MyDir.RETR( Command );
|
||||
Connected.Send( tmpstr );
|
||||
fprintf(stderr, "Sending RETR information\n");
|
||||
std::string tmpstr = MyDir.RETR(Command);
|
||||
Connected.Send(tmpstr);
|
||||
Connected.close();
|
||||
return 226;
|
||||
break;
|
||||
}
|
||||
case CMD_STOR: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( !MyDir.HasPermission( Filesystem::P_STOR ) ) { return 550; }//Access denied.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //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";
|
||||
Socket::Connection Connected = Passive.accept();
|
||||
if( Connected.connected() ) {
|
||||
Conn.Send( "125 Data connection already open; transfer starting.\n" );
|
||||
} else {
|
||||
Conn.Send( "150 File status okay; about to open data connection.\n" );
|
||||
if (Connected.connected()){
|
||||
Conn.Send("125 Data connection already open; transfer starting.\n");
|
||||
}else{
|
||||
Conn.Send("150 File status okay; about to open data connection.\n");
|
||||
}
|
||||
while( !Connected.connected() ) {
|
||||
while ( !Connected.connected()){
|
||||
Connected = Passive.accept();
|
||||
}
|
||||
fprintf( stderr, "Reading STOR information\n" );
|
||||
fprintf(stderr, "Reading STOR information\n");
|
||||
std::string Buffer;
|
||||
while( Connected.spool() ) { }
|
||||
while (Connected.spool()){
|
||||
}
|
||||
/// \todo Comment me back in. ^_^
|
||||
//Buffer = Connected.Received();
|
||||
MyDir.STOR( Command, Buffer );
|
||||
MyDir.STOR(Command, Buffer);
|
||||
return 250;
|
||||
break;
|
||||
}
|
||||
case CMD_TYPE: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
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] ) {
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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': {
|
||||
if( Command.size() > 1 ) {
|
||||
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter.
|
||||
if (Command.size() > 1){
|
||||
if (Command[1] != ' '){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
if (Command[2] != 'N'){
|
||||
return 504;
|
||||
} //Command not implemented for that parameter.
|
||||
}
|
||||
TYPE = TYPE_ASCII_NONPRINT;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
if( Command.size() > 1 ) {
|
||||
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter.
|
||||
if (Command.size() > 1){
|
||||
if (Command[1] != ' '){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
if (Command[2] != 'N'){
|
||||
return 504;
|
||||
} //Command not implemented for that parameter.
|
||||
}
|
||||
TYPE = TYPE_IMAGE_NONPRINT;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return 504;//Command not implemented for that parameter.
|
||||
return 504; //Command not implemented for that parameter.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 200;//Command okay.
|
||||
return 200; //Command okay.
|
||||
break;
|
||||
}
|
||||
case CMD_MODE: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
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.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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;
|
||||
return 200;//Command okay.
|
||||
return 200; //Command okay.
|
||||
break;
|
||||
}
|
||||
case CMD_STRU: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
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] ) {
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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': {
|
||||
STRU = STRU_FILE;
|
||||
break;
|
||||
|
@ -227,24 +349,30 @@ int FTP::User::ParseCommand( std::string Command ) {
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
return 504;//Command not implemented for that parameter.
|
||||
return 504; //Command not implemented for that parameter.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 200;//Command okay.
|
||||
return 200; //Command okay.
|
||||
break;
|
||||
}
|
||||
case CMD_PWD: {
|
||||
if( !LoggedIn( ) ) { return 550; }//Not logged in.
|
||||
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
return 2570;//257 -- 0 to indicate PWD over MKD
|
||||
if ( !LoggedIn()){
|
||||
return 550;
|
||||
} //Not logged in.
|
||||
if (Command != ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
return 2570; //257 -- 0 to indicate PWD over MKD
|
||||
break;
|
||||
}
|
||||
case CMD_CWD: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
Filesystem::Directory TmpDir = MyDir;
|
||||
if( TmpDir.CWD( Command ) ) {
|
||||
if( TmpDir.IsDir( ) ) {
|
||||
if (TmpDir.CWD(Command)){
|
||||
if (TmpDir.IsDir()){
|
||||
MyDir = TmpDir;
|
||||
return 250;
|
||||
}
|
||||
|
@ -253,11 +381,15 @@ int FTP::User::ParseCommand( std::string Command ) {
|
|||
break;
|
||||
}
|
||||
case CMD_CDUP: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
if (Command != ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
Filesystem::Directory TmpDir = MyDir;
|
||||
if( TmpDir.CDUP( ) ) {
|
||||
if( TmpDir.IsDir( ) ) {
|
||||
if (TmpDir.CDUP()){
|
||||
if (TmpDir.IsDir()){
|
||||
MyDir = TmpDir;
|
||||
return 250;
|
||||
}
|
||||
|
@ -266,62 +398,98 @@ int FTP::User::ParseCommand( std::string Command ) {
|
|||
break;
|
||||
}
|
||||
case CMD_DELE: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( !MyDir.DELE( Command ) ) { return 550; }
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
if (Command == ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
if ( !MyDir.DELE(Command)){
|
||||
return 550;
|
||||
}
|
||||
return 250;
|
||||
break;
|
||||
}
|
||||
case CMD_RMD: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( !MyDir.HasPermission( Filesystem::P_RMD ) ) { return 550; }
|
||||
if( !MyDir.DELE( Command ) ) { return 550; }
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case CMD_MKD: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if( !MyDir.HasPermission( Filesystem::P_MKD ) ) { return 550; }
|
||||
if( !MyDir.MKD( Command ) ) { return 550; }
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case CMD_RNFR: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments.
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
if (Command == ""){
|
||||
return 501;
|
||||
} //Syntax error in parameters or arguments.
|
||||
RNFR = Command;
|
||||
return 350;//Awaiting further information
|
||||
return 350; //Awaiting further information
|
||||
}
|
||||
case CMD_RNTO: {
|
||||
if( !LoggedIn( ) ) { return 530; }//Not logged in.
|
||||
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; }
|
||||
if ( !LoggedIn()){
|
||||
return 530;
|
||||
} //Not logged in.
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
return 502;//Command not implemented.
|
||||
return 502; //Command not implemented.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FTP::User::LoggedIn( ) {
|
||||
if( USER == "" || PASS == "" ) { return false; }
|
||||
if( !AllCredentials.size() ) {
|
||||
bool FTP::User::LoggedIn(){
|
||||
if (USER == "" || PASS == ""){
|
||||
return false;
|
||||
}
|
||||
if ( !AllCredentials.size()){
|
||||
return true;
|
||||
}
|
||||
if( ( AllCredentials.find( USER ) != AllCredentials.end() ) && AllCredentials[USER] == PASS ) {
|
||||
if ((AllCredentials.find(USER) != AllCredentials.end()) && AllCredentials[USER] == PASS){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string FTP::User::NumToMsg( int MsgNum ) {
|
||||
std::string FTP::User::NumToMsg(int MsgNum){
|
||||
std::string Result;
|
||||
switch( MsgNum ) {
|
||||
switch (MsgNum){
|
||||
case 200: {
|
||||
Result = "200 Message okay.\n";
|
||||
break;
|
||||
|
@ -335,15 +503,15 @@ std::string FTP::User::NumToMsg( int MsgNum ) {
|
|||
break;
|
||||
}
|
||||
case 227: {
|
||||
std::stringstream sstr;
|
||||
std::stringstream sstr;
|
||||
sstr << "227 Entering passive mode (0,0,0,0,";
|
||||
sstr << (MyPassivePort >> 8) % 256;
|
||||
sstr << ",";
|
||||
sstr << MyPassivePort % 256;
|
||||
sstr << ").\n";
|
||||
Result = sstr.str();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 229: {
|
||||
std::stringstream sstr;
|
||||
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";
|
||||
break;
|
||||
}
|
||||
case 2570: {//PWD
|
||||
Result = "257 \"" + MyDir.PWD( ) + "\" selected as PWD\n";
|
||||
case 2570: { //PWD
|
||||
Result = "257 \"" + MyDir.PWD() + "\" selected as PWD\n";
|
||||
break;
|
||||
}
|
||||
case 2571: {//MKD
|
||||
Result = "257 \"" + MyDir.PWD( ) + "\" created\n";
|
||||
case 2571: { //MKD
|
||||
Result = "257 \"" + MyDir.PWD() + "\" created\n";
|
||||
break;
|
||||
}
|
||||
case 331: {
|
||||
|
|
59
lib/ftp.h
59
lib/ftp.h
|
@ -13,22 +13,23 @@
|
|||
|
||||
namespace FTP {
|
||||
static std::string FTPBasePath = "/tmp/mist/OnDemand/";
|
||||
|
||||
enum Mode {
|
||||
|
||||
enum Mode{
|
||||
MODE_STREAM,
|
||||
};//FTP::Mode enumeration
|
||||
|
||||
enum Structure {
|
||||
STRU_FILE,
|
||||
STRU_RECORD,
|
||||
};//FTP::Structure enumeration
|
||||
|
||||
enum Type {
|
||||
TYPE_ASCII_NONPRINT,
|
||||
TYPE_IMAGE_NONPRINT,
|
||||
};//FTP::Type enumeration
|
||||
|
||||
enum Commands {
|
||||
};
|
||||
//FTP::Mode enumeration
|
||||
|
||||
enum Structure{
|
||||
STRU_FILE, STRU_RECORD,
|
||||
};
|
||||
//FTP::Structure enumeration
|
||||
|
||||
enum Type{
|
||||
TYPE_ASCII_NONPRINT, TYPE_IMAGE_NONPRINT,
|
||||
};
|
||||
//FTP::Type enumeration
|
||||
|
||||
enum Commands{
|
||||
CMD_NOCMD,
|
||||
CMD_NOOP,
|
||||
CMD_USER,
|
||||
|
@ -51,18 +52,19 @@ namespace FTP {
|
|||
CMD_MKD,
|
||||
CMD_RNFR,
|
||||
CMD_RNTO,
|
||||
};//FTP::Commands enumeration
|
||||
|
||||
class User {
|
||||
};
|
||||
//FTP::Commands enumeration
|
||||
|
||||
class User{
|
||||
public:
|
||||
User( Socket::Connection NewConnection, std::map<std::string,std::string> Credentials);
|
||||
~User( );
|
||||
int ParseCommand( std::string Command );
|
||||
bool LoggedIn( );
|
||||
std::string NumToMsg( int MsgNum );
|
||||
User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials);
|
||||
~User();
|
||||
int ParseCommand(std::string Command);
|
||||
bool LoggedIn();
|
||||
std::string NumToMsg(int MsgNum);
|
||||
Socket::Connection Conn;
|
||||
private:
|
||||
std::map<std::string,std::string> AllCredentials;
|
||||
std::map<std::string, std::string> AllCredentials;
|
||||
std::string USER;
|
||||
std::string PASS;
|
||||
Mode MODE;
|
||||
|
@ -73,7 +75,8 @@ namespace FTP {
|
|||
int MyPassivePort;
|
||||
Filesystem::Directory MyDir;
|
||||
std::string RNFR;
|
||||
std::vector< std::string > ActiveStreams;
|
||||
};//FTP::User class
|
||||
|
||||
};//FTP Namespace
|
||||
std::vector<std::string> ActiveStreams;
|
||||
};
|
||||
//FTP::User class
|
||||
|
||||
}//FTP Namespace
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
|
||||
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
|
||||
/// 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.
|
||||
void HTTP::Parser::Clean(){
|
||||
|
@ -27,11 +29,13 @@ void HTTP::Parser::Clean(){
|
|||
std::string & HTTP::Parser::BuildRequest(){
|
||||
/// \todo Include GET/POST variable parsing?
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";}
|
||||
builder = method+" "+url+" "+protocol+"\r\n";
|
||||
for (it=headers.begin(); it != headers.end(); it++){
|
||||
if ((*it).first != "" && (*it).second != ""){
|
||||
builder += (*it).first + ": " + (*it).second + "\r\n";
|
||||
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
|
||||
protocol = "HTTP/1.0";
|
||||
}
|
||||
builder = method + " " + url + " " + protocol + "\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;
|
||||
|
@ -47,12 +51,14 @@ std::string & HTTP::Parser::BuildRequest(){
|
|||
std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){
|
||||
/// \todo Include GET/POST variable parsing?
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";}
|
||||
builder = protocol+" "+code+" "+message+"\r\n";
|
||||
for (it=headers.begin(); it != headers.end(); it++){
|
||||
if ((*it).first != "" && (*it).second != ""){
|
||||
if ((*it).first != "Content-Length" || (*it).second != "0"){
|
||||
builder += (*it).first + ": " + (*it).second + "\r\n";
|
||||
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
|
||||
protocol = "HTTP/1.0";
|
||||
}
|
||||
builder = protocol + " " + code + " " + message + "\r\n";
|
||||
for (it = headers.begin(); it != headers.end(); it++){
|
||||
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){
|
||||
size_t startpos = s.find_first_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.
|
||||
|
@ -96,9 +106,13 @@ std::string HTTP::Parser::getUrl(){
|
|||
}
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
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.
|
||||
void HTTP::Parser::SetHeader(std::string i, int v){
|
||||
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);
|
||||
headers[i] = val;
|
||||
}
|
||||
|
@ -120,7 +134,7 @@ void HTTP::Parser::SetVar(std::string i, std::string v){
|
|||
Trim(i);
|
||||
Trim(v);
|
||||
//only set if there is actually a key
|
||||
if(!i.empty()){
|
||||
if ( !i.empty()){
|
||||
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.
|
||||
bool HTTP::Parser::Read(std::string & strbuf){
|
||||
return parse(strbuf);
|
||||
}//HTTPReader::Read
|
||||
} //HTTPReader::Read
|
||||
|
||||
#include <iostream>
|
||||
/// 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;
|
||||
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?
|
||||
while (!HTTPbuffer.empty()){
|
||||
if (!seenHeaders){
|
||||
while ( !HTTPbuffer.empty()){
|
||||
if ( !seenHeaders){
|
||||
f = HTTPbuffer.find('\n');
|
||||
if (f == std::string::npos) return false;
|
||||
tmpA = HTTPbuffer.substr(0, f);
|
||||
if (f+1 == HTTPbuffer.size()){
|
||||
if (f + 1 == HTTPbuffer.size()){
|
||||
HTTPbuffer.clear();
|
||||
}else{
|
||||
HTTPbuffer.erase(0, f+1);
|
||||
HTTPbuffer.erase(0, f + 1);
|
||||
}
|
||||
while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));}
|
||||
if (!seenReq){
|
||||
while (tmpA.find('\r') != std::string::npos){
|
||||
tmpA.erase(tmpA.find('\r'));
|
||||
}
|
||||
if ( !seenReq){
|
||||
seenReq = true;
|
||||
f = tmpA.find(' ');
|
||||
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(' ');
|
||||
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;
|
||||
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{seenReq = false;}
|
||||
}else{
|
||||
seenReq = false;
|
||||
}
|
||||
}else{
|
||||
seenReq = false;
|
||||
}
|
||||
}else{
|
||||
if (tmpA.size() == 0){
|
||||
seenHeaders = true;
|
||||
body.clear();
|
||||
if (GetHeader("Content-Length") != ""){
|
||||
length = atoi(GetHeader("Content-Length").c_str());
|
||||
if (body.capacity() < length){body.reserve(length);}
|
||||
if (body.capacity() < length){
|
||||
body.reserve(length);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
f = tmpA.find(':');
|
||||
if (f == std::string::npos) continue;
|
||||
tmpB = tmpA.substr(0, f);
|
||||
tmpC = tmpA.substr(f+1);
|
||||
tmpC = tmpA.substr(f + 1);
|
||||
SetHeader(tmpB, tmpC);
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +228,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){
|
|||
}
|
||||
}
|
||||
return false; //empty input
|
||||
}//HTTPReader::parse
|
||||
} //HTTPReader::parse
|
||||
|
||||
/// Parses GET or POST-style variable data.
|
||||
/// 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]);
|
||||
}
|
||||
out += tmp;
|
||||
} else {
|
||||
if (in[i] == '+'){out += ' ';}else{out += in[i];}
|
||||
}else{
|
||||
if (in[i] == '+'){
|
||||
out += ' ';
|
||||
}else{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
@ -276,15 +304,16 @@ std::string HTTP::Parser::urlunescape(const std::string & in){
|
|||
/// Helper function for urlunescape.
|
||||
/// Takes a single char input and outputs its integer hex value.
|
||||
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.
|
||||
std::string HTTP::Parser::urlencode(const std::string &c){
|
||||
std::string escaped="";
|
||||
std::string escaped = "";
|
||||
int max = c.length();
|
||||
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]=='\'')){
|
||||
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] == '\'')){
|
||||
escaped.append( &c[i], 1);
|
||||
}else{
|
||||
escaped.append("%");
|
||||
|
@ -297,14 +326,14 @@ std::string HTTP::Parser::urlencode(const std::string &c){
|
|||
/// Helper function for urlescape.
|
||||
/// Encodes a character as two hex digits.
|
||||
std::string HTTP::Parser::hex(char dec){
|
||||
char dig1 = (dec&0xF0)>>4;
|
||||
char dig2 = (dec&0x0F);
|
||||
if (dig1<= 9) dig1+=48;
|
||||
if (10<= dig1 && dig1<=15) dig1+=97-10;
|
||||
if (dig2<= 9) dig2+=48;
|
||||
if (10<= dig2 && dig2<=15) dig2+=97-10;
|
||||
char dig1 = (dec & 0xF0) >> 4;
|
||||
char dig2 = (dec & 0x0F);
|
||||
if (dig1 <= 9) dig1 += 48;
|
||||
if (10 <= dig1 && dig1 <= 15) dig1 += 97 - 10;
|
||||
if (dig2 <= 9) dig2 += 48;
|
||||
if (10 <= dig2 && dig2 <= 15) dig2 += 97 - 10;
|
||||
std::string r;
|
||||
r.append(&dig1, 1);
|
||||
r.append(&dig2, 1);
|
||||
r.append( &dig1, 1);
|
||||
r.append( &dig2, 1);
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
/// Holds all HTTP processing related code.
|
||||
namespace HTTP{
|
||||
namespace HTTP {
|
||||
/// Simple class for reading and writing HTTP 1.0 and 1.1.
|
||||
class Parser{
|
||||
public:
|
||||
|
@ -45,5 +45,7 @@ namespace HTTP{
|
|||
void Trim(std::string & s);
|
||||
static int unhex(char c);
|
||||
static std::string hex(char dec);
|
||||
};//HTTP::Parser class
|
||||
};//HTTP namespace
|
||||
};
|
||||
//HTTP::Parser class
|
||||
|
||||
}//HTTP namespace
|
||||
|
|
315
lib/json.cpp
315
lib/json.cpp
|
@ -7,7 +7,6 @@
|
|||
#include <stdint.h> //for uint64_t
|
||||
#include <string.h> //for memcpy
|
||||
#include <arpa/inet.h> //for htonl
|
||||
|
||||
int JSON::Value::c2hex(int c){
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
|
@ -15,7 +14,6 @@ int JSON::Value::c2hex(int c){
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::string JSON::Value::read_string(int separator, std::istream & fromstream){
|
||||
std::string out;
|
||||
bool escaped = false;
|
||||
|
@ -27,17 +25,28 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){
|
|||
}
|
||||
if (escaped){
|
||||
switch (c){
|
||||
case 'b': out += '\b'; break;
|
||||
case 'f': out += '\f'; break;
|
||||
case 'n': out += '\n'; break;
|
||||
case 'r': 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);
|
||||
case 'b':
|
||||
out += '\b';
|
||||
break;
|
||||
case 'f':
|
||||
out += '\f';
|
||||
break;
|
||||
case 'n':
|
||||
out += '\n';
|
||||
break;
|
||||
case 'r':
|
||||
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:
|
||||
out += (char)c;
|
||||
|
@ -58,14 +67,30 @@ std::string JSON::Value::string_escape(std::string val){
|
|||
std::string out = "\"";
|
||||
for (unsigned int i = 0; i < val.size(); ++i){
|
||||
switch (val[i]){
|
||||
case '"': out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
case '\n': 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];
|
||||
case '"':
|
||||
out += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
out += "\\\\";
|
||||
break;
|
||||
case '\n':
|
||||
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 += "\"";
|
||||
|
@ -76,9 +101,15 @@ std::string JSON::Value::string_escape(std::string val){
|
|||
void JSON::Value::skipToEnd(std::istream & fromstream){
|
||||
while (fromstream.good()){
|
||||
char peek = fromstream.peek();
|
||||
if (peek == ','){return;}
|
||||
if (peek == ']'){return;}
|
||||
if (peek == '}'){return;}
|
||||
if (peek == ','){
|
||||
return;
|
||||
}
|
||||
if (peek == ']'){
|
||||
return;
|
||||
}
|
||||
if (peek == '}'){
|
||||
return;
|
||||
}
|
||||
peek = fromstream.get();
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +132,7 @@ JSON::Value::Value(std::istream & fromstream){
|
|||
c = fromstream.get();
|
||||
myType = OBJECT;
|
||||
break;
|
||||
case '[':{
|
||||
case '[': {
|
||||
reading_array = true;
|
||||
c = fromstream.get();
|
||||
myType = ARRAY;
|
||||
|
@ -114,13 +145,13 @@ JSON::Value::Value(std::istream & fromstream){
|
|||
case '\'':
|
||||
case '"':
|
||||
c = fromstream.get();
|
||||
if (!reading_object){
|
||||
if ( !reading_object){
|
||||
myType = STRING;
|
||||
strVal = read_string(c, fromstream);
|
||||
return;
|
||||
}else{
|
||||
std::string tmpstr = read_string(c, fromstream);
|
||||
(*this)[tmpstr] = JSON::Value(fromstream);
|
||||
( *this)[tmpstr] = JSON::Value(fromstream);
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
|
@ -139,18 +170,22 @@ JSON::Value::Value(std::istream & fromstream){
|
|||
intVal += c - '0';
|
||||
break;
|
||||
case ',':
|
||||
if (!reading_object && !reading_array) return;
|
||||
if ( !reading_object && !reading_array) return;
|
||||
c = fromstream.get();
|
||||
if (reading_array){
|
||||
append(JSON::Value(fromstream));
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
if (reading_object){c = fromstream.get();}
|
||||
if (reading_object){
|
||||
c = fromstream.get();
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case ']':
|
||||
if (reading_array){c = fromstream.get();}
|
||||
if (reading_array){
|
||||
c = fromstream.get();
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case 't':
|
||||
|
@ -174,7 +209,7 @@ JSON::Value::Value(std::istream & fromstream){
|
|||
return;
|
||||
break;
|
||||
default:
|
||||
c = fromstream.get();//ignore this character
|
||||
c = fromstream.get(); //ignore this character
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
@ -185,12 +220,14 @@ JSON::Value::Value(std::istream & fromstream){
|
|||
JSON::Value::Value(const std::string & val){
|
||||
myType = STRING;
|
||||
strVal = val;
|
||||
intVal = 0;
|
||||
}
|
||||
|
||||
/// Sets this JSON::Value to the given string.
|
||||
JSON::Value::Value(const char * val){
|
||||
myType = STRING;
|
||||
strVal = val;
|
||||
intVal = 0;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
bool JSON::Value::operator==(const JSON::Value & rhs) const{
|
||||
if (myType != rhs.myType) return false;
|
||||
if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;}
|
||||
if (myType == STRING){return strVal == rhs.strVal;}
|
||||
if (myType == EMPTY){return true;}
|
||||
if (myType == INTEGER || myType == BOOL){
|
||||
return intVal == rhs.intVal;
|
||||
}
|
||||
if (myType == STRING){
|
||||
return strVal == rhs.strVal;
|
||||
}
|
||||
if (myType == EMPTY){
|
||||
return true;
|
||||
}
|
||||
if (myType == OBJECT){
|
||||
if (objVal.size() != rhs.objVal.size()) return false;
|
||||
for (std::map<std::string, Value>::const_iterator it = objVal.begin(); it != objVal.end(); ++it){
|
||||
if (!rhs.isMember(it->first)){return false;}
|
||||
if (it->second != rhs.objVal.find(it->first)->second){return false;}
|
||||
if ( !rhs.isMember(it->first)){
|
||||
return false;
|
||||
}
|
||||
if (it->second != rhs.objVal.find(it->first)->second){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -217,7 +264,9 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{
|
|||
if (arrVal.size() != rhs.arrVal.size()) return false;
|
||||
int i = 0;
|
||||
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++;
|
||||
}
|
||||
return true;
|
||||
|
@ -227,7 +276,7 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{
|
|||
|
||||
/// Compares a JSON::Value to another for equality.
|
||||
bool JSON::Value::operator!=(const JSON::Value & rhs) const{
|
||||
return !((*this) == rhs);
|
||||
return !(( *this) == rhs);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
|
@ -261,12 +310,12 @@ JSON::Value & JSON::Value::operator=(const long long int &rhs){
|
|||
|
||||
/// Sets this JSON::Value to the given integer.
|
||||
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.
|
||||
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.
|
||||
|
@ -280,7 +329,6 @@ JSON::Value::operator long long int(){
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Automatic conversion to std::string.
|
||||
/// Returns the raw string value if available, otherwise calls toString().
|
||||
JSON::Value::operator std::string(){
|
||||
|
@ -298,29 +346,40 @@ JSON::Value::operator std::string(){
|
|||
/// Automatic conversion to bool.
|
||||
/// Returns true if there is anything meaningful stored into this value.
|
||||
JSON::Value::operator bool(){
|
||||
if (myType == STRING){return strVal != "";}
|
||||
if (myType == INTEGER){return intVal != 0;}
|
||||
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...
|
||||
if (myType == STRING){
|
||||
return strVal != "";
|
||||
}
|
||||
if (myType == INTEGER){
|
||||
return intVal != 0;
|
||||
}
|
||||
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.
|
||||
const std::string JSON::Value::asString(){
|
||||
return (std::string)*this;
|
||||
return (std::string) *this;
|
||||
}
|
||||
/// Explicit conversion to long long int.
|
||||
const long long int JSON::Value::asInt(){
|
||||
return (long long int)*this;
|
||||
return (long long int) *this;
|
||||
}
|
||||
/// Explicit conversion to bool.
|
||||
const bool JSON::Value::asBool(){
|
||||
return (bool)*this;
|
||||
return (bool) *this;
|
||||
}
|
||||
|
||||
|
||||
/// Retrieves or sets the JSON::Value at this position in the object.
|
||||
/// Converts destructively to object if not already an object.
|
||||
JSON::Value & JSON::Value::operator[](const std::string i){
|
||||
|
@ -362,15 +421,19 @@ std::string JSON::Value::toPacked(){
|
|||
if (isInt() || isNull() || isBool()){
|
||||
r += 0x01;
|
||||
uint64_t numval = intVal;
|
||||
r += *(((char*)&numval)+7); r += *(((char*)&numval)+6);
|
||||
r += *(((char*)&numval)+5); r += *(((char*)&numval)+4);
|
||||
r += *(((char*)&numval)+3); r += *(((char*)&numval)+2);
|
||||
r += *(((char*)&numval)+1); r += *(((char*)&numval));
|
||||
r += *(((char*) &numval) + 7);
|
||||
r += *(((char*) &numval) + 6);
|
||||
r += *(((char*) &numval) + 5);
|
||||
r += *(((char*) &numval) + 4);
|
||||
r += *(((char*) &numval) + 3);
|
||||
r += *(((char*) &numval) + 2);
|
||||
r += *(((char*) &numval) + 1);
|
||||
r += *(((char*) &numval));
|
||||
}
|
||||
if (isString()){
|
||||
r += 0x02;
|
||||
r += strVal.size() / (256*256*256);
|
||||
r += strVal.size() / (256*256);
|
||||
r += strVal.size() / (256 * 256 * 256);
|
||||
r += strVal.size() / (256 * 256);
|
||||
r += strVal.size() / 256;
|
||||
r += strVal.size() % 256;
|
||||
r += strVal;
|
||||
|
@ -385,7 +448,9 @@ std::string JSON::Value::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();
|
||||
}
|
||||
if (isArray()){
|
||||
|
@ -393,11 +458,14 @@ std::string JSON::Value::toPacked(){
|
|||
for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){
|
||||
r += it->toPacked();
|
||||
}
|
||||
r += (char)0x0; r += (char)0x0; r += (char)0xEE;
|
||||
r += (char)0x0;
|
||||
r += (char)0x0;
|
||||
r += (char)0xEE;
|
||||
}
|
||||
return r;
|
||||
};//toPacked
|
||||
|
||||
}
|
||||
;
|
||||
//toPacked
|
||||
|
||||
/// 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.
|
||||
|
@ -422,14 +490,13 @@ std::string & JSON::Value::toNetPacked(){
|
|||
}
|
||||
//insert the packet length at bytes 4-7
|
||||
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
|
||||
memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size());
|
||||
}
|
||||
return strVal;
|
||||
}
|
||||
|
||||
|
||||
/// Converts this JSON::Value to valid JSON notation and returns it.
|
||||
/// Makes absolutely no attempts to pretty-print anything. :-)
|
||||
std::string JSON::Value::toString(){
|
||||
|
@ -449,7 +516,9 @@ std::string JSON::Value::toString(){
|
|||
if (arrVal.size() > 0){
|
||||
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
|
||||
tmp += it->toString();
|
||||
if (it + 1 != ArrEnd()){tmp += ",";}
|
||||
if (it + 1 != ArrEnd()){
|
||||
tmp += ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp += "]";
|
||||
|
@ -464,7 +533,9 @@ std::string JSON::Value::toString(){
|
|||
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
|
||||
tmp2 += "\"" + it2->first + "\":";
|
||||
tmp2 += it2->second.toString();
|
||||
if (it2 != it3){tmp2 += ",";}
|
||||
if (it2 != it3){
|
||||
tmp2 += ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp2 += "}";
|
||||
|
@ -475,7 +546,7 @@ std::string JSON::Value::toString(){
|
|||
default:
|
||||
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.
|
||||
|
@ -491,7 +562,7 @@ std::string JSON::Value::toPrettyString(int indentation){
|
|||
case STRING: {
|
||||
for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){
|
||||
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);
|
||||
|
@ -501,10 +572,12 @@ std::string JSON::Value::toPrettyString(int indentation){
|
|||
if (arrVal.size() > 0){
|
||||
std::string tmp = "[\n";
|
||||
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
|
||||
tmp += std::string(indentation+2, ' ')+it->toPrettyString(indentation+2);
|
||||
if (it + 1 != ArrEnd()){tmp += ",\n";}
|
||||
tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2);
|
||||
if (it + 1 != ArrEnd()){
|
||||
tmp += ",\n";
|
||||
}
|
||||
}
|
||||
tmp += "\n"+std::string(indentation, ' ')+"]";
|
||||
tmp += "\n" + std::string(indentation, ' ') + "]";
|
||||
return tmp;
|
||||
}else{
|
||||
return "[]";
|
||||
|
@ -517,11 +590,13 @@ std::string JSON::Value::toPrettyString(int indentation){
|
|||
ObjIter it3 = ObjEnd();
|
||||
--it3;
|
||||
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
|
||||
tmp2 += std::string(indentation+2, ' ')+"\"" + it2->first + "\":";
|
||||
tmp2 += it2->second.toPrettyString(indentation+2);
|
||||
if (it2 != it3){tmp2 += ",\n";}
|
||||
tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":";
|
||||
tmp2 += it2->second.toPrettyString(indentation + 2);
|
||||
if (it2 != it3){
|
||||
tmp2 += ",\n";
|
||||
}
|
||||
}
|
||||
tmp2 += "\n"+std::string(indentation, ' ')+"}";
|
||||
tmp2 += "\n" + std::string(indentation, ' ') + "}";
|
||||
return tmp2;
|
||||
}else{
|
||||
return "{}";
|
||||
|
@ -532,7 +607,7 @@ std::string JSON::Value::toPrettyString(int indentation){
|
|||
default:
|
||||
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.
|
||||
|
@ -563,11 +638,15 @@ void JSON::Value::prepend(const JSON::Value & rhs){
|
|||
/// given size.
|
||||
void JSON::Value::shrink(unsigned int size){
|
||||
if (myType == ARRAY){
|
||||
while (arrVal.size() > size){arrVal.pop_front();}
|
||||
while (arrVal.size() > size){
|
||||
arrVal.pop_front();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (myType == OBJECT){
|
||||
while (objVal.size() > size){objVal.erase(objVal.begin());}
|
||||
while (objVal.size() > size){
|
||||
objVal.erase(objVal.begin());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -671,62 +750,66 @@ JSON::Value JSON::fromFile(std::string filename){
|
|||
/// \param i Current parsing position in the raw data (defaults to 0).
|
||||
/// \returns A single JSON::Value, parsed from the raw data.
|
||||
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);
|
||||
#endif
|
||||
#endif
|
||||
switch (data[i]){
|
||||
case 0x01:{//integer
|
||||
case 0x01: { //integer
|
||||
unsigned char tmpdbl[8];
|
||||
tmpdbl[7] = data[i+1];
|
||||
tmpdbl[6] = data[i+2];
|
||||
tmpdbl[5] = data[i+3];
|
||||
tmpdbl[4] = data[i+4];
|
||||
tmpdbl[3] = data[i+5];
|
||||
tmpdbl[2] = data[i+6];
|
||||
tmpdbl[1] = data[i+7];
|
||||
tmpdbl[0] = data[i+8];
|
||||
i+=9;//skip 8(an uint64_t)+1 forwards
|
||||
tmpdbl[7] = data[i + 1];
|
||||
tmpdbl[6] = data[i + 2];
|
||||
tmpdbl[5] = data[i + 3];
|
||||
tmpdbl[4] = data[i + 4];
|
||||
tmpdbl[3] = data[i + 5];
|
||||
tmpdbl[2] = data[i + 6];
|
||||
tmpdbl[1] = data[i + 7];
|
||||
tmpdbl[0] = data[i + 8];
|
||||
i += 9; //skip 8(an uint64_t)+1 forwards
|
||||
uint64_t * d = (uint64_t*)tmpdbl;
|
||||
return JSON::Value((long long int)*d);
|
||||
} break;
|
||||
case 0x02:{//string
|
||||
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
|
||||
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((long long int) *d);
|
||||
}
|
||||
break;
|
||||
case 0x02: { //string
|
||||
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
|
||||
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);
|
||||
} break;
|
||||
case 0xFF://also object
|
||||
case 0xE0:{//object
|
||||
}
|
||||
break;
|
||||
case 0xFF: //also object
|
||||
case 0xE0: { //object
|
||||
++i;
|
||||
JSON::Value ret;
|
||||
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
|
||||
std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data
|
||||
i += tmpi + 2;//skip length+size forwards
|
||||
ret[tmpstr] = fromDTMI(data, len, i);//add content, recursively parsed, updating i, setting indice to tmpstr
|
||||
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
|
||||
std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data
|
||||
i += tmpi + 2; //skip length+size forwards
|
||||
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;
|
||||
} break;
|
||||
case 0x0A:{//array
|
||||
}
|
||||
break;
|
||||
case 0x0A: { //array
|
||||
JSON::Value ret;
|
||||
++i;
|
||||
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
|
||||
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
|
||||
}
|
||||
i += 3;//skip 0x0000EE
|
||||
i += 3; //skip 0x0000EE
|
||||
return ret;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]);
|
||||
#endif
|
||||
#endif
|
||||
return JSON::Value();
|
||||
}//fromOneDTMI
|
||||
} //fromOneDTMI
|
||||
|
||||
/// Parses a std::string to a valid JSON::Value.
|
||||
/// This function will find one DTMI object in the string and return it.
|
||||
JSON::Value JSON::fromDTMI(std::string data){
|
||||
unsigned int i = 0;
|
||||
return fromDTMI((const unsigned char*)data.c_str(), data.size(), i);
|
||||
}//fromDTMI
|
||||
} //fromDTMI
|
||||
|
|
21
lib/json.h
21
lib/json.h
|
@ -7,19 +7,24 @@
|
|||
#include <istream>
|
||||
|
||||
//empty definition of DTSC::Stream so it can be a friend.
|
||||
namespace DTSC{class Stream;}
|
||||
namespace DTSC {
|
||||
class Stream;
|
||||
}
|
||||
|
||||
/// JSON-related classes and functions
|
||||
namespace JSON{
|
||||
namespace JSON {
|
||||
|
||||
/// 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::deque<Value>::iterator ArrIter;
|
||||
|
||||
|
||||
/// A JSON::Value is either a string or an integer, but may also be an object, array or null.
|
||||
class Value{
|
||||
private:
|
||||
|
@ -34,7 +39,7 @@ namespace JSON{
|
|||
static void skipToEnd(std::istream & fromstream);
|
||||
public:
|
||||
//friends
|
||||
friend class DTSC::Stream;//for access to strVal
|
||||
friend class DTSC::Stream; //for access to strVal
|
||||
//constructors
|
||||
Value();
|
||||
Value(std::istream & fromstream);
|
||||
|
@ -91,5 +96,5 @@ namespace JSON{
|
|||
Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i);
|
||||
Value fromString(std::string json);
|
||||
Value fromFile(std::string filename);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
1962
lib/mp4.cpp
1962
lib/mp4.cpp
File diff suppressed because it is too large
Load diff
289
lib/mp4.h
289
lib/mp4.h
|
@ -9,36 +9,36 @@
|
|||
#include "json.h"
|
||||
|
||||
/// Contains all MP4 format related code.
|
||||
namespace MP4{
|
||||
namespace MP4 {
|
||||
|
||||
class Box {
|
||||
class Box{
|
||||
public:
|
||||
Box(char * datapointer = 0, bool manage = true);
|
||||
~Box();
|
||||
std::string getType();
|
||||
bool isType( char* boxType );
|
||||
bool isType(const char* boxType);
|
||||
bool read(std::string & newData);
|
||||
long long int boxedSize();
|
||||
long long int payloadSize();
|
||||
char * asBox();
|
||||
char * payload();
|
||||
void clear();
|
||||
std::string toPrettyString( int indent = 0 );
|
||||
std::string toPrettyString(int indent = 0);
|
||||
protected:
|
||||
//integer functions
|
||||
void setInt8( char newData, size_t index );
|
||||
char getInt8( size_t index );
|
||||
void setInt16( short newData, size_t index );
|
||||
short getInt16( size_t index );
|
||||
void setInt24( long newData, size_t index );
|
||||
long getInt24( size_t index );
|
||||
void setInt32( long newData, size_t index );
|
||||
long getInt32( size_t index );
|
||||
void setInt64( long long int newData, size_t index );
|
||||
long long int getInt64( size_t index );
|
||||
void setInt8(char newData, size_t index);
|
||||
char getInt8(size_t index);
|
||||
void setInt16(short newData, size_t index);
|
||||
short getInt16(size_t index);
|
||||
void setInt24(long newData, size_t index);
|
||||
long getInt24(size_t index);
|
||||
void setInt32(long newData, size_t index);
|
||||
long getInt32(size_t index);
|
||||
void setInt64(long long int newData, size_t index);
|
||||
long long int getInt64(size_t index);
|
||||
//string functions
|
||||
void setString(std::string newData, size_t index );
|
||||
void setString(char* newData, size_t size, size_t index );
|
||||
void setString(std::string newData, size_t index);
|
||||
void setString(char* newData, size_t size, size_t index);
|
||||
char * getString(size_t index);
|
||||
size_t getStringLen(size_t index);
|
||||
//box functions
|
||||
|
@ -51,59 +51,63 @@ namespace MP4{
|
|||
char * data; ///< Holds the data of this box
|
||||
int data_size; ///< Currently reserved size
|
||||
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
|
||||
};//Box Class
|
||||
int payloadOffset; ///<The offset of the payload with regards to the data
|
||||
};
|
||||
//Box Class
|
||||
|
||||
struct afrt_runtable {
|
||||
long firstFragment;
|
||||
long long int firstTimestamp;
|
||||
long duration;
|
||||
long discontinuity;
|
||||
};//fragmentRun
|
||||
struct afrt_runtable{
|
||||
long firstFragment;
|
||||
long long int firstTimestamp;
|
||||
long duration;
|
||||
long discontinuity;
|
||||
};
|
||||
//fragmentRun
|
||||
|
||||
/// AFRT Box class
|
||||
class AFRT : public Box {
|
||||
class AFRT: public Box{
|
||||
public:
|
||||
AFRT();
|
||||
void setVersion( char newVersion );
|
||||
void setVersion(char newVersion);
|
||||
long getVersion();
|
||||
void setUpdate( long newUpdate );
|
||||
void setUpdate(long newUpdate);
|
||||
long getUpdate();
|
||||
void setTimeScale( long newScale );
|
||||
void setTimeScale(long newScale);
|
||||
long getTimeScale();
|
||||
long getQualityEntryCount();
|
||||
void setQualityEntry( std::string & newQuality, long no );
|
||||
const char * getQualityEntry( long no );
|
||||
void setQualityEntry(std::string & newQuality, long no);
|
||||
const char * getQualityEntry(long no);
|
||||
long getFragmentRunCount();
|
||||
void setFragmentRun( afrt_runtable newRun, long no );
|
||||
afrt_runtable getFragmentRun( long no );
|
||||
void setFragmentRun(afrt_runtable newRun, long no);
|
||||
afrt_runtable getFragmentRun(long no);
|
||||
std::string toPrettyString(int indent = 0);
|
||||
};//AFRT Box
|
||||
};
|
||||
//AFRT Box
|
||||
|
||||
struct asrt_runtable{
|
||||
long firstSegment;
|
||||
long fragmentsPerSegment;
|
||||
long firstSegment;
|
||||
long fragmentsPerSegment;
|
||||
};
|
||||
|
||||
/// ASRT Box class
|
||||
class ASRT : public Box {
|
||||
class ASRT: public Box{
|
||||
public:
|
||||
ASRT();
|
||||
void setVersion( char newVersion );
|
||||
void setVersion(char newVersion);
|
||||
long getVersion();
|
||||
void setUpdate( long newUpdate );
|
||||
void setUpdate(long newUpdate);
|
||||
long getUpdate();
|
||||
long getQualityEntryCount();
|
||||
void setQualityEntry( std::string & newQuality, long no );
|
||||
const char* getQualityEntry( long no );
|
||||
void setQualityEntry(std::string & newQuality, long no);
|
||||
const char* getQualityEntry(long no);
|
||||
long getSegmentRunEntryCount();
|
||||
void setSegmentRun( long firstSegment, long fragmentsPerSegment, long no );
|
||||
asrt_runtable getSegmentRun( long no );
|
||||
void setSegmentRun(long firstSegment, long fragmentsPerSegment, long no);
|
||||
asrt_runtable getSegmentRun(long no);
|
||||
std::string toPrettyString(int indent = 0);
|
||||
};//ASRT Box
|
||||
|
||||
};
|
||||
//ASRT Box
|
||||
|
||||
/// ABST Box class
|
||||
class ABST: public Box {
|
||||
class ABST: public Box{
|
||||
public:
|
||||
ABST();
|
||||
void setVersion(char newVersion);
|
||||
|
@ -143,61 +147,65 @@ namespace MP4{
|
|||
void setFragmentRunTable(AFRT & table, long no);
|
||||
AFRT & getFragmentRunTable(long no);
|
||||
std::string toPrettyString(long indent = 0);
|
||||
};//ABST Box
|
||||
|
||||
class MFHD : public Box {
|
||||
};
|
||||
//ABST Box
|
||||
|
||||
class MFHD: public Box{
|
||||
public:
|
||||
MFHD();
|
||||
void setSequenceNumber( long newSequenceNumber );
|
||||
void setSequenceNumber(long newSequenceNumber);
|
||||
long getSequenceNumber();
|
||||
std::string toPrettyString(int indent = 0);
|
||||
};//MFHD Box
|
||||
|
||||
class MOOF : public Box {
|
||||
};
|
||||
//MFHD Box
|
||||
|
||||
class MOOF: public Box{
|
||||
public:
|
||||
MOOF();
|
||||
long getContentCount();
|
||||
void setContent(Box & newContent, long no);
|
||||
Box & getContent(long no);
|
||||
std::string toPrettyString(int indent = 0);
|
||||
};//MOOF Box
|
||||
|
||||
class TRAF : public Box {
|
||||
};
|
||||
//MOOF Box
|
||||
|
||||
class TRAF: public Box{
|
||||
public:
|
||||
TRAF();
|
||||
long getContentCount();
|
||||
void setContent(Box & newContent, long no);
|
||||
Box & getContent(long no);
|
||||
std::string toPrettyString(int indent = 0);
|
||||
};//TRAF Box
|
||||
|
||||
struct trunSampleInformation {
|
||||
long sampleDuration;
|
||||
long sampleSize;
|
||||
long sampleFlags;
|
||||
long sampleOffset;
|
||||
};
|
||||
enum trunflags {
|
||||
trundataOffset = 0x00000001,
|
||||
//TRAF Box
|
||||
|
||||
struct trunSampleInformation{
|
||||
long sampleDuration;
|
||||
long sampleSize;
|
||||
long sampleFlags;
|
||||
long sampleOffset;
|
||||
};
|
||||
enum trunflags{
|
||||
trundataOffset = 0x00000001,
|
||||
trunfirstSampleFlags = 0x00000004,
|
||||
trunsampleDuration = 0x00000100,
|
||||
trunsampleSize = 0x00000200,
|
||||
trunsampleFlags = 0x00000400,
|
||||
trunsampleOffsets = 0x00000800
|
||||
trunsampleDuration = 0x00000100,
|
||||
trunsampleSize = 0x00000200,
|
||||
trunsampleFlags = 0x00000400,
|
||||
trunsampleOffsets = 0x00000800
|
||||
};
|
||||
enum sampleflags {
|
||||
noIPicture = 0x01000000,
|
||||
isIPicture = 0x02000000,
|
||||
enum sampleflags{
|
||||
noIPicture = 0x01000000,
|
||||
isIPicture = 0x02000000,
|
||||
noDisposable = 0x00400000,
|
||||
isDisposable = 0x00800000,
|
||||
isRedundant = 0x00100000,
|
||||
noRedundant = 0x00200000,
|
||||
noKeySample = 0x00010000,
|
||||
isKeySample = 0x00000000,
|
||||
isRedundant = 0x00100000,
|
||||
noRedundant = 0x00200000,
|
||||
noKeySample = 0x00010000,
|
||||
isKeySample = 0x00000000,
|
||||
MUST_BE_PRESENT = 0x1
|
||||
};
|
||||
std::string prettySampleFlags(long flag);
|
||||
class TRUN : public Box {
|
||||
class TRUN: public Box{
|
||||
public:
|
||||
TRUN();
|
||||
void setFlags(long newFlags);
|
||||
|
@ -212,7 +220,7 @@ namespace MP4{
|
|||
std::string toPrettyString(long indent = 0);
|
||||
};
|
||||
|
||||
enum tfhdflags {
|
||||
enum tfhdflags{
|
||||
tfhdBaseOffset = 0x000001,
|
||||
tfhdSampleDesc = 0x000002,
|
||||
tfhdSampleDura = 0x000008,
|
||||
|
@ -220,7 +228,7 @@ namespace MP4{
|
|||
tfhdSampleFlag = 0x000020,
|
||||
tfhdNoDuration = 0x010000,
|
||||
};
|
||||
class TFHD : public Box {
|
||||
class TFHD: public Box{
|
||||
public:
|
||||
TFHD();
|
||||
void setFlags(long newFlags);
|
||||
|
@ -240,73 +248,74 @@ namespace MP4{
|
|||
std::string toPrettyString(long indent = 0);
|
||||
};
|
||||
|
||||
struct afraentry {
|
||||
long long time;
|
||||
long long offset;
|
||||
struct afraentry{
|
||||
long long time;
|
||||
long long offset;
|
||||
};
|
||||
struct globalafraentry {
|
||||
long long time;
|
||||
long segment;
|
||||
long fragment;
|
||||
long long afraoffset;
|
||||
long long offsetfromafra;
|
||||
struct globalafraentry{
|
||||
long long time;
|
||||
long segment;
|
||||
long fragment;
|
||||
long long afraoffset;
|
||||
long long offsetfromafra;
|
||||
};
|
||||
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 {
|
||||
class AFRA: 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 );
|
||||
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 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:
|
||||
SDTP();
|
||||
void setVersion( long newVersion );
|
||||
long getVersion( );
|
||||
void setValue( long newValue, size_t index );
|
||||
long getValue( size_t index );
|
||||
void setVersion(long newVersion);
|
||||
long getVersion();
|
||||
void setValue(long newValue, size_t index);
|
||||
long getValue(size_t index);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
|
302
lib/procs.cpp
302
lib/procs.cpp
|
@ -25,12 +25,14 @@ bool Util::Procs::handler_set = false;
|
|||
|
||||
/// Used internally to capture child signals and update plist.
|
||||
void Util::Procs::childsig_handler(int signum){
|
||||
if (signum != SIGCHLD){return;}
|
||||
int status;
|
||||
pid_t ret = waitpid(-1, &status, WNOHANG);
|
||||
if (ret == 0){//ignore, would block otherwise
|
||||
if (signum != SIGCHLD){
|
||||
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
|
||||
std::cerr << "SIGCHLD received, but no child died";
|
||||
#endif
|
||||
|
@ -41,27 +43,29 @@ void Util::Procs::childsig_handler(int signum){
|
|||
exitcode = WEXITSTATUS(status);
|
||||
}else if (WIFSIGNALED(status)){
|
||||
exitcode = -WTERMSIG(status);
|
||||
}else{/* not possible */return;}
|
||||
}else{/* not possible */
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::string pname = plist[ret];
|
||||
#endif
|
||||
#endif
|
||||
plist.erase(ret);
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
if (isActive(pname)){
|
||||
Stop(pname);
|
||||
}else{
|
||||
} else{
|
||||
//can this ever happen?
|
||||
std::cerr << "Process " << pname << " fully terminated." << std::endl;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (exitHandlers.count(ret) > 0){
|
||||
TerminationNotifier tn = exitHandlers[ret];
|
||||
exitHandlers.erase(ret);
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
std::cerr << "Calling termination handler for " << pname << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
tn(ret, exitcode);
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +88,14 @@ void Util::Procs::runCmd(std::string & cmd){
|
|||
++i;
|
||||
args[i] = tmp2;
|
||||
}
|
||||
if (i == 20){args[20] = 0;}
|
||||
if (i == 20){
|
||||
args[20] = 0;
|
||||
}
|
||||
//execute the command
|
||||
execvp(args[0], args);
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
_exit(42);
|
||||
}
|
||||
|
||||
|
@ -98,11 +104,13 @@ void Util::Procs::runCmd(std::string & cmd){
|
|||
/// \arg name Name for this process - only used internally.
|
||||
/// \arg cmd Commandline for this process.
|
||||
pid_t Util::Procs::Start(std::string name, std::string cmd){
|
||||
if (isActive(name)){return getPid(name);}
|
||||
if (!handler_set){
|
||||
if (isActive(name)){
|
||||
return getPid(name);
|
||||
}
|
||||
if ( !handler_set){
|
||||
struct sigaction new_action;
|
||||
new_action.sa_handler = Util::Procs::childsig_handler;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
sigemptyset( &new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &new_action, NULL);
|
||||
handler_set = true;
|
||||
|
@ -112,14 +120,14 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){
|
|||
runCmd(cmd);
|
||||
}else{
|
||||
if (ret > 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
plist.insert(std::pair<pid_t, std::string>(ret, name));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
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 cmd2 Commandline for main (receiving) process.
|
||||
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
|
||||
if (isActive(name)){return getPid(name);}
|
||||
if (!handler_set){
|
||||
if (isActive(name)){
|
||||
return getPid(name);
|
||||
}
|
||||
if ( !handler_set){
|
||||
struct sigaction new_action;
|
||||
new_action.sa_handler = Util::Procs::childsig_handler;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
sigemptyset( &new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &new_action, NULL);
|
||||
handler_set = true;
|
||||
}
|
||||
int pfildes[2];
|
||||
if (pipe(pfildes) == -1){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -153,7 +163,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
|
|||
pid_t ret = fork();
|
||||
if (ret == 0){
|
||||
close(pfildes[0]);
|
||||
dup2(pfildes[1],STDOUT_FILENO);
|
||||
dup2(pfildes[1], STDOUT_FILENO);
|
||||
close(pfildes[1]);
|
||||
dup2(devnull, STDIN_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){
|
||||
plist.insert(std::pair<pid_t, std::string>(ret, name));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
close(pfildes[1]);
|
||||
close(pfildes[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();
|
||||
if (ret2 == 0){
|
||||
close(pfildes[1]);
|
||||
dup2(pfildes[0],STDIN_FILENO);
|
||||
dup2(pfildes[0], STDIN_FILENO);
|
||||
close(pfildes[0]);
|
||||
dup2(devnull, STDOUT_FILENO);
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
runCmd(cmd2);
|
||||
}else{
|
||||
if (ret2 > 0){
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl;
|
||||
#endif
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl;
|
||||
#endif
|
||||
plist.insert(std::pair<pid_t, std::string>(ret2, name));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
Stop(name);
|
||||
close(pfildes[1]);
|
||||
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 cmd3 Commandline for main (receiving) process.
|
||||
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){
|
||||
if (isActive(name)){return getPid(name);}
|
||||
if (!handler_set){
|
||||
if (isActive(name)){
|
||||
return getPid(name);
|
||||
}
|
||||
if ( !handler_set){
|
||||
struct sigaction new_action;
|
||||
new_action.sa_handler = Util::Procs::childsig_handler;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
sigemptyset( &new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &new_action, NULL);
|
||||
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 pfildes2[2];
|
||||
if (pipe(pfildes) == -1){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
if (pipe(pfildes2) == -1){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int devnull = open("/dev/null", O_RDWR);
|
||||
pid_t ret = fork();
|
||||
if (ret == 0){
|
||||
close(pfildes[0]);
|
||||
dup2(pfildes[1],STDOUT_FILENO);
|
||||
dup2(pfildes[1], STDOUT_FILENO);
|
||||
close(pfildes[1]);
|
||||
dup2(devnull, STDIN_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){
|
||||
plist.insert(std::pair<pid_t, std::string>(ret, name));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
close(pfildes[1]);
|
||||
close(pfildes[0]);
|
||||
close(pfildes2[1]);
|
||||
|
@ -257,27 +269,27 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pid_t ret2 = fork();
|
||||
if (ret2 == 0){
|
||||
close(pfildes[1]);
|
||||
close(pfildes2[0]);
|
||||
dup2(pfildes[0],STDIN_FILENO);
|
||||
dup2(pfildes[0], STDIN_FILENO);
|
||||
close(pfildes[0]);
|
||||
dup2(pfildes2[1],STDOUT_FILENO);
|
||||
dup2(pfildes2[1], STDOUT_FILENO);
|
||||
close(pfildes2[1]);
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
runCmd(cmd2);
|
||||
}else{
|
||||
if (ret2 > 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
plist.insert(std::pair<pid_t, std::string>(ret2, name));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
Stop(name);
|
||||
close(pfildes[1]);
|
||||
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[0]);
|
||||
close(pfildes2[1]);
|
||||
dup2(pfildes2[0],STDIN_FILENO);
|
||||
dup2(pfildes2[0], STDIN_FILENO);
|
||||
close(pfildes2[0]);
|
||||
dup2(devnull, STDOUT_FILENO);
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
runCmd(cmd3);
|
||||
}else{
|
||||
if (ret3 > 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
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));
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
Stop(name);
|
||||
close(pfildes[1]);
|
||||
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 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 stderr.
|
||||
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;
|
||||
int pipein[2], pipeout[2], pipeerr[2];
|
||||
if (!handler_set){
|
||||
if ( !handler_set){
|
||||
struct sigaction new_action;
|
||||
new_action.sa_handler = Util::Procs::childsig_handler;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
sigemptyset( &new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &new_action, NULL);
|
||||
handler_set = true;
|
||||
}
|
||||
if (fdin && *fdin == -1 && pipe(pipein) < 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Pipe (in) creation failed for " << name << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
if (fdout && *fdout == -1 && pipe(pipeout) < 0) {
|
||||
#if DEBUG >= 1
|
||||
if (fdout && *fdout == -1 && pipe(pipeout) < 0){
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Pipe (out) creation failed for " << name << std::endl;
|
||||
#endif
|
||||
if (*fdin == -1){close(pipein[0]);close(pipein[1]);}
|
||||
#endif
|
||||
if ( *fdin == -1){
|
||||
close(pipein[0]);
|
||||
close(pipein[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (fderr && *fderr == -1 && pipe(pipeerr) < 0) {
|
||||
#if DEBUG >= 1
|
||||
if (fderr && *fderr == -1 && pipe(pipeerr) < 0){
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Pipe (err) creation failed for " << name << std::endl;
|
||||
#endif
|
||||
if (*fdin == -1){close(pipein [0]);close(pipein [1]);}
|
||||
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);}
|
||||
#endif
|
||||
if ( *fdin == -1){
|
||||
close(pipein[0]);
|
||||
close(pipein[1]);
|
||||
}
|
||||
if ( *fdout == -1){
|
||||
close(pipeout[0]);
|
||||
close(pipeout[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int devnull = -1;
|
||||
if (!fdin || !fdout || !fderr){
|
||||
if ( !fdin || !fdout || !fderr){
|
||||
devnull = open("/dev/null", O_RDWR);
|
||||
if (devnull == -1){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl;
|
||||
#endif
|
||||
if (*fdin == -1){close(pipein [0]);close(pipein [1]);}
|
||||
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);}
|
||||
if (*fderr == -1){close(pipeerr[0]);close(pipeerr[1]);}
|
||||
#endif
|
||||
if ( *fdin == -1){
|
||||
close(pipein[0]);
|
||||
close(pipein[1]);
|
||||
}
|
||||
if ( *fdout == -1){
|
||||
close(pipeout[0]);
|
||||
close(pipeout[1]);
|
||||
}
|
||||
if ( *fderr == -1){
|
||||
close(pipeerr[0]);
|
||||
close(pipeerr[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pid = fork();
|
||||
if (pid == 0){//child
|
||||
if (!fdin){
|
||||
if (pid == 0){ //child
|
||||
if ( !fdin){
|
||||
dup2(devnull, STDIN_FILENO);
|
||||
}else if (*fdin == -1){
|
||||
close(pipein[1]);// close unused write end
|
||||
}else if ( *fdin == -1){
|
||||
close(pipein[1]); // close unused write end
|
||||
dup2(pipein[0], STDIN_FILENO);
|
||||
close(pipein[0]);
|
||||
}else if (*fdin != STDIN_FILENO){
|
||||
dup2(*fdin, STDIN_FILENO);
|
||||
close(*fdin);
|
||||
}else if ( *fdin != STDIN_FILENO){
|
||||
dup2( *fdin, STDIN_FILENO);
|
||||
close( *fdin);
|
||||
}
|
||||
if (!fdout){
|
||||
if ( !fdout){
|
||||
dup2(devnull, STDOUT_FILENO);
|
||||
}else if (*fdout == -1){
|
||||
close(pipeout[0]);// close unused read end
|
||||
}else if ( *fdout == -1){
|
||||
close(pipeout[0]); // close unused read end
|
||||
dup2(pipeout[1], STDOUT_FILENO);
|
||||
close(pipeout[1]);
|
||||
}else if (*fdout != STDOUT_FILENO){
|
||||
dup2(*fdout, STDOUT_FILENO);
|
||||
close(*fdout);
|
||||
}else if ( *fdout != STDOUT_FILENO){
|
||||
dup2( *fdout, STDOUT_FILENO);
|
||||
close( *fdout);
|
||||
}
|
||||
if (!fderr){
|
||||
if ( !fderr){
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
}else if (*fderr == -1){
|
||||
close(pipeerr[0]);// close unused read end
|
||||
}else if ( *fderr == -1){
|
||||
close(pipeerr[0]); // close unused read end
|
||||
dup2(pipeerr[1], STDERR_FILENO);
|
||||
close(pipeerr[1]);
|
||||
}else if (*fderr != STDERR_FILENO){
|
||||
dup2(*fderr, STDERR_FILENO);
|
||||
close(*fderr);
|
||||
}else if ( *fderr != STDERR_FILENO){
|
||||
dup2( *fderr, STDERR_FILENO);
|
||||
close( *fderr);
|
||||
}
|
||||
if (devnull != -1){
|
||||
close(devnull);
|
||||
}
|
||||
if (devnull != -1){close(devnull);}
|
||||
execvp(argv[0], argv);
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
perror("execvp failed");
|
||||
#endif
|
||||
#endif
|
||||
exit(42);
|
||||
} else if (pid == -1){
|
||||
#if DEBUG >= 1
|
||||
}else if (pid == -1){
|
||||
#if DEBUG >= 1
|
||||
std::cerr << "Failed to fork for pipe: " << name << std::endl;
|
||||
#endif
|
||||
if (fdin && *fdin == -1){close(pipein [0]);close(pipein [1]);}
|
||||
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);}
|
||||
#endif
|
||||
if (fdin && *fdin == -1){
|
||||
close(pipein[0]);
|
||||
close(pipein[1]);
|
||||
}
|
||||
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;
|
||||
} else{//parent
|
||||
#if DEBUG >= 1
|
||||
}else{ //parent
|
||||
#if DEBUG >= 1
|
||||
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 (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr);
|
||||
if (devnull != -1) std::cerr << " null=" << devnull;
|
||||
std::cerr << ", PID " << pid << ": " << argv[0] << std::endl;
|
||||
#endif
|
||||
if (devnull != -1){close(devnull);}
|
||||
if (fdin && *fdin == -1){
|
||||
close(pipein[0]);// close unused end end
|
||||
#endif
|
||||
if (devnull != -1){
|
||||
close(devnull);
|
||||
}
|
||||
if (fdin && *fdin == -1){
|
||||
close(pipein[0]); // close unused end end
|
||||
*fdin = pipein[1];
|
||||
}
|
||||
if (fdout && *fdout == -1){
|
||||
close(pipeout[1]);// close unused write end
|
||||
close(pipeout[1]); // close unused write end
|
||||
*fdout = pipeout[0];
|
||||
}
|
||||
if (fderr && *fderr == -1){
|
||||
close(pipeerr[1]);// close unused write end
|
||||
close(pipeerr[1]); // close unused write end
|
||||
*fderr = pipeerr[0];
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// Stops the named process, if running.
|
||||
/// \arg name (Internal) name of process to stop
|
||||
void Util::Procs::Stop(std::string name){
|
||||
|
@ -456,7 +502,9 @@ void Util::Procs::Stop(std::string name){
|
|||
while (isActive(name)){
|
||||
Stop(getPid(name));
|
||||
max--;
|
||||
if (max <= 0){return;}
|
||||
if (max <= 0){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,20 +520,22 @@ void Util::Procs::Stop(pid_t name){
|
|||
void Util::Procs::StopAll(){
|
||||
std::map<pid_t, std::string>::iterator it;
|
||||
for (it = plist.begin(); it != plist.end(); it++){
|
||||
Stop((*it).first);
|
||||
Stop(( *it).first);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of active child processes.
|
||||
int Util::Procs::Count(){
|
||||
return plist.size();
|
||||
return plist.size();
|
||||
}
|
||||
|
||||
/// Returns true if a process by this name is currently active.
|
||||
bool Util::Procs::isActive(std::string name){
|
||||
std::map<pid_t, std::string>::iterator it;
|
||||
for (it = plist.begin(); it != plist.end(); it++){
|
||||
if ((*it).second == name){return true;}
|
||||
if (( *it).second == name){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -500,7 +550,9 @@ bool Util::Procs::isActive(pid_t name){
|
|||
pid_t Util::Procs::getPid(std::string name){
|
||||
std::map<pid_t, std::string>::iterator 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;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <map>
|
||||
|
||||
/// Contains utility code, not directly related to streaming media
|
||||
namespace Util{
|
||||
namespace Util {
|
||||
|
||||
typedef void (*TerminationNotifier)(pid_t pid, int exitCode);
|
||||
|
||||
|
@ -35,4 +35,4 @@ namespace Util{
|
|||
static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier);
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
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_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_snd_max = 128;
|
||||
|
@ -39,19 +39,17 @@ std::map<unsigned int, RTMPStream::Chunk> RTMPStream::Chunk::lastrecv;
|
|||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
|
||||
|
||||
uint8_t genuineFMSKey[] = {
|
||||
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, 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,
|
||||
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
|
||||
}; // 68
|
||||
uint8_t genuineFMSKey[] = {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,
|
||||
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, 0x6e, 0xec,
|
||||
0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 68
|
||||
|
||||
uint8_t genuineFPKey[] = {
|
||||
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
|
||||
0x50, 0x6c, 0x61, 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,
|
||||
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
|
||||
}; // 62
|
||||
uint8_t genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
|
||||
0x50, 0x6c, 0x61,
|
||||
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, 0x6e, 0xec,
|
||||
0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 62
|
||||
|
||||
inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){
|
||||
if (scheme == 0){
|
||||
|
@ -69,7 +67,7 @@ inline uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){
|
|||
}
|
||||
}
|
||||
|
||||
class DHWrapper {
|
||||
class DHWrapper{
|
||||
private:
|
||||
int32_t _bitsCount;
|
||||
DH *_pDH;
|
||||
|
@ -89,7 +87,7 @@ class DHWrapper {
|
|||
bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength);
|
||||
};
|
||||
|
||||
DHWrapper::DHWrapper(int32_t bitsCount) {
|
||||
DHWrapper::DHWrapper(int32_t bitsCount){
|
||||
_bitsCount = bitsCount;
|
||||
_pDH = 0;
|
||||
_pSharedKey = 0;
|
||||
|
@ -97,119 +95,172 @@ DHWrapper::DHWrapper(int32_t bitsCount) {
|
|||
_peerPublickey = 0;
|
||||
}
|
||||
|
||||
DHWrapper::~DHWrapper() {
|
||||
DHWrapper::~DHWrapper(){
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
bool DHWrapper::Initialize() {
|
||||
bool DHWrapper::Initialize(){
|
||||
Cleanup();
|
||||
_pDH = DH_new();
|
||||
if (!_pDH){Cleanup(); return false;}
|
||||
if ( !_pDH){
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
_pDH->p = BN_new();
|
||||
if (!_pDH->p){Cleanup(); return false;}
|
||||
if ( !_pDH->p){
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
_pDH->g = BN_new();
|
||||
if (!_pDH->g){Cleanup(); return false;}
|
||||
if (BN_hex2bn(&_pDH->p, P1024) == 0){Cleanup(); return false;}
|
||||
if (BN_set_word(_pDH->g, 2) != 1){Cleanup(); return false;}
|
||||
if ( !_pDH->g){
|
||||
Cleanup();
|
||||
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;
|
||||
if (DH_generate_key(_pDH) != 1){Cleanup(); return false;}
|
||||
if (DH_generate_key(_pDH) != 1){
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) {
|
||||
if (!_pDH){return false;}
|
||||
bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength){
|
||||
if ( !_pDH){
|
||||
return false;
|
||||
}
|
||||
return CopyKey(_pDH->pub_key, pDst, dstLength);
|
||||
}
|
||||
|
||||
bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) {
|
||||
if (!_pDH){return false;}
|
||||
bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength){
|
||||
if ( !_pDH){
|
||||
return false;
|
||||
}
|
||||
return CopyKey(_pDH->priv_key, pDst, dstLength);
|
||||
}
|
||||
|
||||
bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) {
|
||||
if (!_pDH){return false;}
|
||||
if (_sharedKeyLength != 0 || _pSharedKey){return false;}
|
||||
|
||||
bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length){
|
||||
if ( !_pDH){
|
||||
return false;
|
||||
}
|
||||
if (_sharedKeyLength != 0 || _pSharedKey){
|
||||
return false;
|
||||
}
|
||||
|
||||
_sharedKeyLength = DH_size(_pDH);
|
||||
if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){return false;}
|
||||
|
||||
if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){
|
||||
return false;
|
||||
}
|
||||
|
||||
_pSharedKey = new uint8_t[_sharedKeyLength];
|
||||
_peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0);
|
||||
if (!_peerPublickey){return false;}
|
||||
|
||||
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){return false;}
|
||||
|
||||
if ( !_peerPublickey){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) {
|
||||
if (!_pDH){return false;}
|
||||
if (dstLength != _sharedKeyLength){return false;}
|
||||
bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength){
|
||||
if ( !_pDH){
|
||||
return false;
|
||||
}
|
||||
if (dstLength != _sharedKeyLength){
|
||||
return false;
|
||||
}
|
||||
memcpy(pDst, _pSharedKey, _sharedKeyLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DHWrapper::Cleanup() {
|
||||
void DHWrapper::Cleanup(){
|
||||
if (_pDH){
|
||||
if (_pDH->p){BN_free(_pDH->p); _pDH->p = 0;}
|
||||
if (_pDH->g){BN_free(_pDH->g); _pDH->g = 0;}
|
||||
DH_free(_pDH); _pDH = 0;
|
||||
if (_pDH->p){
|
||||
BN_free(_pDH->p);
|
||||
_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;
|
||||
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);
|
||||
if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){return false;}
|
||||
if (BN_bn2bin(pNum, pDst) != keySize){return false;}
|
||||
if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){
|
||||
return false;
|
||||
}
|
||||
if (BN_bn2bin(pNum, pDst) != keySize){
|
||||
return false;
|
||||
}
|
||||
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];
|
||||
unsigned int digestLen = 0;
|
||||
|
||||
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
|
||||
HMAC_Update(&ctx, pubKeyIn, 128);
|
||||
HMAC_Final(&ctx, digest, &digestLen);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
HMAC_CTX_init( &ctx);
|
||||
HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0);
|
||||
HMAC_Update( &ctx, pubKeyIn, 128);
|
||||
HMAC_Final( &ctx, digest, &digestLen);
|
||||
HMAC_CTX_cleanup( &ctx);
|
||||
|
||||
RC4_set_key(rc4keyOut, 16, digest);
|
||||
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
|
||||
HMAC_Update(&ctx, pubKeyOut, 128);
|
||||
HMAC_Final(&ctx, digest, &digestLen);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
|
||||
HMAC_CTX_init( &ctx);
|
||||
HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0);
|
||||
HMAC_Update( &ctx, pubKeyOut, 128);
|
||||
HMAC_Final( &ctx, digest, &digestLen);
|
||||
HMAC_CTX_cleanup( &ctx);
|
||||
|
||||
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;
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), 0);
|
||||
HMAC_Update(&ctx, (unsigned char *) pData, dataLength);
|
||||
HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
HMAC_CTX_init( &ctx);
|
||||
HMAC_Init_ex( &ctx, (unsigned char*)pKey, keyLength, EVP_sha256(), 0);
|
||||
HMAC_Update( &ctx, (unsigned char *)pData, dataLength);
|
||||
HMAC_Final( &ctx, (unsigned char *)pResult, &digestLen);
|
||||
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);
|
||||
uint8_t *pTempBuffer = new uint8_t[1536 - 32];
|
||||
memcpy(pTempBuffer, pBuffer, clientDigestOffset);
|
||||
memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32);
|
||||
uint8_t *pTempHash = new uint8_t[512];
|
||||
HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
|
||||
bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0);
|
||||
#if DEBUG >= 4
|
||||
bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0);
|
||||
#if DEBUG >= 4
|
||||
fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed");
|
||||
#endif
|
||||
#endif
|
||||
delete[] pTempBuffer;
|
||||
delete[] pTempHash;
|
||||
return result;
|
||||
|
@ -226,23 +277,25 @@ std::string & RTMPStream::Chunk::Pack(){
|
|||
unsigned char chtype = 0x00;
|
||||
if ((prev.msg_type_id > 0) && (prev.cs_id == cs_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 (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){
|
||||
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
|
||||
if (timestamp < prev.timestamp){chtype = 0x00;}
|
||||
if (timestamp < prev.timestamp){
|
||||
chtype = 0x00;
|
||||
}
|
||||
}
|
||||
if (cs_id <= 63){
|
||||
output += (unsigned char)(chtype | cs_id);
|
||||
}else{
|
||||
if (cs_id <= 255+64){
|
||||
if (cs_id <= 255 + 64){
|
||||
output += (unsigned char)(chtype | 0);
|
||||
output += (unsigned char)(cs_id - 64);
|
||||
}else{
|
||||
|
@ -259,7 +312,10 @@ std::string & RTMPStream::Chunk::Pack(){
|
|||
}else{
|
||||
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 >> 8) & 0xff);
|
||||
output += (unsigned char)(tmpi & 0xff);
|
||||
|
@ -275,8 +331,8 @@ std::string & RTMPStream::Chunk::Pack(){
|
|||
//msg stream id
|
||||
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*256));
|
||||
output += (unsigned char)(msg_stream_id / (256 * 256));
|
||||
output += (unsigned char)(msg_stream_id / (256 * 256 * 256));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,14 +346,16 @@ std::string & RTMPStream::Chunk::Pack(){
|
|||
len_left = 0;
|
||||
while (len_left < len){
|
||||
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);
|
||||
len_left += tmpi;
|
||||
if (len_left < len){
|
||||
if (cs_id <= 63){
|
||||
output += (unsigned char)(0xC0 + cs_id);
|
||||
}else{
|
||||
if (cs_id <= 255+64){
|
||||
if (cs_id <= 255 + 64){
|
||||
output += (unsigned char)(0xC0);
|
||||
output += (unsigned char)(cs_id - 64);
|
||||
}else{
|
||||
|
@ -311,10 +369,11 @@ std::string & RTMPStream::Chunk::Pack(){
|
|||
lastsend[cs_id] = *this;
|
||||
RTMPStream::snd_cnt += output.size();
|
||||
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(){
|
||||
headertype = 0;
|
||||
cs_id = 0;
|
||||
timestamp = 0;
|
||||
len = 0;
|
||||
|
@ -323,7 +382,7 @@ RTMPStream::Chunk::Chunk(){
|
|||
msg_type_id = 0;
|
||||
msg_stream_id = 0;
|
||||
data = "";
|
||||
}//constructor
|
||||
} //constructor
|
||||
|
||||
/// 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){
|
||||
|
@ -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.data = data;
|
||||
return ch.Pack();
|
||||
}//constructor
|
||||
} //constructor
|
||||
|
||||
/// Packs up a chunk with media contents.
|
||||
/// \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.
|
||||
std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){
|
||||
static RTMPStream::Chunk ch;
|
||||
ch.cs_id = msg_type_id+42;
|
||||
ch.cs_id = msg_type_id + 42;
|
||||
ch.timestamp = ts;
|
||||
ch.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.data = std::string((char*)data, (size_t)len);
|
||||
return ch.Pack();
|
||||
}//SendMedia
|
||||
} //SendMedia
|
||||
|
||||
/// Packs up a chunk with media contents.
|
||||
/// \param tag FLV::Tag with media to send.
|
||||
|
@ -363,14 +422,14 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag){
|
|||
static RTMPStream::Chunk ch;
|
||||
ch.cs_id = ((unsigned char)tag.data[0]);
|
||||
ch.timestamp = tag.tagTime();
|
||||
ch.len = tag.len-15;
|
||||
ch.real_len = tag.len-15;
|
||||
ch.len = tag.len - 15;
|
||||
ch.real_len = tag.len - 15;
|
||||
ch.len_left = 0;
|
||||
ch.msg_type_id = (unsigned char)tag.data[0];
|
||||
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();
|
||||
}//SendMedia
|
||||
} //SendMedia
|
||||
|
||||
/// Packs up a chunk for a control message with 1 argument.
|
||||
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);
|
||||
*(int*)((char*)ch.data.c_str()) = htonl(data);
|
||||
return ch.Pack();
|
||||
}//SendCTL
|
||||
} //SendCTL
|
||||
|
||||
/// Packs up a chunk for a control message with 2 arguments.
|
||||
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);
|
||||
ch.data[4] = data2;
|
||||
return ch.Pack();
|
||||
}//SendCTL
|
||||
} //SendCTL
|
||||
|
||||
/// Packs up a chunk for a user control message with 1 argument.
|
||||
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_stream_id = 0;
|
||||
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[1] = type;
|
||||
return ch.Pack();
|
||||
}//SendUSR
|
||||
} //SendUSR
|
||||
|
||||
/// 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){
|
||||
|
@ -431,13 +490,12 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne
|
|||
ch.msg_type_id = 4;
|
||||
ch.msg_stream_id = 0;
|
||||
ch.data.resize(10);
|
||||
*(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()) + 2) = htonl(data);
|
||||
*(unsigned int*)(((char*)ch.data.c_str()) + 6) = htonl(data2);
|
||||
ch.data[0] = 0;
|
||||
ch.data[1] = type;
|
||||
return ch.Pack();
|
||||
}//SendUSR
|
||||
|
||||
} //SendUSR
|
||||
|
||||
/// Parses the argument string into the current chunk.
|
||||
/// 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!
|
||||
/// \returns True if a whole chunk could be read, false otherwise.
|
||||
bool RTMPStream::Chunk::Parse(std::string & indata){
|
||||
gettimeofday(&RTMPStream::lastrec, 0);
|
||||
gettimeofday( &RTMPStream::lastrec, 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
|
||||
switch (chunktype & 0x3F){
|
||||
case 0:
|
||||
if (indata.size() < 2) return false;//need at least 2 bytes to continue
|
||||
cs_id = indata[i++] + 64;
|
||||
if (indata.size() < 2) return false; //need at least 2 bytes to continue
|
||||
cs_id = indata[i++ ] + 64;
|
||||
break;
|
||||
case 1:
|
||||
if (indata.size() < 3) return false;//need at least 3 bytes to continue
|
||||
cs_id = indata[i++] + 64;
|
||||
cs_id += indata[i++] * 256;
|
||||
if (indata.size() < 3) return false; //need at least 3 bytes to continue
|
||||
cs_id = indata[i++ ] + 64;
|
||||
cs_id += indata[i++ ] * 256;
|
||||
break;
|
||||
default:
|
||||
cs_id = chunktype & 0x3F;
|
||||
|
@ -475,48 +533,58 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
|
|||
headertype = chunktype & 0xC0;
|
||||
switch (headertype){
|
||||
case 0x00:
|
||||
if (indata.size() < i+11) return false; //can't read whole header
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
len = indata[i++]*256*256;
|
||||
len += indata[i++]*256;
|
||||
len += indata[i++];
|
||||
if (indata.size() < i + 11) return false; //can't read whole header
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
len = indata[i++ ] * 256 * 256;
|
||||
len += indata[i++ ] * 256;
|
||||
len += indata[i++ ];
|
||||
len_left = 0;
|
||||
msg_type_id = indata[i++];
|
||||
msg_stream_id = indata[i++];
|
||||
msg_stream_id += indata[i++]*256;
|
||||
msg_stream_id += indata[i++]*256*256;
|
||||
msg_stream_id += indata[i++]*256*256*256;
|
||||
msg_type_id = indata[i++ ];
|
||||
msg_stream_id = indata[i++ ];
|
||||
msg_stream_id += indata[i++ ] * 256;
|
||||
msg_stream_id += indata[i++ ] * 256 * 256;
|
||||
msg_stream_id += indata[i++ ] * 256 * 256 * 256;
|
||||
break;
|
||||
case 0x40:
|
||||
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");}
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;}
|
||||
len = indata[i++]*256*256;
|
||||
len += indata[i++]*256;
|
||||
len += indata[i++];
|
||||
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");
|
||||
}
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
if (timestamp != 0x00ffffff){
|
||||
timestamp += prev.timestamp;
|
||||
}
|
||||
len = indata[i++ ] * 256 * 256;
|
||||
len += indata[i++ ] * 256;
|
||||
len += indata[i++ ];
|
||||
len_left = 0;
|
||||
msg_type_id = indata[i++];
|
||||
msg_type_id = indata[i++ ];
|
||||
msg_stream_id = prev.msg_stream_id;
|
||||
break;
|
||||
case 0x80:
|
||||
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");}
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;}
|
||||
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");
|
||||
}
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
if (timestamp != 0x00ffffff){
|
||||
timestamp += prev.timestamp;
|
||||
}
|
||||
len = prev.len;
|
||||
len_left = prev.len_left;
|
||||
msg_type_id = prev.msg_type_id;
|
||||
msg_stream_id = prev.msg_stream_id;
|
||||
break;
|
||||
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;
|
||||
len = prev.len;
|
||||
len_left = prev.len_left;
|
||||
|
@ -537,11 +605,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
|
|||
}
|
||||
//read extended timestamp, if neccesary
|
||||
if (timestamp == 0x00ffffff){
|
||||
if (indata.size() < i+4) return false; //can't read whole header
|
||||
timestamp = indata[i++]*256*256*256;
|
||||
timestamp += indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if (indata.size() < i + 4) return false; //can't read whole header
|
||||
timestamp = indata[i++ ] * 256 * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
}
|
||||
|
||||
//read data if length > 0, and allocate it
|
||||
|
@ -551,11 +619,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
|
|||
}else{
|
||||
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);
|
||||
indata = indata.substr(i+real_len);
|
||||
indata = indata.substr(i + real_len);
|
||||
lastrecv[cs_id] = *this;
|
||||
RTMPStream::rec_cnt += i+real_len;
|
||||
RTMPStream::rec_cnt += i + real_len;
|
||||
if (len_left == 0){
|
||||
return true;
|
||||
}else{
|
||||
|
@ -563,12 +631,12 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
|
|||
}
|
||||
}else{
|
||||
data = "";
|
||||
indata = indata.substr(i+real_len);
|
||||
indata = indata.substr(i + real_len);
|
||||
lastrecv[cs_id] = *this;
|
||||
RTMPStream::rec_cnt += i+real_len;
|
||||
RTMPStream::rec_cnt += i + real_len;
|
||||
return true;
|
||||
}
|
||||
}//Parse
|
||||
} //Parse
|
||||
|
||||
/// Parses the argument string into the current chunk.
|
||||
/// 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!
|
||||
/// \returns True if a whole chunk could be read, false otherwise.
|
||||
bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
|
||||
gettimeofday(&RTMPStream::lastrec, 0);
|
||||
gettimeofday( &RTMPStream::lastrec, 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);
|
||||
|
||||
unsigned char chunktype = indata[i++];
|
||||
|
||||
unsigned char chunktype = indata[i++ ];
|
||||
//read the chunkstream ID properly
|
||||
switch (chunktype & 0x3F){
|
||||
case 0:
|
||||
cs_id = indata[i++] + 64;
|
||||
cs_id = indata[i++ ] + 64;
|
||||
break;
|
||||
case 1:
|
||||
cs_id = indata[i++] + 64;
|
||||
cs_id += indata[i++] * 256;
|
||||
cs_id = indata[i++ ] + 64;
|
||||
cs_id += indata[i++ ] * 256;
|
||||
break;
|
||||
default:
|
||||
cs_id = chunktype & 0x3F;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RTMPStream::Chunk prev = lastrecv[cs_id];
|
||||
|
||||
|
||||
//process the rest of the header, for each chunk type
|
||||
headertype = chunktype & 0xC0;
|
||||
switch (headertype){
|
||||
case 0x00:
|
||||
if (!buffer.available(i+11)){return false;} //can't read whole header
|
||||
indata = buffer.copy(i+11);
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
len = indata[i++]*256*256;
|
||||
len += indata[i++]*256;
|
||||
len += indata[i++];
|
||||
if ( !buffer.available(i + 11)){
|
||||
return false;
|
||||
} //can't read whole header
|
||||
indata = buffer.copy(i + 11);
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
len = indata[i++ ] * 256 * 256;
|
||||
len += indata[i++ ] * 256;
|
||||
len += indata[i++ ];
|
||||
len_left = 0;
|
||||
msg_type_id = indata[i++];
|
||||
msg_stream_id = indata[i++];
|
||||
msg_stream_id += indata[i++]*256;
|
||||
msg_stream_id += indata[i++]*256*256;
|
||||
msg_stream_id += indata[i++]*256*256*256;
|
||||
msg_type_id = indata[i++ ];
|
||||
msg_stream_id = indata[i++ ];
|
||||
msg_stream_id += indata[i++ ] * 256;
|
||||
msg_stream_id += indata[i++ ] * 256 * 256;
|
||||
msg_stream_id += indata[i++ ] * 256 * 256 * 256;
|
||||
break;
|
||||
case 0x40:
|
||||
if (!buffer.available(i+7)){return false;} //can't read whole header
|
||||
indata = buffer.copy(i+7);
|
||||
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");}
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;}
|
||||
len = indata[i++]*256*256;
|
||||
len += indata[i++]*256;
|
||||
len += indata[i++];
|
||||
if ( !buffer.available(i + 7)){
|
||||
return false;
|
||||
} //can't read whole header
|
||||
indata = buffer.copy(i + 7);
|
||||
if (prev.msg_type_id == 0){
|
||||
fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");
|
||||
}
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
if (timestamp != 0x00ffffff){
|
||||
timestamp += prev.timestamp;
|
||||
}
|
||||
len = indata[i++ ] * 256 * 256;
|
||||
len += indata[i++ ] * 256;
|
||||
len += indata[i++ ];
|
||||
len_left = 0;
|
||||
msg_type_id = indata[i++];
|
||||
msg_type_id = indata[i++ ];
|
||||
msg_stream_id = prev.msg_stream_id;
|
||||
break;
|
||||
case 0x80:
|
||||
if (!buffer.available(i+3)){return false;} //can't read whole header
|
||||
indata = buffer.copy(i+3);
|
||||
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");}
|
||||
timestamp = indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;}
|
||||
if ( !buffer.available(i + 3)){
|
||||
return false;
|
||||
} //can't read whole header
|
||||
indata = buffer.copy(i + 3);
|
||||
if (prev.msg_type_id == 0){
|
||||
fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");
|
||||
}
|
||||
timestamp = indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
if (timestamp != 0x00ffffff){
|
||||
timestamp += prev.timestamp;
|
||||
}
|
||||
len = prev.len;
|
||||
len_left = prev.len_left;
|
||||
msg_type_id = prev.msg_type_id;
|
||||
msg_stream_id = prev.msg_stream_id;
|
||||
break;
|
||||
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;
|
||||
len = prev.len;
|
||||
len_left = prev.len_left;
|
||||
|
@ -670,39 +756,43 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
|
|||
}
|
||||
//read extended timestamp, if neccesary
|
||||
if (timestamp == 0x00ffffff){
|
||||
if (!buffer.available(i+4)){return false;} //can't read timestamp
|
||||
indata = buffer.copy(i+4);
|
||||
timestamp = indata[i++]*256*256*256;
|
||||
timestamp += indata[i++]*256*256;
|
||||
timestamp += indata[i++]*256;
|
||||
timestamp += indata[i++];
|
||||
if ( !buffer.available(i + 4)){
|
||||
return false;
|
||||
} //can't read timestamp
|
||||
indata = buffer.copy(i + 4);
|
||||
timestamp = indata[i++ ] * 256 * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256 * 256;
|
||||
timestamp += indata[i++ ] * 256;
|
||||
timestamp += indata[i++ ];
|
||||
}
|
||||
|
||||
|
||||
//read data if length > 0, and allocate it
|
||||
if (real_len > 0){
|
||||
if (!buffer.available(i+real_len)){return false;}//can't read all data (yet)
|
||||
buffer.remove(i);//remove the header
|
||||
if ( !buffer.available(i + real_len)){
|
||||
return false;
|
||||
} //can't read all data (yet)
|
||||
buffer.remove(i); //remove the header
|
||||
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{
|
||||
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;
|
||||
RTMPStream::rec_cnt += i+real_len;
|
||||
RTMPStream::rec_cnt += i + real_len;
|
||||
if (len_left == 0){
|
||||
return true;
|
||||
}else{
|
||||
return Parse(buffer);
|
||||
}
|
||||
}else{
|
||||
buffer.remove(i);//remove the header
|
||||
buffer.remove(i); //remove the header
|
||||
data = "";
|
||||
indata = indata.substr(i+real_len);
|
||||
indata = indata.substr(i + real_len);
|
||||
lastrecv[cs_id] = *this;
|
||||
RTMPStream::rec_cnt += i+real_len;
|
||||
RTMPStream::rec_cnt += i + real_len;
|
||||
return true;
|
||||
}
|
||||
}//Parse
|
||||
} //Parse
|
||||
|
||||
/// 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,
|
||||
|
@ -718,21 +808,23 @@ bool RTMPStream::doHandshake(){
|
|||
RTMPStream::rec_cnt += 1537;
|
||||
|
||||
//Build S1 Packet
|
||||
*((uint32_t*)Server) = 0;//time zero
|
||||
*(((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
|
||||
*((uint32_t*)Server) = 0; //time zero
|
||||
*(((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
|
||||
|
||||
bool encrypted = (Version == 6);
|
||||
#if DEBUG >= 4
|
||||
#if DEBUG >= 4
|
||||
fprintf(stderr, "Handshake version is %hhi\n", Version);
|
||||
#endif
|
||||
#endif
|
||||
uint8_t _validationScheme = 5;
|
||||
if (ValidateClientScheme(Client, 0)) _validationScheme = 0;
|
||||
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");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//FIRST 1536 bytes from server response
|
||||
//compute DH key position
|
||||
|
@ -741,19 +833,19 @@ bool RTMPStream::doHandshake(){
|
|||
|
||||
//generate DH key
|
||||
DHWrapper dhWrapper(1024);
|
||||
if (!dhWrapper.Initialize()) return false;
|
||||
if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false;
|
||||
if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false;
|
||||
if ( !dhWrapper.Initialize()) return false;
|
||||
if ( !dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false;
|
||||
if ( !dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false;
|
||||
|
||||
if (encrypted) {
|
||||
if (encrypted){
|
||||
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 _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];
|
||||
RC4(&_pKeyIn, 1536, data, data);
|
||||
RC4(&_pKeyOut, 1536, data, data);
|
||||
RC4( &_pKeyIn, 1536, data, data);
|
||||
RC4( &_pKeyOut, 1536, data, data);
|
||||
}
|
||||
//generate the digest
|
||||
uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme);
|
||||
|
@ -776,7 +868,7 @@ bool RTMPStream::doHandshake(){
|
|||
delete[] pTempHash;
|
||||
delete[] pLastHash;
|
||||
//DONE BUILDING THE RESPONSE ***//
|
||||
Server[-1] = Version;
|
||||
Server[ -1] = Version;
|
||||
RTMPStream::snd_cnt += 3073;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
#include "socket.h"
|
||||
|
||||
//forward declaration of FLV::Tag to avoid circular dependencies.
|
||||
namespace FLV{
|
||||
namespace FLV {
|
||||
class Tag;
|
||||
};
|
||||
}
|
||||
|
||||
/// 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_snd_max; ///< Maximum size for a sent chunk.
|
||||
|
@ -50,7 +50,8 @@ namespace RTMPStream{
|
|||
private:
|
||||
static std::map<unsigned int, Chunk> lastsend;
|
||||
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 & 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;
|
||||
/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out.
|
||||
bool doHandshake();
|
||||
};//RTMPStream namespace
|
||||
} //RTMPStream namespace
|
||||
|
|
295
lib/socket.cpp
295
lib/socket.cpp
|
@ -16,7 +16,6 @@
|
|||
#define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB
|
||||
#include <iostream>//temporary for debugging
|
||||
|
||||
|
||||
std::string uint2string(unsigned int i){
|
||||
std::stringstream st;
|
||||
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
|
||||
/// guaranteed to return 0 if the buffer is empty.
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -45,10 +46,12 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
|
|||
j = i;
|
||||
while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){
|
||||
j++;
|
||||
if (newdata[j-1] == '\n'){break;}
|
||||
if (newdata[j - 1] == '\n'){
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}else{
|
||||
break;
|
||||
|
@ -63,8 +66,10 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
|
|||
bool Socket::Buffer::available(unsigned int count){
|
||||
unsigned int i = 0;
|
||||
for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){
|
||||
i += (*it).size();
|
||||
if (i >= count){return true;}
|
||||
i += ( *it).size();
|
||||
if (i >= count){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -72,18 +77,20 @@ bool Socket::Buffer::available(unsigned int count){
|
|||
/// Removes count bytes from the buffer, returning them by value.
|
||||
/// Returns an empty string if not all count bytes are available.
|
||||
std::string Socket::Buffer::remove(unsigned int count){
|
||||
if (!available(count)){return "";}
|
||||
if ( !available(count)){
|
||||
return "";
|
||||
}
|
||||
unsigned int i = 0;
|
||||
std::string ret;
|
||||
ret.reserve(count);
|
||||
for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){
|
||||
if (i + (*it).size() < count){
|
||||
ret.append(*it);
|
||||
i += (*it).size();
|
||||
(*it).clear();
|
||||
if (i + ( *it).size() < count){
|
||||
ret.append( *it);
|
||||
i += ( *it).size();
|
||||
( *it).clear();
|
||||
}else{
|
||||
ret.append(*it, 0, count - i);
|
||||
(*it).erase(0, count - i);
|
||||
ret.append( *it, 0, count - i);
|
||||
( *it).erase(0, count - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -93,15 +100,17 @@ std::string Socket::Buffer::remove(unsigned int count){
|
|||
/// Copies count bytes from the buffer, returning them by value.
|
||||
/// Returns an empty string if not all count bytes are available.
|
||||
std::string Socket::Buffer::copy(unsigned int count){
|
||||
if (!available(count)){return "";}
|
||||
if ( !available(count)){
|
||||
return "";
|
||||
}
|
||||
unsigned int i = 0;
|
||||
std::string ret;
|
||||
for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){
|
||||
if (i + (*it).size() < count){
|
||||
ret.append(*it);
|
||||
i += (*it).size();
|
||||
if (i + ( *it).size() < count){
|
||||
ret.append( *it);
|
||||
i += ( *it).size();
|
||||
}else{
|
||||
ret.append(*it, 0, count - i);
|
||||
ret.append( *it, 0, count - i);
|
||||
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.
|
||||
/// \param sockNo Integer representing the socket to convert.
|
||||
Socket::Connection::Connection(int sockNo){
|
||||
|
@ -130,7 +138,7 @@ Socket::Connection::Connection(int sockNo){
|
|||
conntime = Util::epoch();
|
||||
Error = false;
|
||||
Blocking = false;
|
||||
}//Socket::Connection basic constructor
|
||||
} //Socket::Connection basic constructor
|
||||
|
||||
/// Simulate a socket using two file descriptors.
|
||||
/// \param write The filedescriptor to write to.
|
||||
|
@ -144,7 +152,7 @@ Socket::Connection::Connection(int write, int read){
|
|||
conntime = Util::epoch();
|
||||
Error = 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.
|
||||
/// 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();
|
||||
Error = false;
|
||||
Blocking = false;
|
||||
}//Socket::Connection basic constructor
|
||||
} //Socket::Connection basic constructor
|
||||
|
||||
/// Internally used call to make an file descriptor blocking or not.
|
||||
void setFDBlocking(int FD, bool blocking){
|
||||
int flags = fcntl(FD, F_GETFL, 0);
|
||||
if (!blocking){
|
||||
if ( !blocking){
|
||||
flags |= O_NONBLOCK;
|
||||
}else{
|
||||
flags &= !O_NONBLOCK;
|
||||
|
@ -172,44 +180,57 @@ void setFDBlocking(int FD, bool blocking){
|
|||
|
||||
/// Set this socket to be blocking (true) or nonblocking (false).
|
||||
void Socket::Connection::setBlocking(bool blocking){
|
||||
if (sock >=0){setFDBlocking(sock, blocking);}
|
||||
if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);}
|
||||
if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);}
|
||||
if (sock >= 0){
|
||||
setFDBlocking(sock, 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.
|
||||
/// If the connection is already closed, nothing happens.
|
||||
void Socket::Connection::close(){
|
||||
if (connected()){
|
||||
#if DEBUG >= 6
|
||||
#if DEBUG >= 6
|
||||
fprintf(stderr, "Socket closed.\n");
|
||||
#endif
|
||||
#endif
|
||||
if (sock != -1){
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
errno = EINTR;
|
||||
while (::close(sock) != 0 && errno == EINTR){}
|
||||
while (::close(sock) != 0 && errno == EINTR){
|
||||
}
|
||||
sock = -1;
|
||||
}
|
||||
if (pipes[0] != -1){
|
||||
errno = EINTR;
|
||||
while (::close(pipes[0]) != 0 && errno == EINTR){}
|
||||
while (::close(pipes[0]) != 0 && errno == EINTR){
|
||||
}
|
||||
pipes[0] = -1;
|
||||
}
|
||||
if (pipes[1] != -1){
|
||||
errno = EINTR;
|
||||
while (::close(pipes[1]) != 0 && errno == EINTR){}
|
||||
while (::close(pipes[1]) != 0 && errno == EINTR){
|
||||
}
|
||||
pipes[1] = -1;
|
||||
}
|
||||
}
|
||||
}//Socket::Connection::close
|
||||
} //Socket::Connection::close
|
||||
|
||||
/// 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.
|
||||
/// Simply calls strerror(errno) - not very reliable!
|
||||
/// \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.
|
||||
/// \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;
|
||||
sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
Error = false;
|
||||
|
@ -231,8 +252,8 @@ Socket::Connection::Connection(std::string address, bool nonblock){
|
|||
conntime = Util::epoch();
|
||||
sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, address.c_str(), address.size()+1);
|
||||
int r = connect(sock, (sockaddr*)&addr, sizeof(addr));
|
||||
strncpy(addr.sun_path, address.c_str(), address.size() + 1);
|
||||
int r = connect(sock, (sockaddr*) &addr, sizeof(addr));
|
||||
if (r == 0){
|
||||
if (nonblock){
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
|
@ -240,12 +261,12 @@ Socket::Connection::Connection(std::string address, bool nonblock){
|
|||
fcntl(sock, F_SETFL, flags);
|
||||
}
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
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.
|
||||
/// \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;
|
||||
ss << port;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
memset( &hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
|
@ -273,25 +294,29 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
|
|||
hints.ai_next = NULL;
|
||||
int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result);
|
||||
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));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
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);
|
||||
if (sock < 0){continue;}
|
||||
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;}
|
||||
if (sock < 0){
|
||||
continue;
|
||||
}
|
||||
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){
|
||||
break;
|
||||
}
|
||||
::close(sock);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (rp == 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
}else{
|
||||
if (nonblock){
|
||||
|
@ -300,7 +325,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
|
|||
fcntl(sock, F_SETFL, flags);
|
||||
}
|
||||
}
|
||||
}//Socket::Connection TCP Contructor
|
||||
} //Socket::Connection TCP Contructor
|
||||
|
||||
/// Returns the connected-state for this socket.
|
||||
/// 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.
|
||||
bool Socket::Connection::flush(){
|
||||
while (upbuffer.size() > 0 && connected()){
|
||||
if (!iwrite(upbuffer.get())){
|
||||
Util::sleep(10);//sleep 10ms
|
||||
if ( !iwrite(upbuffer.get())){
|
||||
Util::sleep(10); //sleep 10ms
|
||||
}
|
||||
}
|
||||
/// \todo Provide better mechanism to prevent overbuffering.
|
||||
|
@ -357,7 +382,6 @@ bool Socket::Connection::flush(){
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns a reference to the download buffer.
|
||||
Socket::Buffer & Socket::Connection::Received(){
|
||||
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.
|
||||
void Socket::Connection::SendNow(const char * data, size_t len){
|
||||
while (upbuffer.size() > 0 && connected()){
|
||||
if (!iwrite(upbuffer.get())){
|
||||
Util::sleep(1);//sleep 1ms if buffer full
|
||||
if ( !iwrite(upbuffer.get())){
|
||||
Util::sleep(1); //sleep 1ms if buffer full
|
||||
}
|
||||
}
|
||||
int i = iwrite(data, len);
|
||||
while (i < len && connected()){
|
||||
int j = iwrite(data+i, len-i);
|
||||
int j = iwrite(data + i, len - i);
|
||||
if (j > 0){
|
||||
i += j;
|
||||
}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.
|
||||
void Socket::Connection::Send(const char * data, size_t len){
|
||||
while (upbuffer.size() > 0){
|
||||
if (!iwrite(upbuffer.get())){break;}
|
||||
if ( !iwrite(upbuffer.get())){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (upbuffer.size() > 0){
|
||||
upbuffer.append(data, len);
|
||||
|
@ -442,7 +468,9 @@ void Socket::Connection::Send(std::string & data){
|
|||
/// \param len Amount of bytes to write.
|
||||
/// \returns The amount of bytes actually written.
|
||||
int Socket::Connection::iwrite(const void * buffer, int len){
|
||||
if (!connected() || len < 1){return 0;}
|
||||
if ( !connected() || len < 1){
|
||||
return 0;
|
||||
}
|
||||
int r;
|
||||
if (sock >= 0){
|
||||
r = send(sock, buffer, len, 0);
|
||||
|
@ -457,9 +485,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){
|
|||
default:
|
||||
if (errno != EPIPE){
|
||||
Error = true;
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
close();
|
||||
return 0;
|
||||
|
@ -471,7 +499,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){
|
|||
}
|
||||
up += 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,
|
||||
/// 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.
|
||||
/// \returns The amount of bytes actually read.
|
||||
int Socket::Connection::iread(void * buffer, int len){
|
||||
if (!connected() || len < 1){return 0;}
|
||||
if ( !connected() || len < 1){
|
||||
return 0;
|
||||
}
|
||||
int r;
|
||||
if (sock >= 0){
|
||||
r = recv(sock, buffer, len, 0);
|
||||
|
@ -494,9 +524,9 @@ int Socket::Connection::iread(void * buffer, int len){
|
|||
default:
|
||||
if (errno != EPIPE){
|
||||
Error = true;
|
||||
#if DEBUG >= 2
|
||||
#if DEBUG >= 2
|
||||
fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
close();
|
||||
return 0;
|
||||
|
@ -508,7 +538,7 @@ int Socket::Connection::iread(void * buffer, int len){
|
|||
}
|
||||
down += r;
|
||||
return r;
|
||||
}//Socket::Connection::iread
|
||||
} //Socket::Connection::iread
|
||||
|
||||
/// Read call that is compatible with Socket::Buffer.
|
||||
/// 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){
|
||||
char cbuffer[BUFFER_BLOCKSIZE];
|
||||
int num = iread(cbuffer, BUFFER_BLOCKSIZE);
|
||||
if (num < 1){return false;}
|
||||
if (num < 1){
|
||||
return false;
|
||||
}
|
||||
buffer.append(cbuffer, num);
|
||||
return true;
|
||||
}//iread
|
||||
} //iread
|
||||
|
||||
/// Incremental write call that is compatible with std::string.
|
||||
/// 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.
|
||||
/// \return True if more data was sent, false otherwise.
|
||||
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());
|
||||
if (tmp < 1){return false;}
|
||||
if (tmp < 1){
|
||||
return false;
|
||||
}
|
||||
buffer = buffer.substr(tmp);
|
||||
return true;
|
||||
}//iwrite
|
||||
} //iwrite
|
||||
|
||||
/// Gets hostname for connection, if available.
|
||||
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.
|
||||
/// 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];
|
||||
}
|
||||
|
||||
/// Returns true if these sockets are not the same socket.
|
||||
/// 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];
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
Socket::Server::Server(){
|
||||
sock = -1;
|
||||
}//Socket::Server base Constructor
|
||||
} //Socket::Server base Constructor
|
||||
|
||||
/// 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.
|
||||
|
@ -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 nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking).
|
||||
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));
|
||||
sock = -1;
|
||||
}
|
||||
}//Socket::Server TCP Constructor
|
||||
} //Socket::Server TCP Constructor
|
||||
|
||||
/// Attempt to bind an IPv6 socket.
|
||||
/// \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){
|
||||
sock = socket(AF_INET6, SOCK_STREAM, 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));
|
||||
#endif
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
int on = 1;
|
||||
|
@ -605,31 +641,31 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){
|
|||
}
|
||||
struct sockaddr_in6 addr;
|
||||
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){
|
||||
addr.sin6_addr = in6addr_any;
|
||||
}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){
|
||||
ret = listen(sock, 100);//start listening, backlog of 100 allowed
|
||||
ret = listen(sock, 100); //start listening, backlog of 100 allowed
|
||||
if (ret == 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port);
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
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){
|
||||
sock = socket(AF_INET, SOCK_STREAM, 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));
|
||||
#endif
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
int on = 1;
|
||||
|
@ -657,31 +693,31 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){
|
|||
}
|
||||
struct sockaddr_in addr4;
|
||||
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){
|
||||
addr4.sin_addr.s_addr = INADDR_ANY;
|
||||
}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){
|
||||
ret = listen(sock, 100);//start listening, backlog of 100 allowed
|
||||
ret = listen(sock, 100); //start listening, backlog of 100 allowed
|
||||
if (ret == 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port);
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
@ -697,9 +733,9 @@ Socket::Server::Server(std::string address, bool nonblock){
|
|||
unlink(address.c_str());
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (nonblock){
|
||||
|
@ -709,38 +745,40 @@ Socket::Server::Server(std::string address, bool nonblock){
|
|||
}
|
||||
sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, address.c_str(), address.size()+1);
|
||||
int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));
|
||||
strncpy(addr.sun_path, address.c_str(), address.size() + 1);
|
||||
int ret = bind(sock, (sockaddr*) &addr, sizeof(addr));
|
||||
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){
|
||||
return;
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno));
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
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.
|
||||
/// 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).
|
||||
/// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances.
|
||||
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;
|
||||
socklen_t len = sizeof(addrinfo);
|
||||
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.
|
||||
//we could do this through accept4 with a flag, but that call is non-standard...
|
||||
if ((r >= 0) && nonblock){
|
||||
|
@ -751,29 +789,29 @@ Socket::Connection Socket::Server::accept(bool nonblock){
|
|||
Socket::Connection tmp(r);
|
||||
if (r < 0){
|
||||
if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){
|
||||
#if DEBUG >= 1
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Error during accept - closing server socket.\n");
|
||||
#endif
|
||||
#endif
|
||||
close();
|
||||
}
|
||||
}else{
|
||||
if (addrinfo.sin6_family == AF_INET6){
|
||||
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());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (addrinfo.sin6_family == AF_INET){
|
||||
tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN);
|
||||
#if DEBUG >= 6
|
||||
tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*) &addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN);
|
||||
#if DEBUG >= 6
|
||||
fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (addrinfo.sin6_family == AF_UNIX){
|
||||
#if DEBUG >= 6
|
||||
#if DEBUG >= 6
|
||||
tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path;
|
||||
fprintf(stderr,"Unix socket, no address\n");
|
||||
#endif
|
||||
#endif
|
||||
tmp.remotehost = "UNIX_SOCKET";
|
||||
}
|
||||
}
|
||||
|
@ -784,15 +822,16 @@ Socket::Connection Socket::Server::accept(bool nonblock){
|
|||
/// If the connection is already closed, nothing happens.
|
||||
void Socket::Server::close(){
|
||||
if (connected()){
|
||||
#if DEBUG >= 6
|
||||
#if DEBUG >= 6
|
||||
fprintf(stderr, "ServerSocket closed.\n");
|
||||
#endif
|
||||
#endif
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
errno = EINTR;
|
||||
while (::close(sock) != 0 && errno == EINTR){}
|
||||
while (::close(sock) != 0 && errno == EINTR){
|
||||
}
|
||||
sock = -1;
|
||||
}
|
||||
}//Socket::Server::close
|
||||
} //Socket::Server::close
|
||||
|
||||
/// Returns the connected-state for this socket.
|
||||
/// 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.
|
||||
bool Socket::Server::connected() const{
|
||||
return (sock >= 0);
|
||||
}//Socket::Server::connected
|
||||
} //Socket::Server::connected
|
||||
|
||||
/// Returns internal socket number.
|
||||
int Socket::Server::getSocket(){return sock;}
|
||||
int Socket::Server::getSocket(){
|
||||
return sock;
|
||||
}
|
||||
|
|
21
lib/socket.h
21
lib/socket.h
|
@ -17,10 +17,12 @@
|
|||
#include <deque>
|
||||
|
||||
//for being friendly with Socket::Connection down below
|
||||
namespace Buffer{class user;};
|
||||
namespace Buffer {
|
||||
class user;
|
||||
}
|
||||
|
||||
///Holds Socket tools.
|
||||
namespace Socket{
|
||||
namespace Socket {
|
||||
|
||||
/// A buffer made out of std::string objects that can be efficiently read from and written to.
|
||||
class Buffer{
|
||||
|
@ -34,7 +36,8 @@ namespace Socket{
|
|||
bool available(unsigned int count);
|
||||
std::string remove(unsigned int count);
|
||||
std::string copy(unsigned int count);
|
||||
};//Buffer
|
||||
};
|
||||
//Buffer
|
||||
|
||||
/// This class is for easy communicating through sockets, either TCP or Unix.
|
||||
class Connection{
|
||||
|
@ -51,7 +54,7 @@ namespace Socket{
|
|||
int iwrite(const void * buffer, int len); ///< Incremental write call.
|
||||
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.
|
||||
public:
|
||||
public:
|
||||
//friends
|
||||
friend class ::Buffer::user;
|
||||
//constructors
|
||||
|
@ -86,8 +89,8 @@ namespace Socket{
|
|||
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.
|
||||
//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;
|
||||
};
|
||||
|
||||
|
@ -97,7 +100,7 @@ namespace Socket{
|
|||
int sock; ///< Internally saved socket number.
|
||||
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
|
||||
public:
|
||||
public:
|
||||
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(std::string adres, bool nonblock = false); ///< Create a new Unix Server.
|
||||
|
@ -106,5 +109,5 @@ namespace Socket{
|
|||
void close(); ///< Close connection.
|
||||
int getSocket(); ///< Returns internal socket number.
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,18 +18,21 @@
|
|||
/// that character is deleted. The original string is modified.
|
||||
void Util::Stream::sanitizeName(std::string & streamname){
|
||||
//strip anything that isn't numbers, digits or underscores
|
||||
for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){
|
||||
if (*i == '?'){streamname.erase(i, streamname.end()); break;}
|
||||
if (!isalpha(*i) && !isdigit(*i) && *i != '_'){
|
||||
for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){
|
||||
if ( *i == '?'){
|
||||
streamname.erase(i, streamname.end());
|
||||
break;
|
||||
}
|
||||
if ( !isalpha( *i) && !isdigit( *i) && *i != '_'){
|
||||
streamname.erase(i);
|
||||
}else{
|
||||
*i=tolower(*i);
|
||||
*i = tolower( *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -49,20 +52,20 @@ Socket::Connection Util::Stream::getStream(std::string streamname){
|
|||
JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist");
|
||||
if (ServConf["streams"].isMember(streamname)){
|
||||
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;
|
||||
#endif
|
||||
#endif
|
||||
return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString());
|
||||
}else{
|
||||
#if DEBUG >= 4
|
||||
#if DEBUG >= 4
|
||||
std::cerr << "Opening live stream " << streamname << std::endl;
|
||||
#endif
|
||||
return Socket::Connection("/tmp/mist/stream_"+streamname);
|
||||
#endif
|
||||
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;
|
||||
#endif
|
||||
#endif
|
||||
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.
|
||||
Socket::Server Util::Stream::makeLive(std::string 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.
|
||||
//ignore errors - we catch all problems in the Socket::Server creation already
|
||||
mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <string>
|
||||
#include "socket.h"
|
||||
|
||||
namespace Util{
|
||||
namespace Util {
|
||||
class Stream{
|
||||
public:
|
||||
static void sanitizeName(std::string & streamname);
|
||||
|
|
|
@ -4,22 +4,25 @@
|
|||
#include "timing.h"
|
||||
#include <sys/time.h>//for gettimeofday
|
||||
#include <time.h>//for time and nanosleep
|
||||
|
||||
/// Sleeps for the indicated amount of milliseconds or longer.
|
||||
void Util::sleep(int ms){
|
||||
if (ms < 0){return;}
|
||||
if (ms > 10000){return;}
|
||||
if (ms < 0){
|
||||
return;
|
||||
}
|
||||
if (ms > 10000){
|
||||
return;
|
||||
}
|
||||
struct timespec T;
|
||||
T.tv_sec = ms/1000;
|
||||
T.tv_nsec = 1000000*(ms%1000);
|
||||
nanosleep(&T, 0);
|
||||
T.tv_sec = ms / 1000;
|
||||
T.tv_nsec = 1000000 * (ms % 1000);
|
||||
nanosleep( &T, 0);
|
||||
}
|
||||
|
||||
/// Gets the current time in milliseconds.
|
||||
long long int Util::getMS(){
|
||||
struct timespec 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.
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace Util{
|
||||
namespace Util {
|
||||
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 epoch(); ///< Gets the amount of seconds since 01/01/1970.
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue