Global cleanups and standardization of code style.

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

File diff suppressed because it is too large Load diff

View file

@ -4,14 +4,13 @@
#pragma once
#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

View file

@ -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.

View file

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

View file

@ -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;
}

View file

@ -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
}
}

View file

@ -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();
};
}
;

View file

View file

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

View file

@ -5,12 +5,12 @@
#include <stdlib.h>
#include <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);

View file

@ -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;
};
};
}

View file

@ -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;
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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: {

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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);
};
}

File diff suppressed because it is too large Load diff

289
lib/mp4.h
View file

@ -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);
};
};
}

View file

@ -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;
}

View file

@ -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);
};
};
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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.
};
};
}

View file

@ -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);

View file

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

View file

@ -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.

View file

@ -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.
};
}