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

View file

@ -4,68 +4,89 @@
#include "amf.h" #include "amf.h"
#include <sstream> #include <sstream>
#include <cstdio> //needed for stderr only #include <cstdio> //needed for stderr only
/// Returns the std::string Indice for the current object, if available. /// Returns the std::string Indice for the current object, if available.
/// Returns an empty string if no indice exists. /// Returns an empty string if no indice exists.
std::string AMF::Object::Indice(){return myIndice;}; std::string AMF::Object::Indice(){
return myIndice;
}
/// Returns the AMF::obj0type AMF0 object type for this object. /// Returns the AMF::obj0type AMF0 object type for this object.
AMF::obj0type AMF::Object::GetType(){return myType;}; AMF::obj0type AMF::Object::GetType(){
return myType;
}
/// Returns the numeric value of this object, if available. /// Returns the numeric value of this object, if available.
/// If this object holds no numeric value, 0 is returned. /// If this object holds no numeric value, 0 is returned.
double AMF::Object::NumValue(){return numval;}; double AMF::Object::NumValue(){
return numval;
}
/// Returns the std::string value of this object, if available. /// Returns the std::string value of this object, if available.
/// If this object holds no string value, an empty string is returned. /// If this object holds no string value, an empty string is returned.
std::string AMF::Object::StrValue(){return strval;}; std::string AMF::Object::StrValue(){
return strval;
}
/// Returns the C-string value of this object, if available. /// Returns the C-string value of this object, if available.
/// If this object holds no string value, an empty C-string is returned. /// If this object holds no string value, an empty C-string is returned.
const char * AMF::Object::Str(){return strval.c_str();}; const char * AMF::Object::Str(){
return strval.c_str();
}
/// Returns a count of the amount of objects this object currently holds. /// Returns a count of the amount of objects this object currently holds.
/// If this object is not a container type, this function will always return 0. /// If this object is not a container type, this function will always return 0.
int AMF::Object::hasContent(){return contents.size();}; int AMF::Object::hasContent(){
return contents.size();
}
/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. /// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types.
void AMF::Object::addContent(AMF::Object c){contents.push_back(c);}; void AMF::Object::addContent(AMF::Object c){
contents.push_back(c);
}
/// Returns a pointer to the object held at indice i. /// Returns a pointer to the object held at indice i.
/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param i The indice of the object in this container. /// \param i The indice of the object in this container.
AMF::Object* AMF::Object::getContentP(int i){return &contents.at(i);}; AMF::Object* AMF::Object::getContentP(int i){
return &contents.at(i);
}
/// Returns a copy of the object held at indice i. /// Returns a copy of the object held at indice i.
/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param i The indice of the object in this container. /// \param i The indice of the object in this container.
AMF::Object AMF::Object::getContent(int i){return contents.at(i);}; AMF::Object AMF::Object::getContent(int i){
return contents.at(i);
}
/// Returns a pointer to the object held at indice s. /// Returns a pointer to the object held at indice s.
/// Returns NULL if no object is held at this indice. /// Returns NULL if no object is held at this indice.
/// \param s The indice of the object in this container. /// \param s The indice of the object in this container.
AMF::Object* AMF::Object::getContentP(std::string s){ AMF::Object* AMF::Object::getContentP(std::string s){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
if (it->Indice() == s){return &(*it);} if (it->Indice() == s){
return &( *it);
}
} }
return 0; return 0;
}; }
/// Returns a copy of the object held at indice s. /// Returns a copy of the object held at indice s.
/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param s The indice of the object in this container. /// \param s The indice of the object in this container.
AMF::Object AMF::Object::getContent(std::string s){ AMF::Object AMF::Object::getContent(std::string s){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
if (it->Indice() == s){return *it;} if (it->Indice() == s){
return *it;
}
} }
return AMF::Object("error", AMF0_DDV_CONTAINER); return AMF::Object("error", AMF0_DDV_CONTAINER);
}; }
/// Default constructor. /// Default constructor.
/// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) /// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER)
AMF::Object::Object(){ AMF::Object::Object(){
*this = AMF::Object("error", AMF0_DDV_CONTAINER); *this = AMF::Object("error", AMF0_DDV_CONTAINER);
};//default constructor } //default constructor
/// Constructor for numeric objects. /// Constructor for numeric objects.
/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. /// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value.
@ -77,7 +98,7 @@ AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){//num
myType = setType; myType = setType;
strval = ""; strval = "";
numval = val; numval = val;
}; }
/// Constructor for string objects. /// Constructor for string objects.
/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value.
@ -90,7 +111,7 @@ AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType){
myType = setType; myType = setType;
strval = val; strval = val;
numval = 0; numval = 0;
}; }
/// Constructor for container objects. /// Constructor for container objects.
/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value.
@ -101,7 +122,7 @@ AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type ini
myType = setType; myType = setType;
strval = ""; strval = "";
numval = 0; numval = 0;
}; }
/// Prints the contents of this object to std::cerr. /// Prints the contents of this object to std::cerr.
/// If this object contains other objects, it will call itself recursively /// If this object contains other objects, it will call itself recursively
@ -111,41 +132,90 @@ std::string AMF::Object::Print(std::string indent){
st << indent; st << indent;
// print my type // print my type
switch (myType){ switch (myType){
case AMF::AMF0_NUMBER: st << "Number"; break; case AMF::AMF0_NUMBER:
case AMF::AMF0_BOOL: st << "Bool"; break; st << "Number";
break;
case AMF::AMF0_BOOL:
st << "Bool";
break;
case AMF::AMF0_STRING: //short string case AMF::AMF0_STRING: //short string
case AMF::AMF0_LONGSTRING: st << "String"; break; case AMF::AMF0_LONGSTRING:
case AMF::AMF0_OBJECT: st << "Object"; break; st << "String";
case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break; break;
case AMF::AMF0_NULL: st << "Null"; break; case AMF::AMF0_OBJECT:
case AMF::AMF0_UNDEFINED: st << "Undefined"; break; st << "Object";
case AMF::AMF0_REFERENCE: st << "Reference"; break; break;
case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break; case AMF::AMF0_MOVIECLIP:
case AMF::AMF0_OBJ_END: st << "Object end"; break; st << "MovieClip";
case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break; break;
case AMF::AMF0_DATE: st << "Date"; break; case AMF::AMF0_NULL:
case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break; st << "Null";
case AMF::AMF0_RECORDSET: st << "Recordset"; break; break;
case AMF::AMF0_XMLDOC: st << "XML Document"; break; case AMF::AMF0_UNDEFINED:
case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break; st << "Undefined";
case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break; break;
case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break; case AMF::AMF0_REFERENCE:
st << "Reference";
break;
case AMF::AMF0_ECMA_ARRAY:
st << "ECMA Array";
break;
case AMF::AMF0_OBJ_END:
st << "Object end";
break;
case AMF::AMF0_STRICT_ARRAY:
st << "Strict Array";
break;
case AMF::AMF0_DATE:
st << "Date";
break;
case AMF::AMF0_UNSUPPORTED:
st << "Unsupported";
break;
case AMF::AMF0_RECORDSET:
st << "Recordset";
break;
case AMF::AMF0_XMLDOC:
st << "XML Document";
break;
case AMF::AMF0_TYPED_OBJ:
st << "Typed Object";
break;
case AMF::AMF0_UPGRADE:
st << "Upgrade to AMF3";
break;
case AMF::AMF0_DDV_CONTAINER:
st << "DDVTech Container";
break;
} }
// print my string indice, if available // print my string indice, if available
st << " " << myIndice << " "; st << " " << myIndice << " ";
// print my numeric or string contents // print my numeric or string contents
switch (myType){ switch (myType){
case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: st << numval; break; case AMF::AMF0_NUMBER:
case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: st << strval; break; case AMF::AMF0_BOOL:
default: break;//we don't care about the rest, and don't want a compiler warning... case AMF::AMF0_REFERENCE:
case AMF::AMF0_DATE:
st << numval;
break;
case AMF::AMF0_STRING:
case AMF::AMF0_LONGSTRING:
case AMF::AMF0_XMLDOC:
case AMF::AMF0_TYPED_OBJ:
st << strval;
break;
default:
break; //we don't care about the rest, and don't want a compiler warning...
} }
st << std::endl; st << std::endl;
// if I hold other objects, print those too, recursively. // if I hold other objects, print those too, recursively.
if (contents.size() > 0){ if (contents.size() > 0){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){st << it->Print(indent+" ");} for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
st << it->Print(indent + " ");
}
} }
return st.str(); return st.str();
};//print } //print
/// Packs the AMF object to a std::string for transfer over the network. /// Packs the AMF object to a std::string for transfer over the network.
/// If the object is a container type, this function will call itself recursively and contain all contents. /// If the object is a container type, this function will call itself recursively and contain all contents.
@ -153,22 +223,34 @@ std::string AMF::Object::Print(std::string indent){
std::string AMF::Object::Pack(){ std::string AMF::Object::Pack(){
std::string r = ""; std::string r = "";
//check for string/longstring conversion //check for string/longstring conversion
if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF::AMF0_LONGSTRING;} if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){
myType = AMF::AMF0_LONGSTRING;
}
//skip output of DDV container types, they do not exist. Only output their contents. //skip output of DDV container types, they do not exist. Only output their contents.
if (myType != AMF::AMF0_DDV_CONTAINER){r += myType;} if (myType != AMF::AMF0_DDV_CONTAINER){
r += myType;
}
//output the properly formatted AMF0 data stream for this object's contents. //output the properly formatted AMF0 data stream for this object's contents.
switch (myType){ switch (myType){
case AMF::AMF0_NUMBER: case AMF::AMF0_NUMBER:
r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); r += *(((char*) &numval) + 7);
r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); r += *(((char*) &numval) + 6);
r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); r += *(((char*) &numval) + 5);
r += *(((char*)&numval)+1); r += *(((char*)&numval)); r += *(((char*) &numval) + 4);
r += *(((char*) &numval) + 3);
r += *(((char*) &numval) + 2);
r += *(((char*) &numval) + 1);
r += *(((char*) &numval));
break; break;
case AMF::AMF0_DATE: case AMF::AMF0_DATE:
r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); r += *(((char*) &numval) + 7);
r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); r += *(((char*) &numval) + 6);
r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); r += *(((char*) &numval) + 5);
r += *(((char*)&numval)+1); r += *(((char*)&numval)); r += *(((char*) &numval) + 4);
r += *(((char*) &numval) + 3);
r += *(((char*) &numval) + 2);
r += *(((char*) &numval) + 1);
r += *(((char*) &numval));
r += (char)0; //timezone always 0 r += (char)0; //timezone always 0
r += (char)0; //timezone always 0 r += (char)0; //timezone always 0
break; break;
@ -188,11 +270,11 @@ std::string AMF::Object::Pack(){
r += strval.size() % 256; r += strval.size() % 256;
r += strval; r += strval;
break; break;
case AMF::AMF0_TYPED_OBJ: case AMF::AMF0_TYPED_OBJ: //is an object, with the classname first
r += Indice().size() / 256; r += Indice().size() / 256;
r += Indice().size() % 256; r += Indice().size() % 256;
r += Indice(); r += Indice();
//is an object, with the classname first /* no break */
case AMF::AMF0_OBJECT: case AMF::AMF0_OBJECT:
if (contents.size() > 0){ if (contents.size() > 0){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
@ -202,7 +284,9 @@ std::string AMF::Object::Pack(){
r += it->Pack(); r += it->Pack();
} }
} }
r += (char)0; r += (char)0; r += (char)9; r += (char)0;
r += (char)0;
r += (char)9;
break; break;
case AMF::AMF0_MOVIECLIP: case AMF::AMF0_MOVIECLIP:
case AMF::AMF0_OBJ_END: case AMF::AMF0_OBJ_END:
@ -221,7 +305,10 @@ std::string AMF::Object::Pack(){
int arrlen = 0; int arrlen = 0;
if (contents.size() > 0){ if (contents.size() > 0){
arrlen = contents.size(); arrlen = contents.size();
r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; r += arrlen / (256 * 256 * 256);
r += arrlen / (256 * 256);
r += arrlen / 256;
r += arrlen % 256;
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
r += it->Indice().size() / 256; r += it->Indice().size() / 256;
r += it->Indice().size() % 256; r += it->Indice().size() % 256;
@ -229,22 +316,35 @@ std::string AMF::Object::Pack(){
r += it->Pack(); r += it->Pack();
} }
}else{ }else{
r += (char)0; r += (char)0; r += (char)0; r += (char)0; r += (char)0;
r += (char)0;
r += (char)0;
r += (char)0;
} }
r += (char)0; r += (char)0; r += (char)9; r += (char)0;
} break; r += (char)0;
r += (char)9;
}
break;
case AMF::AMF0_STRICT_ARRAY: { case AMF::AMF0_STRICT_ARRAY: {
int arrlen = 0; int arrlen = 0;
if (contents.size() > 0){ if (contents.size() > 0){
arrlen = contents.size(); arrlen = contents.size();
r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; r += arrlen / (256 * 256 * 256);
r += arrlen / (256 * 256);
r += arrlen / 256;
r += arrlen % 256;
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
r += it->Pack(); r += it->Pack();
} }
}else{ }else{
r += (char)0; r += (char)0; r += (char)0; r += (char)0; r += (char)0;
r += (char)0;
r += (char)0;
r += (char)0;
} }
} break; }
break;
case AMF::AMF0_DDV_CONTAINER: //only send contents case AMF::AMF0_DDV_CONTAINER: //only send contents
if (contents.size() > 0){ if (contents.size() > 0){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){
@ -254,7 +354,7 @@ std::string AMF::Object::Pack(){
break; break;
} }
return r; return r;
};//pack } //pack
/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. /// Parses a single AMF0 type - used recursively by the AMF::parse() functions.
/// This function updates i every call with the new position in the data. /// This function updates i every call with the new position in the data.
@ -350,7 +450,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign
} }
i += 3; //skip 0x000009 i += 3; //skip 0x000009
return ret; return ret;
} break; }
break;
case AMF::AMF0_TYPED_OBJ: { case AMF::AMF0_TYPED_OBJ: {
++i; ++i;
tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length
@ -366,7 +467,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign
} }
i += 3; //skip 0x000009 i += 3; //skip 0x000009
return ret; return ret;
} break; }
break;
case AMF::AMF0_ECMA_ARRAY: { case AMF::AMF0_ECMA_ARRAY: {
++i; ++i;
AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY);
@ -380,7 +482,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign
} }
i += 3; //skip 0x000009 i += 3; //skip 0x000009
return ret; return ret;
} break; }
break;
case AMF::AMF0_STRICT_ARRAY: { case AMF::AMF0_STRICT_ARRAY: {
AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY);
tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to array length tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to array length
@ -390,7 +493,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign
--tmpi; --tmpi;
} }
return ret; return ret;
} break; }
break;
} }
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]);
@ -406,7 +510,11 @@ AMF::Object AMF::parse(const unsigned char * data, unsigned int len){
unsigned int i = 0, j = 0; unsigned int i = 0, j = 0;
while (i < len){ while (i < len){
ret.addContent(AMF::parseOne(data, len, i, "")); ret.addContent(AMF::parseOne(data, len, i, ""));
if (i > j){j = i;}else{return ret;} if (i > j){
j = i;
}else{
return ret;
}
} }
return ret; return ret;
} //parse } //parse
@ -420,69 +528,93 @@ AMF::Object AMF::parse(std::string data){
/// Returns the std::string Indice for the current object, if available. /// Returns the std::string Indice for the current object, if available.
/// Returns an empty string if no indice exists. /// Returns an empty string if no indice exists.
std::string AMF::Object3::Indice(){return myIndice;}; std::string AMF::Object3::Indice(){
return myIndice;
}
/// Returns the AMF::obj0type AMF0 object type for this object. /// Returns the AMF::obj0type AMF0 object type for this object.
AMF::obj3type AMF::Object3::GetType(){return myType;}; AMF::obj3type AMF::Object3::GetType(){
return myType;
}
/// Returns the double value of this object, if available. /// Returns the double value of this object, if available.
/// If this object holds no double value, 0 is returned. /// If this object holds no double value, 0 is returned.
double AMF::Object3::DblValue(){return dblval;}; double AMF::Object3::DblValue(){
return dblval;
}
/// Returns the integer value of this object, if available. /// Returns the integer value of this object, if available.
/// If this object holds no integer value, 0 is returned. /// If this object holds no integer value, 0 is returned.
int AMF::Object3::IntValue(){return intval;}; int AMF::Object3::IntValue(){
return intval;
}
/// Returns the std::string value of this object, if available. /// Returns the std::string value of this object, if available.
/// If this object holds no string value, an empty string is returned. /// If this object holds no string value, an empty string is returned.
std::string AMF::Object3::StrValue(){return strval;}; std::string AMF::Object3::StrValue(){
return strval;
}
/// Returns the C-string value of this object, if available. /// Returns the C-string value of this object, if available.
/// If this object holds no string value, an empty C-string is returned. /// If this object holds no string value, an empty C-string is returned.
const char * AMF::Object3::Str(){return strval.c_str();}; const char * AMF::Object3::Str(){
return strval.c_str();
}
/// Returns a count of the amount of objects this object currently holds. /// Returns a count of the amount of objects this object currently holds.
/// If this object is not a container type, this function will always return 0. /// If this object is not a container type, this function will always return 0.
int AMF::Object3::hasContent(){return contents.size();}; int AMF::Object3::hasContent(){
return contents.size();
}
/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. /// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types.
void AMF::Object3::addContent(AMF::Object3 c){contents.push_back(c);}; void AMF::Object3::addContent(AMF::Object3 c){
contents.push_back(c);
}
/// Returns a pointer to the object held at indice i. /// Returns a pointer to the object held at indice i.
/// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param i The indice of the object in this container. /// \param i The indice of the object in this container.
AMF::Object3* AMF::Object3::getContentP(int i){return &contents.at(i);}; AMF::Object3* AMF::Object3::getContentP(int i){
return &contents.at(i);
}
/// Returns a copy of the object held at indice i. /// Returns a copy of the object held at indice i.
/// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param i The indice of the object in this container. /// \param i The indice of the object in this container.
AMF::Object3 AMF::Object3::getContent(int i){return contents.at(i);}; AMF::Object3 AMF::Object3::getContent(int i){
return contents.at(i);
}
/// Returns a pointer to the object held at indice s. /// Returns a pointer to the object held at indice s.
/// Returns NULL if no object is held at this indice. /// Returns NULL if no object is held at this indice.
/// \param s The indice of the object in this container. /// \param s The indice of the object in this container.
AMF::Object3* AMF::Object3::getContentP(std::string s){ AMF::Object3* AMF::Object3::getContentP(std::string s){
for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){
if (it->Indice() == s){return &(*it);} if (it->Indice() == s){
return &( *it);
}
} }
return 0; return 0;
}; }
/// Returns a copy of the object held at indice s. /// Returns a copy of the object held at indice s.
/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice.
/// \param s The indice of the object in this container. /// \param s The indice of the object in this container.
AMF::Object3 AMF::Object3::getContent(std::string s){ AMF::Object3 AMF::Object3::getContent(std::string s){
for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){ for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){
if (it->Indice() == s){return *it;} if (it->Indice() == s){
return *it;
}
} }
return AMF::Object3("error", AMF3_DDV_CONTAINER); return AMF::Object3("error", AMF3_DDV_CONTAINER);
}; }
/// Default constructor. /// Default constructor.
/// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) /// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER)
AMF::Object3::Object3(){ AMF::Object3::Object3(){
*this = AMF::Object3("error", AMF3_DDV_CONTAINER); *this = AMF::Object3("error", AMF3_DDV_CONTAINER);
};//default constructor } //default constructor
/// Constructor for double objects. /// Constructor for double objects.
/// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value. /// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value.
@ -495,7 +627,7 @@ AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType){//n
strval = ""; strval = "";
dblval = val; dblval = val;
intval = 0; intval = 0;
}; }
/// Constructor for integer objects. /// Constructor for integer objects.
/// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value. /// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value.
@ -508,7 +640,7 @@ myType = setType;
strval = ""; strval = "";
dblval = val; dblval = val;
intval = 0; intval = 0;
}; }
/// Constructor for string objects. /// Constructor for string objects.
/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value.
@ -522,7 +654,7 @@ AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType
strval = val; strval = val;
dblval = 0; dblval = 0;
intval = 0; intval = 0;
}; }
/// Constructor for container objects. /// Constructor for container objects.
/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value.
@ -534,7 +666,7 @@ AMF::Object3::Object3(std::string indice, AMF::obj3type setType){//object type i
strval = ""; strval = "";
dblval = 0; dblval = 0;
intval = 0; intval = 0;
}; }
/// Prints the contents of this object to std::cerr. /// Prints the contents of this object to std::cerr.
/// If this object contains other objects, it will call itself recursively /// If this object contains other objects, it will call itself recursively
@ -543,28 +675,63 @@ void AMF::Object3::Print(std::string indent){
std::cerr << indent; std::cerr << indent;
// print my type // print my type
switch (myType){ switch (myType){
case AMF::AMF3_UNDEFINED: std::cerr << "Undefined"; break; case AMF::AMF3_UNDEFINED:
case AMF::AMF3_NULL: std::cerr << "Null"; break; std::cerr << "Undefined";
case AMF::AMF3_FALSE: std::cerr << "False"; break; break;
case AMF::AMF3_TRUE: std::cerr << "True"; break; case AMF::AMF3_NULL:
case AMF::AMF3_INTEGER: std::cerr << "Integer"; break; std::cerr << "Null";
case AMF::AMF3_DOUBLE: std::cerr << "Double"; break; break;
case AMF::AMF3_STRING: std::cerr << "String"; break; case AMF::AMF3_FALSE:
case AMF::AMF3_XMLDOC: std::cerr << "XML Doc"; break; std::cerr << "False";
case AMF::AMF3_DATE: std::cerr << "Date"; break; break;
case AMF::AMF3_ARRAY: std::cerr << "Array"; break; case AMF::AMF3_TRUE:
case AMF::AMF3_OBJECT: std::cerr << "Object"; break; std::cerr << "True";
case AMF::AMF3_XML: std::cerr << "XML"; break; break;
case AMF::AMF3_BYTES: std::cerr << "ByteArray"; break; case AMF::AMF3_INTEGER:
case AMF::AMF3_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; std::cerr << "Integer";
break;
case AMF::AMF3_DOUBLE:
std::cerr << "Double";
break;
case AMF::AMF3_STRING:
std::cerr << "String";
break;
case AMF::AMF3_XMLDOC:
std::cerr << "XML Doc";
break;
case AMF::AMF3_DATE:
std::cerr << "Date";
break;
case AMF::AMF3_ARRAY:
std::cerr << "Array";
break;
case AMF::AMF3_OBJECT:
std::cerr << "Object";
break;
case AMF::AMF3_XML:
std::cerr << "XML";
break;
case AMF::AMF3_BYTES:
std::cerr << "ByteArray";
break;
case AMF::AMF3_DDV_CONTAINER:
std::cerr << "DDVTech Container";
break;
} }
// print my string indice, if available // print my string indice, if available
std::cerr << " " << myIndice << " "; std::cerr << " " << myIndice << " ";
// print my numeric or string contents // print my numeric or string contents
switch (myType){ switch (myType){
case AMF::AMF3_INTEGER: std::cerr << intval; break; case AMF::AMF3_INTEGER:
case AMF::AMF3_DOUBLE: std::cerr << dblval; break; std::cerr << intval;
case AMF::AMF3_STRING: case AMF::AMF3_XMLDOC: case AMF::AMF3_XML: case AMF::AMF3_BYTES: break;
case AMF::AMF3_DOUBLE:
std::cerr << dblval;
break;
case AMF::AMF3_STRING:
case AMF::AMF3_XMLDOC:
case AMF::AMF3_XML:
case AMF::AMF3_BYTES:
if (intval > 0){ if (intval > 0){
std::cerr << "REF" << intval; std::cerr << "REF" << intval;
}else{ }else{
@ -578,19 +745,23 @@ void AMF::Object3::Print(std::string indent){
std::cerr << dblval; std::cerr << dblval;
} }
break; break;
case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: case AMF::AMF3_ARRAY:
case AMF::AMF3_OBJECT:
if (intval > 0){ if (intval > 0){
std::cerr << "REF" << intval; std::cerr << "REF" << intval;
} }
break; break;
default: break;//we don't care about the rest, and don't want a compiler warning... default:
break; //we don't care about the rest, and don't want a compiler warning...
} }
std::cerr << std::endl; std::cerr << std::endl;
// if I hold other objects, print those too, recursively. // if I hold other objects, print those too, recursively.
if (contents.size() > 0){ if (contents.size() > 0){
for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} for (std::vector<AMF::Object3>::iterator it = contents.begin(); it != contents.end(); it++){
it->Print(indent + " ");
} }
};//print }
} //print
/// Packs the AMF object to a std::string for transfer over the network. /// Packs the AMF object to a std::string for transfer over the network.
/// If the object is a container type, this function will call itself recursively and contain all contents. /// If the object is a container type, this function will call itself recursively and contain all contents.
@ -598,7 +769,7 @@ void AMF::Object3::Print(std::string indent){
std::string AMF::Object3::Pack(){ std::string AMF::Object3::Pack(){
std::string r = ""; std::string r = "";
return r; return r;
};//pack } //pack
/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions.
/// This function updates i every call with the new position in the data. /// This function updates i every call with the new position in the data.
@ -879,7 +1050,8 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi
--arrsize; --arrsize;
} }
return ret; return ret;
} break; }
break;
case AMF::AMF3_OBJECT: { case AMF::AMF3_OBJECT: {
if (data[i + 1] < 0x80){ if (data[i + 1] < 0x80){
tmpi = data[i + 1]; tmpi = data[i + 1];
@ -946,7 +1118,8 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi
}while (tmpi > 0); //keep reading dynamic values until empty string }while (tmpi > 0); //keep reading dynamic values until empty string
} //dynamic types } //dynamic types
return ret; return ret;
} break; }
break;
} }
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]);
@ -962,7 +1135,11 @@ AMF::Object3 AMF::parse3(const unsigned char * data, unsigned int len){
unsigned int i = 0, j = 0; unsigned int i = 0, j = 0;
while (i < len){ while (i < len){
ret.addContent(AMF::parseOne3(data, len, i, "")); ret.addContent(AMF::parseOne3(data, len, i, ""));
if (i > j){j = i;}else{return ret;} if (i > j){
j = i;
}else{
return ret;
}
} }
return ret; return ret;
} //parse } //parse

View file

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <iostream> #include <iostream>
//#include <string.h>
#include <string> #include <string>
/// Holds all AMF parsing and creation related functions and classes. /// Holds all AMF parsing and creation related functions and classes.
@ -78,7 +77,8 @@ namespace AMF{
std::string strval; ///< Holds this objects string value, if any. std::string strval; ///< Holds this objects string value, if any.
double numval; ///< Holds this objects numeric value, if any. double numval; ///< Holds this objects numeric value, if any.
std::vector<Object> contents; ///< Holds this objects contents, if any (for container types). std::vector<Object> contents; ///< Holds this objects contents, if any (for container types).
};//AMFType };
//AMFType
/// Parses a C-string to a valid AMF::Object. /// Parses a C-string to a valid AMF::Object.
Object parse(const unsigned char * data, unsigned int len); Object parse(const unsigned char * data, unsigned int len);
@ -117,7 +117,8 @@ namespace AMF{
double dblval; ///< Holds this objects double value, if any. double dblval; ///< Holds this objects double value, if any.
int intval; ///< Holds this objects int value, if any. int intval; ///< Holds this objects int value, if any.
std::vector<Object3> contents; ///< Holds this objects contents, if any (for container types). std::vector<Object3> contents; ///< Holds this objects contents, if any (for container types).
};//AMFType };
//AMFType
/// Parses a C-string to a valid AMF::Object3. /// Parses a C-string to a valid AMF::Object3.
Object3 parse3(const unsigned char * data, unsigned int len); Object3 parse3(const unsigned char * data, unsigned int len);
@ -126,4 +127,4 @@ namespace AMF{
/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions.
Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name);
};//AMF namespace } //AMF namespace

View file

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

View file

@ -17,16 +17,28 @@ std::string Base64::encode(std::string const input) {
char quad[4], triple[3]; char quad[4], triple[3];
unsigned int i, x, n = 3; unsigned int i, x, n = 3;
for (x = 0; x < in_len; x = x + 3){ for (x = 0; x < in_len; x = x + 3){
if ((in_len - x) / 3 == 0){n = (in_len - x) % 3;} if ((in_len - x) / 3 == 0){
for (i=0; i < 3; i++){triple[i] = '0';} n = (in_len - x) % 3;
for (i=0; i < n; i++){triple[i] = input[x + i];} }
for (i = 0; i < 3; i++){
triple[i] = '0';
}
for (i = 0; i < n; i++){
triple[i] = input[x + i];
}
quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100 quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100
quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11 quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11
quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110 quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110
quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111 quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111
if (n < 3){quad[3] = '=';} if (n < 3){
if (n < 2){quad[2] = '=';} quad[3] = '=';
for(i=0; i < 4; i++){ret += quad[i];} }
if (n < 2){
quad[2] = '=';
}
for (i = 0; i < 4; i++){
ret += quad[i];
}
} }
return ret; return ret;
} //base64_encode } //base64_encode
@ -42,23 +54,33 @@ std::string Base64::decode(std::string const& encoded_string) {
unsigned char char_array_4[4], char_array_3[3]; unsigned char char_array_4[4], char_array_3[3];
std::string ret; std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])){ while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])){
char_array_4[i++] = encoded_string[in_]; in_++; char_array_4[i++ ] = encoded_string[in_];
in_++;
if (i == 4){ if (i == 4){
for (i = 0; i <4; i++){char_array_4[i] = chars.find(char_array_4[i]);} for (i = 0; i < 4; i++){
char_array_4[i] = chars.find(char_array_4[i]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++){ret += char_array_3[i];} for (i = 0; (i < 3); i++){
ret += char_array_3[i];
}
i = 0; i = 0;
} }
} }
if (i){ if (i){
for (j = i; j <4; j++){char_array_4[j] = 0;} for (j = i; j < 4; j++){
for (j = 0; j <4; j++){char_array_4[j] = chars.find(char_array_4[j]);} char_array_4[j] = 0;
}
for (j = 0; j < 4; j++){
char_array_4[j] = chars.find(char_array_4[j]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
} }
return ret; return ret;
} }

View file

@ -62,8 +62,12 @@ void Util::Config::addOption(std::string optname, JSON::Value option){
} }
long_count = 0; long_count = 0;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
if (it->second.isMember("long")){long_count++;} if (it->second.isMember("long")){
if (it->second.isMember("long_off")){long_count++;} long_count++;
}
if (it->second.isMember("long_off")){
long_count++;
}
} }
} }
@ -73,16 +77,30 @@ void Util::Config::printHelp(std::ostream & output){
std::map<long long int, std::string> args; std::map<long long int, std::string> args;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
unsigned int current = 0; unsigned int current = 0;
if (it->second.isMember("long")){current += it->second["long"].asString().size() + 4;} if (it->second.isMember("long")){
if (it->second.isMember("short")){current += it->second["short"].asString().size() + 3;} current += it->second["long"].asString().size() + 4;
if (current > longest){longest = current;} }
if (it->second.isMember("short")){
current += it->second["short"].asString().size() + 3;
}
if (current > longest){
longest = current;
}
current = 0; current = 0;
if (it->second.isMember("long_off")){current += it->second["long_off"].asString().size() + 4;} if (it->second.isMember("long_off")){
if (it->second.isMember("short_off")){current += it->second["short_off"].asString().size() + 3;} current += it->second["long_off"].asString().size() + 4;
if (current > longest){longest = current;} }
if (it->second.isMember("short_off")){
current += it->second["short_off"].asString().size() + 3;
}
if (current > longest){
longest = current;
}
if (it->second.isMember("arg_num")){ if (it->second.isMember("arg_num")){
current = it->first.size() + 3; current = it->first.size() + 3;
if (current > longest){longest = current;} if (current > longest){
longest = current;
}
args[it->second["arg_num"].asInt()] = it->first; args[it->second["arg_num"].asInt()] = it->first;
} }
} }
@ -108,7 +126,9 @@ void Util::Config::printHelp(std::ostream & output){
f = "-" + it->second["short"].asString(); f = "-" + it->second["short"].asString();
} }
} }
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
if (it->second.isMember("arg")){ if (it->second.isMember("arg")){
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
}else{ }else{
@ -126,7 +146,9 @@ void Util::Config::printHelp(std::ostream & output){
f = "-" + it->second["short_off"].asString(); f = "-" + it->second["short_off"].asString();
} }
} }
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
if (it->second.isMember("arg")){ if (it->second.isMember("arg")){
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
}else{ }else{
@ -135,13 +157,14 @@ void Util::Config::printHelp(std::ostream & output){
} }
if (it->second.isMember("arg_num")){ if (it->second.isMember("arg_num")){
f = it->first; f = it->first;
while (f.size() < longest){f.append(" ");} while (f.size() < longest){
f.append(" ");
}
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
} }
} }
} }
/// Parses commandline arguments. /// Parses commandline arguments.
/// Calls exit if an unknown option is encountered, printing a help message. /// Calls exit if an unknown option is encountered, printing a help message.
void Util::Config::parseArgs(int argc, char ** argv){ void Util::Config::parseArgs(int argc, char ** argv){
@ -153,22 +176,30 @@ void Util::Config::parseArgs(int argc, char ** argv){
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){
if (it->second.isMember("short")){ if (it->second.isMember("short")){
shortopts += it->second["short"].asString(); shortopts += it->second["short"].asString();
if (it->second.isMember("arg")){shortopts += ":";} if (it->second.isMember("arg")){
shortopts += ":";
}
} }
if (it->second.isMember("short_off")){ if (it->second.isMember("short_off")){
shortopts += it->second["short_off"].asString(); shortopts += it->second["short_off"].asString();
if (it->second.isMember("arg")){shortopts += ":";} if (it->second.isMember("arg")){
shortopts += ":";
}
} }
if (it->second.isMember("long")){ if (it->second.isMember("long")){
longOpts[long_i].name = it->second["long"].asString().c_str(); longOpts[long_i].name = it->second["long"].asString().c_str();
longOpts[long_i].val = it->second["short"].asString()[0]; longOpts[long_i].val = it->second["short"].asString()[0];
if (it->second.isMember("arg")){longOpts[long_i].has_arg = 1;} if (it->second.isMember("arg")){
longOpts[long_i].has_arg = 1;
}
long_i++; long_i++;
} }
if (it->second.isMember("long_off")){ if (it->second.isMember("long_off")){
longOpts[long_i].name = it->second["long_off"].asString().c_str(); longOpts[long_i].name = it->second["long_off"].asString().c_str();
longOpts[long_i].val = it->second["short_off"].asString()[0]; longOpts[long_i].val = it->second["short_off"].asString()[0];
if (it->second.isMember("arg")){longOpts[long_i].has_arg = 1;} if (it->second.isMember("arg")){
longOpts[long_i].has_arg = 1;
}
long_i++; long_i++;
} }
if (it->second.isMember("arg_num") && !(it->second.isMember("value") && it->second["value"].size())){ if (it->second.isMember("arg_num") && !(it->second.isMember("value") && it->second["value"].size())){
@ -306,21 +337,33 @@ void Util::Config::addConnectorOptions(int port){
JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}");
stored_port["value"].append((long long int)port); stored_port["value"].append((long long int)port);
addOption("listen_port", stored_port); addOption("listen_port", stored_port);
addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); addOption("listen_interface",
addOption("username", JSON::fromString("{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); JSON::fromString(
addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); "{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}"));
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 } //addConnectorOptions
/// Gets directory the current executable is stored in. /// Gets directory the current executable is stored in.
std::string Util::getMyPath(){ std::string Util::getMyPath(){
char mypath[500]; char mypath[500];
int ret = readlink("/proc/self/exe", mypath, 500); int ret = readlink("/proc/self/exe", mypath, 500);
if (ret != -1){mypath[ret] = 0;}else{mypath[0] = 0;} if (ret != -1){
mypath[ret] = 0;
}else{
mypath[0] = 0;
}
std::string tPath = mypath; std::string tPath = mypath;
size_t slash = tPath.rfind('/'); size_t slash = tPath.rfind('/');
if (slash == std::string::npos){ if (slash == std::string::npos){
slash = tPath.rfind('\\'); slash = tPath.rfind('\\');
if (slash == std::string::npos){return "";} if (slash == std::string::npos){
return "";
}
} }
tPath.resize(slash + 1); tPath.resize(slash + 1);
return tPath; return tPath;

View file

@ -2,6 +2,11 @@
/// Contains generic function headers for managing configuration. /// Contains generic function headers for managing configuration.
#pragma once #pragma once
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "unknown"
#endif
#include <string> #include <string>
#include "json.h" #include "json.h"
@ -40,4 +45,5 @@ namespace Util{
/// Will turn the current process into a daemon. /// Will turn the current process into a daemon.
void Daemonize(); void Daemonize();
}; }
;

View file

View file

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

View file

@ -5,12 +5,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> //for memcmp #include <string.h> //for memcmp
#include <arpa/inet.h> //for htonl/ntohl #include <arpa/inet.h> //for htonl/ntohl
char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Header[] = "DTSC";
char DTSC::Magic_Packet[] = "DTPD"; char DTSC::Magic_Packet[] = "DTPD";
/// Initializes a DTSC::Stream with only one packet buffer. /// Initializes a DTSC::Stream with only one packet buffer.
DTSC::Stream::Stream(){ DTSC::Stream::Stream(){
datapointertype = DTSC::INVALID;
datapointer = 0; datapointer = 0;
buffercount = 1; buffercount = 1;
} }
@ -18,8 +18,11 @@ DTSC::Stream::Stream(){
/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. /// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers.
/// The actual buffer count may not at all times be the requested amount. /// The actual buffer count may not at all times be the requested amount.
DTSC::Stream::Stream(unsigned int rbuffers){ DTSC::Stream::Stream(unsigned int rbuffers){
datapointertype = DTSC::INVALID;
datapointer = 0; datapointer = 0;
if (rbuffers < 1){rbuffers = 1;} if (rbuffers < 1){
rbuffers = 1;
}
buffercount = rbuffers; buffercount = rbuffers;
} }
@ -39,7 +42,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
if (buffer.length() > 8){ if (buffer.length() > 8){
if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){
len = ntohl(((uint32_t *)buffer.c_str())[1]); len = ntohl(((uint32_t *)buffer.c_str())[1]);
if (buffer.length() < len+8){return false;} if (buffer.length() < len + 8){
return false;
}
unsigned int i = 0; unsigned int i = 0;
metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i);
buffer.erase(0, len + 8); buffer.erase(0, len + 8);
@ -47,7 +52,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
} }
if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){
len = ntohl(((uint32_t *)buffer.c_str())[1]); len = ntohl(((uint32_t *)buffer.c_str())[1]);
if (buffer.length() < len+8){return false;} if (buffer.length() < len + 8){
return false;
}
buffers.push_front(JSON::Value()); buffers.push_front(JSON::Value());
unsigned int i = 0; unsigned int i = 0;
buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i);
@ -59,13 +66,23 @@ bool DTSC::Stream::parsePacket(std::string & buffer){
} }
if (buffers.front().isMember("datatype")){ if (buffers.front().isMember("datatype")){
std::string tmp = buffers.front()["datatype"].asString(); std::string tmp = buffers.front()["datatype"].asString();
if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "video"){
if (tmp == "audio"){datapointertype = AUDIO;} datapointertype = VIDEO;
if (tmp == "meta"){datapointertype = META;} }
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} if (tmp == "audio"){
datapointertype = AUDIO;
}
if (tmp == "meta"){
datapointertype = META;
}
if (tmp == "pause_marker"){
datapointertype = PAUSEMARK;
}
} }
buffer.erase(0, len + 8); buffer.erase(0, len + 8);
while (buffers.size() > buffercount){buffers.pop_back();} while (buffers.size() > buffercount){
buffers.pop_back();
}
advanceRings(); advanceRings();
syncing = false; syncing = false;
return true; return true;
@ -97,7 +114,9 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
std::string header_bytes = buffer.copy(8); std::string header_bytes = buffer.copy(8);
if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){ if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){
len = ntohl(((uint32_t *)header_bytes.c_str())[1]); len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
if (!buffer.available(len+8)){return false;} if ( !buffer.available(len + 8)){
return false;
}
unsigned int i = 0; unsigned int i = 0;
std::string wholepacket = buffer.remove(len + 8); std::string wholepacket = buffer.remove(len + 8);
metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i);
@ -105,7 +124,9 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
} }
if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){
len = ntohl(((uint32_t *)header_bytes.c_str())[1]); len = ntohl(((uint32_t *)header_bytes.c_str())[1]);
if (!buffer.available(len+8)){return false;} if ( !buffer.available(len + 8)){
return false;
}
buffers.push_front(JSON::Value()); buffers.push_front(JSON::Value());
unsigned int i = 0; unsigned int i = 0;
std::string wholepacket = buffer.remove(len + 8); std::string wholepacket = buffer.remove(len + 8);
@ -118,12 +139,22 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){
} }
if (buffers.front().isMember("datatype")){ if (buffers.front().isMember("datatype")){
std::string tmp = buffers.front()["datatype"].asString(); std::string tmp = buffers.front()["datatype"].asString();
if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "video"){
if (tmp == "audio"){datapointertype = AUDIO;} datapointertype = VIDEO;
if (tmp == "meta"){datapointertype = META;} }
if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} if (tmp == "audio"){
datapointertype = AUDIO;
}
if (tmp == "meta"){
datapointertype = META;
}
if (tmp == "pause_marker"){
datapointertype = PAUSEMARK;
}
}
while (buffers.size() > buffercount){
buffers.pop_back();
} }
while (buffers.size() > buffercount){buffers.pop_back();}
advanceRings(); advanceRings();
syncing = false; syncing = false;
return true; return true;
@ -186,18 +217,29 @@ void DTSC::Stream::advanceRings(){
std::set<DTSC::Ring *>::iterator sit; std::set<DTSC::Ring *>::iterator sit;
for (sit = rings.begin(); sit != rings.end(); sit++){ for (sit = rings.begin(); sit != rings.end(); sit++){
( *sit)->b++; ( *sit)->b++;
if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} if (( *sit)->waiting){
if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;} ( *sit)->waiting = false;
( *sit)->b = 0;
}
if (( *sit)->starved || (( *sit)->b >= buffers.size())){
( *sit)->starved = true;
( *sit)->b = 0;
}
} }
for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ for (dit = keyframes.begin(); dit != keyframes.end(); dit++){
dit->b++; dit->b++;
if (dit->b >= buffers.size()){keyframes.erase(dit); break;} if (dit->b >= buffers.size()){
keyframes.erase(dit);
break;
}
} }
if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){
keyframes.push_front(DTSC::Ring(0)); keyframes.push_front(DTSC::Ring(0));
} }
//increase buffer size if no keyframes available //increase buffer size if no keyframes available
if ((buffercount > 1) && (keyframes.size() < 1)){buffercount++;} if ((buffercount > 1) && (keyframes.size() < 1)){
buffercount++;
}
} }
/// Constructs a new Ring, at the given buffer position. /// Constructs a new Ring, at the given buffer position.
@ -235,7 +277,9 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){
/// Drops all Ring classes that have been given out. /// Drops all Ring classes that have been given out.
DTSC::Stream::~Stream(){ DTSC::Stream::~Stream(){
std::set<DTSC::Ring *>::iterator sit; std::set<DTSC::Ring *>::iterator sit;
for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} for (sit = rings.begin(); sit != rings.end(); sit++){
delete ( *sit);
}
} }
/// Open a filename for DTSC reading/writing. /// Open a filename for DTSC reading/writing.
@ -300,11 +344,17 @@ long long int DTSC::File::addHeader(std::string & header){
long long int writePos = ftell(F); long long int writePos = ftell(F);
int hSize = htonl(header.size()); int hSize = htonl(header.size());
int ret = fwrite(DTSC::Magic_Header, 4, 1, F); //write header int ret = fwrite(DTSC::Magic_Header, 4, 1, F); //write header
if (ret != 1){return 0;} if (ret != 1){
return 0;
}
ret = fwrite((void*)( &hSize), 4, 1, F); //write size ret = fwrite((void*)( &hSize), 4, 1, F); //write size
if (ret != 1){return 0;} if (ret != 1){
return 0;
}
ret = fwrite(header.c_str(), header.size(), 1, F); //write contents ret = fwrite(header.c_str(), header.size(), 1, F); //write contents
if (ret != 1){return 0;} if (ret != 1){
return 0;
}
return writePos; //return position written at return writePos; //return position written at
} }
@ -423,13 +473,19 @@ void DTSC::File::seekNext(){
} }
/// Returns the byte positon of the start of the last packet that was read. /// Returns the byte positon of the start of the last packet that was read.
long long int DTSC::File::getLastReadPos(){return lastreadpos;} long long int DTSC::File::getLastReadPos(){
return lastreadpos;
}
/// Returns the internal buffer of the last read packet in raw binary format. /// Returns the internal buffer of the last read packet in raw binary format.
std::string & DTSC::File::getPacket(){return strbuffer;} std::string & DTSC::File::getPacket(){
return strbuffer;
}
/// Returns the internal buffer of the last read packet in JSON format. /// Returns the internal buffer of the last read packet in JSON format.
JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} JSON::Value & DTSC::File::getJSON(){
return jsonbuffer;
}
/// Attempts to seek to the given frame number within the file. /// Attempts to seek to the given frame number within the file.
/// Returns true if successful, false otherwise. /// Returns true if successful, false otherwise.
@ -444,7 +500,10 @@ bool DTSC::File::seek_frame(int frameno){
} }
}else{ }else{
for (int i = frameno; i >= 1; --i){ for (int i = frameno; i >= 1; --i){
if (frames.count(i) > 0){currframe = i; break;} if (frames.count(i) > 0){
currframe = i;
break;
}
} }
if (fseek(F, frames[currframe], SEEK_SET) == 0){ if (fseek(F, frames[currframe], SEEK_SET) == 0){
#if DEBUG >= 4 #if DEBUG >= 4
@ -452,7 +511,9 @@ bool DTSC::File::seek_frame(int frameno){
#endif #endif
while (currframe < frameno){ while (currframe < frameno){
seekNext(); seekNext();
if (jsonbuffer.isNull()){return false;} if (jsonbuffer.isNull()){
return false;
}
} }
seek_frame(frameno); seek_frame(frameno);
return true; return true;
@ -468,8 +529,13 @@ bool DTSC::File::seek_time(int ms){
currtime = 0; currtime = 0;
currframe = 1; currframe = 1;
for (it = msframes.begin(); it != msframes.end(); ++it){ for (it = msframes.begin(); it != msframes.end(); ++it){
if (it->second > ms){break;} if (it->second > ms){
if (it->second > currtime){currtime = it->second; currframe = it->first;} break;
}
if (it->second > currtime){
currtime = it->second;
currframe = it->first;
}
} }
if (fseek(F, frames[currframe], SEEK_SET) == 0){ if (fseek(F, frames[currframe], SEEK_SET) == 0){
#if DEBUG >= 4 #if DEBUG >= 4
@ -477,7 +543,9 @@ bool DTSC::File::seek_time(int ms){
#endif #endif
while (currtime < ms){ while (currtime < ms){
seekNext(); seekNext();
if (jsonbuffer.isNull()){return false;} if (jsonbuffer.isNull()){
return false;
}
} }
if (currtime > ms){ if (currtime > ms){
return seek_frame(currframe - 1); return seek_frame(currframe - 1);

View file

@ -12,8 +12,6 @@
#include "json.h" #include "json.h"
#include "socket.h" #include "socket.h"
/// Holds all DDVTECH Stream Container classes and parsers. /// Holds all DDVTECH Stream Container classes and parsers.
///length (int, length in seconds, if available) ///length (int, length in seconds, if available)
///video: ///video:
@ -91,8 +89,8 @@ namespace DTSC{
FILE * F; FILE * F;
unsigned long headerSize; unsigned long headerSize;
char buffer[4]; char buffer[4];
};//FileWriter };
//FileWriter
/// A part from the DTSC::Stream ringbuffer. /// A part from the DTSC::Stream ringbuffer.
/// Holds information about a buffer that will stay consistent /// Holds information about a buffer that will stay consistent
@ -134,4 +132,4 @@ namespace DTSC{
datatype datapointertype; datatype datapointertype;
unsigned int buffercount; unsigned int buffercount;
}; };
}; }

View file

@ -1,15 +1,19 @@
#include "filesystem.h" #include "filesystem.h"
Filesystem::Directory::Directory(std::string PathName, std::string BasePath){ Filesystem::Directory::Directory(std::string PathName, std::string BasePath){
MyBase = BasePath; MyBase = BasePath;
if( PathName[0] == '/' ) { PathName.erase(0,1); } if (PathName[0] == '/'){
if( BasePath[BasePath.size()-1] != '/' ) { BasePath += "/"; } PathName.erase(0, 1);
}
if (BasePath[BasePath.size() - 1] != '/'){
BasePath += "/";
}
MyPath = PathName; MyPath = PathName;
FillEntries(); FillEntries();
} }
Filesystem::Directory::~Directory( ) { } Filesystem::Directory::~Directory(){
}
void Filesystem::Directory::FillEntries(){ void Filesystem::Directory::FillEntries(){
fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str()); fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str());
@ -21,7 +25,7 @@ void Filesystem::Directory::FillEntries( ) {
ValidDir = false; ValidDir = false;
}else{ }else{
dirent * entry; dirent * entry;
while( entry = readdir( Dirp ) ) { while ((entry = readdir(Dirp))){
if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){ if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){
fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno)); fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno));
continue; continue;
@ -64,7 +68,9 @@ std::string Filesystem::Directory::LIST( std::vector<std::string> ActiveStreams
char datestring[256]; //For time localisation char datestring[256]; //For time localisation
std::string MyLoc = MyBase + MyPath; std::string MyLoc = MyBase + MyPath;
if( MyLoc[MyLoc.size()-1] != '/' ) { MyLoc += "/"; } if (MyLoc[MyLoc.size() - 1] != '/'){
MyLoc += "/";
}
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){ for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){
@ -73,19 +79,59 @@ std::string Filesystem::Directory::LIST( std::vector<std::string> ActiveStreams
fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]); 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); 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 ((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 << '-'; } if ((( *it).second.st_mode / 010000) == 4){
Converter << 'd';
}else{
Converter << '-';
}
MyPermissions = ((( *it).second.st_mode % 010000) / 0100); MyPermissions = ((( *it).second.st_mode % 010000) / 0100);
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } if (MyPermissions & 4){
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } Converter << 'r';
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } }else{
Converter << '-';
}
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
MyPermissions = ((( *it).second.st_mode % 0100) / 010); MyPermissions = ((( *it).second.st_mode % 0100) / 010);
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } if (MyPermissions & 4){
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } Converter << 'r';
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } }else{
Converter << '-';
}
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
MyPermissions = (( *it).second.st_mode % 010); MyPermissions = (( *it).second.st_mode % 010);
if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } if (MyPermissions & 4){
if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } Converter << 'r';
if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } }else{
Converter << '-';
}
if (MyPermissions & 2){
Converter << 'w';
}else{
Converter << '-';
}
if (MyPermissions & 1){
Converter << 'x';
}else{
Converter << '-';
}
Converter << ' '; Converter << ' ';
Converter << ( *it).second.st_nlink; Converter << ( *it).second.st_nlink;
Converter << ' '; Converter << ' ';
@ -147,7 +193,9 @@ std::string Filesystem::Directory::RETR( std::string Path ) {
} }
std::ifstream File; std::ifstream File;
File.open(FileName.c_str()); File.open(FileName.c_str());
while( File.good() ) { Result += File.get(); } while (File.good()){
Result += File.get();
}
File.close(); File.close();
return Result; return Result;
} }
@ -191,7 +239,9 @@ bool Filesystem::Directory::SimplifyPath( ) {
MyPath = ""; MyPath = "";
for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++){ for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++){
MyPath += ( *it); MyPath += ( *it);
if( it != ( TempPath.end() - 1 ) ) { MyPath += "/"; } if (it != (TempPath.end() - 1)){
MyPath += "/";
}
} }
if (MyVisible.find(MyPath) == MyVisible.end()){ if (MyVisible.find(MyPath) == MyVisible.end()){
MyVisible[MyPath] = S_ALL; MyVisible[MyPath] = S_ALL;

View file

@ -32,10 +32,7 @@ namespace Filesystem {
}; };
enum DIR_Show{ enum DIR_Show{
S_NONE = 0x00, S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03,
S_ACTIVE = 0x01,
S_INACTIVE = 0x02,
S_ALL = 0x03,
}; };
class Directory{ class Directory{
@ -65,5 +62,6 @@ namespace Filesystem {
std::map<std::string, struct stat> Entries; std::map<std::string, struct stat> Entries;
std::map<std::string, char> MyPermissions; std::map<std::string, char> MyPermissions;
std::map<std::string, char> MyVisible; std::map<std::string, char> MyVisible;
};//Directory Class };
};//Filesystem namespace //Directory Class
}//Filesystem namespace

View file

@ -1,9 +1,9 @@
/// \file flv_tag.cpp /// \file flv_tag.cpp
/// Holds all code for the FLV namespace. /// Holds all code for the FLV namespace.
#include "flv_tag.h"
#include "amf.h" #include "amf.h"
#include "rtmpchunks.h" #include "rtmpchunks.h"
#include "flv_tag.h"
#include <stdio.h> //for Tag::FileLoader #include <stdio.h> //for Tag::FileLoader
#include <unistd.h> //for Tag::FileLoader #include <unistd.h> //for Tag::FileLoader
#include <fcntl.h> //for Tag::FileLoader #include <fcntl.h> //for Tag::FileLoader
@ -57,17 +57,31 @@ bool FLV::Tag::needsInitData(){
switch (data[0]){ switch (data[0]){
case 0x09: case 0x09:
switch (data[11] & 0x0F){ switch (data[11] & 0x0F){
case 2: return true; break;//H263 requires init data case 2:
case 7: return true; break;//AVC requires init data return true;
default: return false; break;//other formats do not break; //H263 requires init data
case 7:
return true;
break; //AVC requires init data
default:
return false;
break; //other formats do not
} }
break; break;
case 0x08: case 0x08:
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x20: return false; break;//MP3 does not...? Unsure. case 0x20:
case 0xA0: return true; break;//AAC requires init data return false;
case 0xE0: return false; break;//MP38kHz does not...? break; //MP3 does not...? Unsure.
default: return false; break;//other formats do not case 0xA0:
return true;
break; //AAC requires init data
case 0xE0:
return false;
break; //MP38kHz does not...?
default:
return false;
break; //other formats do not
} }
break; break;
} }
@ -79,11 +93,15 @@ bool FLV::Tag::isInitData(){
switch (data[0]){ switch (data[0]){
case 0x09: case 0x09:
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x50: return true; break; case 0x50:
return true;
break;
} }
if ((data[11] & 0x0F) == 7){ if ((data[11] & 0x0F) == 7){
switch (data[12]){ switch (data[12]){
case 0: return true; break; case 0:
return true;
break;
} }
} }
break; break;
@ -106,62 +124,140 @@ std::string FLV::Tag::tagType(){
switch (data[0]){ switch (data[0]){
case 0x09: case 0x09:
switch (data[11] & 0x0F){ switch (data[11] & 0x0F){
case 1: R << "JPEG"; break; case 1:
case 2: R << "H263"; break; R << "JPEG";
case 3: R << "ScreenVideo1"; break; break;
case 4: R << "VP6"; break; case 2:
case 5: R << "VP6Alpha"; break; R << "H263";
case 6: R << "ScreenVideo2"; break; break;
case 7: R << "H264"; break; case 3:
default: R << "unknown"; break; R << "ScreenVideo1";
break;
case 4:
R << "VP6";
break;
case 5:
R << "VP6Alpha";
break;
case 6:
R << "ScreenVideo2";
break;
case 7:
R << "H264";
break;
default:
R << "unknown";
break;
} }
R << " video "; R << " video ";
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x10: R << "keyframe"; break; case 0x10:
case 0x20: R << "iframe"; break; R << "keyframe";
case 0x30: R << "disposableiframe"; break; break;
case 0x40: R << "generatedkeyframe"; break; case 0x20:
case 0x50: R << "videoinfo"; break; R << "iframe";
break;
case 0x30:
R << "disposableiframe";
break;
case 0x40:
R << "generatedkeyframe";
break;
case 0x50:
R << "videoinfo";
break;
} }
if ((data[11] & 0x0F) == 7){ if ((data[11] & 0x0F) == 7){
switch (data[12]){ switch (data[12]){
case 0: R << " header"; break; case 0:
case 1: R << " NALU"; break; R << " header";
case 2: R << " endofsequence"; break; break;
case 1:
R << " NALU";
break;
case 2:
R << " endofsequence";
break;
} }
} }
break; break;
case 0x08: case 0x08:
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x00: R << "linear PCM PE"; break; case 0x00:
case 0x10: R << "ADPCM"; break; R << "linear PCM PE";
case 0x20: R << "MP3"; break; break;
case 0x30: R << "linear PCM LE"; break; case 0x10:
case 0x40: R << "Nelly16kHz"; break; R << "ADPCM";
case 0x50: R << "Nelly8kHz"; break; break;
case 0x60: R << "Nelly"; break; case 0x20:
case 0x70: R << "G711A-law"; break; R << "MP3";
case 0x80: R << "G711mu-law"; break; break;
case 0x90: R << "reserved"; break; case 0x30:
case 0xA0: R << "AAC"; break; R << "linear PCM LE";
case 0xB0: R << "Speex"; break; break;
case 0xE0: R << "MP38kHz"; break; case 0x40:
case 0xF0: R << "DeviceSpecific"; break; R << "Nelly16kHz";
default: R << "unknown"; break; break;
case 0x50:
R << "Nelly8kHz";
break;
case 0x60:
R << "Nelly";
break;
case 0x70:
R << "G711A-law";
break;
case 0x80:
R << "G711mu-law";
break;
case 0x90:
R << "reserved";
break;
case 0xA0:
R << "AAC";
break;
case 0xB0:
R << "Speex";
break;
case 0xE0:
R << "MP38kHz";
break;
case 0xF0:
R << "DeviceSpecific";
break;
default:
R << "unknown";
break;
} }
switch (data[11] & 0x0C){ switch (data[11] & 0x0C){
case 0x0: R << " 5.5kHz"; break; case 0x0:
case 0x4: R << " 11kHz"; break; R << " 5.5kHz";
case 0x8: R << " 22kHz"; break; break;
case 0xC: R << " 44kHz"; break; case 0x4:
R << " 11kHz";
break;
case 0x8:
R << " 22kHz";
break;
case 0xC:
R << " 44kHz";
break;
} }
switch (data[11] & 0x02){ switch (data[11] & 0x02){
case 0: R << " 8bit"; break; case 0:
case 2: R << " 16bit"; break; R << " 8bit";
break;
case 2:
R << " 16bit";
break;
} }
switch (data[11] & 0x01){ switch (data[11] & 0x01){
case 0: R << " mono"; break; case 0:
case 1: R << " stereo"; break; R << " mono";
break;
case 1:
R << " stereo";
break;
} }
R << " audio"; R << " audio";
if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){
@ -198,7 +294,12 @@ void FLV::Tag::tagTime(unsigned int T){
/// The buffer length is initialized to 0, and later automatically /// The buffer length is initialized to 0, and later automatically
/// increased if neccesary. /// increased if neccesary.
FLV::Tag::Tag(){ FLV::Tag::Tag(){
len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; len = 0;
buf = 0;
data = 0;
isKeyframe = false;
done = true;
sofar = 0;
} //empty constructor } //empty constructor
/// Copy constructor, copies the contents of an existing tag. /// Copy constructor, copies the contents of an existing tag.
@ -219,13 +320,17 @@ FLV::Tag::Tag(const Tag& O){
isKeyframe = O.isKeyframe; isKeyframe = O.isKeyframe;
} //copy constructor } //copy constructor
/// Copy constructor from a RTMP chunk. /// Copy constructor from a RTMP chunk.
/// Copies the contents of a RTMP chunk into a valid FLV tag. /// Copies the contents of a RTMP chunk into a valid FLV tag.
/// Exactly the same as making a chunk by through the default (empty) constructor /// Exactly the same as making a chunk by through the default (empty) constructor
/// and then calling FLV::Tag::ChunkLoader with the chunk as argument. /// and then calling FLV::Tag::ChunkLoader with the chunk as argument.
FLV::Tag::Tag(const RTMPStream::Chunk& O){ FLV::Tag::Tag(const RTMPStream::Chunk& O){
len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; len = 0;
buf = 0;
data = 0;
isKeyframe = false;
done = true;
sofar = 0;
ChunkLoader(O); ChunkLoader(O);
} }
@ -258,13 +363,17 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
case DTSC::VIDEO: case DTSC::VIDEO:
len = S.lastData().length() + 16; len = S.lastData().length() + 16;
if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){ if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){
if (S.metadata["video"]["codec"].asString() == "H264"){len += 4;} if (S.metadata["video"]["codec"].asString() == "H264"){
len += 4;
}
} }
break; break;
case DTSC::AUDIO: case DTSC::AUDIO:
len = S.lastData().length() + 16; len = S.lastData().length() + 16;
if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){ if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){
if (S.metadata["audio"]["codec"].asString() == "AAC"){len += 1;} if (S.metadata["audio"]["codec"].asString() == "AAC"){
len += 1;
}
} }
break; break;
case DTSC::META: case DTSC::META:
@ -289,18 +398,32 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
memcpy(data + 12, S.lastData().c_str(), S.lastData().length()); memcpy(data + 12, S.lastData().c_str(), S.lastData().length());
}else{ }else{
memcpy(data + 16, S.lastData().c_str(), S.lastData().length()); memcpy(data + 16, S.lastData().c_str(), S.lastData().length());
if (S.getPacket().isMember("nalu")){data[12] = 1;}else{data[12] = 2;} if (S.getPacket().isMember("nalu")){
data[12] = 1;
}else{
data[12] = 2;
}
int offset = S.getPacket()["offset"].asInt(); int offset = S.getPacket()["offset"].asInt();
data[13] = (offset >> 16) & 0xFF; data[13] = (offset >> 16) & 0xFF;
data[14] = (offset >> 8) & 0XFF; data[14] = (offset >> 8) & 0XFF;
data[15] = offset & 0xFF; data[15] = offset & 0xFF;
} }
data[11] = 0; data[11] = 0;
if (S.metadata["video"]["codec"].asString() == "H264"){data[11] += 7;} if (S.metadata["video"]["codec"].asString() == "H264"){
if (S.metadata["video"]["codec"].asString() == "H263"){data[11] += 2;} data[11] += 7;
if (S.getPacket().isMember("keyframe")){data[11] += 0x10;} }
if (S.getPacket().isMember("interframe")){data[11] += 0x20;} if (S.metadata["video"]["codec"].asString() == "H263"){
if (S.getPacket().isMember("disposableframe")){data[11] += 0x30;} data[11] += 2;
}
if (S.getPacket().isMember("keyframe")){
data[11] += 0x10;
}
if (S.getPacket().isMember("interframe")){
data[11] += 0x20;
}
if (S.getPacket().isMember("disposableframe")){
data[11] += 0x30;
}
break; break;
case DTSC::AUDIO: { case DTSC::AUDIO: {
if ((unsigned int)len == S.lastData().length() + 16){ if ((unsigned int)len == S.lastData().length() + 16){
@ -310,8 +433,12 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
data[12] = 1; //raw AAC data, not sequence header data[12] = 1; //raw AAC data, not sequence header
} }
data[11] = 0; data[11] = 0;
if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} if (S.metadata["audio"]["codec"].asString() == "AAC"){
if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} data[11] += 0xA0;
}
if (S.metadata["audio"]["codec"].asString() == "MP3"){
data[11] += 0x20;
}
unsigned int datarate = S.metadata["audio"]["rate"].asInt(); unsigned int datarate = S.metadata["audio"]["rate"].asInt();
if (datarate >= 44100){ if (datarate >= 44100){
data[11] += 0x0C; data[11] += 0x0C;
@ -320,22 +447,34 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
}else if (datarate >= 11025){ }else if (datarate >= 11025){
data[11] += 0x04; data[11] += 0x04;
} }
if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} if (S.metadata["audio"]["size"].asInt() == 16){
if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} data[11] += 0x02;
}
if (S.metadata["audio"]["channels"].asInt() > 1){
data[11] += 0x01;
}
break; break;
} }
case DTSC::META: case DTSC::META:
memcpy(data + 11, S.lastData().c_str(), S.lastData().length()); memcpy(data + 11, S.lastData().c_str(), S.lastData().length());
break; break;
default: break; default:
break;
} }
} }
setLen(); setLen();
switch (S.lastType()){ switch (S.lastType()){
case DTSC::VIDEO: data[0] = 0x09; break; case DTSC::VIDEO:
case DTSC::AUDIO: data[0] = 0x08; break; data[0] = 0x09;
case DTSC::META: data[0] = 0x12; break; break;
default: break; case DTSC::AUDIO:
data[0] = 0x08;
break;
case DTSC::META:
data[0] = 0x12;
break;
default:
break;
} }
data[1] = ((len - 15) >> 16) & 0xFF; data[1] = ((len - 15) >> 16) & 0xFF;
data[2] = ((len - 15) >> 8) & 0xFF; data[2] = ((len - 15) >> 8) & 0xFF;
@ -425,8 +564,12 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){
memcpy(data + 13, S.metadata["audio"]["init"].asString().c_str(), len - 17); memcpy(data + 13, S.metadata["audio"]["init"].asString().c_str(), len - 17);
data[12] = 0; //AAC sequence header data[12] = 0; //AAC sequence header
data[11] = 0; data[11] = 0;
if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} if (S.metadata["audio"]["codec"].asString() == "AAC"){
if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} data[11] += 0xA0;
}
if (S.metadata["audio"]["codec"].asString() == "MP3"){
data[11] += 0x20;
}
unsigned int datarate = S.metadata["audio"]["rate"].asInt(); unsigned int datarate = S.metadata["audio"]["rate"].asInt();
if (datarate >= 44100){ if (datarate >= 44100){
data[11] += 0x0C; data[11] += 0x0C;
@ -435,8 +578,12 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){
}else if (datarate >= 11025){ }else if (datarate >= 11025){
data[11] += 0x04; data[11] += 0x04;
} }
if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} if (S.metadata["audio"]["size"].asInt() == 16){
if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} data[11] += 0x02;
}
if (S.metadata["audio"]["channels"].asInt() > 1){
data[11] += 0x01;
}
} }
setLen(); setLen();
data[0] = 0x08; data[0] = 0x08;
@ -536,7 +683,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){
int i = 0; int i = 0;
if (S.metadata.isMember("audio")){ if (S.metadata.isMember("audio")){
trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT));
trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(
AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
if (S.metadata["audio"]["codec"].asString() == "AAC"){ if (S.metadata["audio"]["codec"].asString() == "AAC"){
@ -549,7 +697,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){
} }
if (S.metadata.isMember("video")){ if (S.metadata.isMember("video")){
trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT));
trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(
AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER));
trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
if (S.metadata["video"]["codec"].asString() == "H264"){ if (S.metadata["video"]["codec"].asString() == "H264"){
@ -616,7 +765,6 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){
return true; return true;
} }
/// Helper function for FLV::MemLoader. /// Helper function for FLV::MemLoader.
/// This function will try to read count bytes from data buffer D into buffer. /// This function will try to read count bytes from data buffer D into buffer.
/// This function should be called repeatedly until true. /// This function should be called repeatedly until true.
@ -629,17 +777,24 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){
/// \param P The current position in the data buffer. Will be updated to reflect new position. /// \param P The current position in the data buffer. Will be updated to reflect new position.
/// \return True if count bytes are read succesfully, false otherwise. /// \return True if count bytes are read succesfully, false otherwise.
bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){
if (sofar >= count){return true;} if (sofar >= count){
return true;
}
int r = 0; int r = 0;
if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} if (P + (count - sofar) > S){
r = S - P;
}else{
r = count - sofar;
}
memcpy(buffer + sofar, D + P, r); memcpy(buffer + sofar, D + P, r);
P += r; P += r;
sofar += r; sofar += r;
if (sofar >= count){return true;} if (sofar >= count){
return true;
}
return false; return false;
} //Tag::MemReadUntil } //Tag::MemReadUntil
/// Try to load a tag from a data buffer in memory. /// Try to load a tag from a data buffer in memory.
/// This is a stateful function - if fed incorrect data, it will most likely never return true again! /// This is a stateful function - if fed incorrect data, it will most likely never return true again!
/// While this function returns false, the Tag might not contain valid data. /// While this function returns false, the Tag might not contain valid data.
@ -648,7 +803,10 @@ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & so
/// \param P The current position in the data buffer. Will be updated to reflect new position. /// \param P The current position in the data buffer. Will be updated to reflect new position.
/// \return True if a whole tag is succesfully read, false otherwise. /// \return True if a whole tag is succesfully read, false otherwise.
bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){
if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (buf < 15){
data = (char*)realloc(data, 15);
buf = 15;
}
if (done){ if (done){
//read a header //read a header
if (MemReadUntil(data, 11, sofar, D, S, P)){ if (MemReadUntil(data, 11, sofar, D, S, P)){
@ -658,14 +816,21 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){
if (FLV::check_header(data)){ if (FLV::check_header(data)){
sofar = 0; sofar = 0;
memcpy(FLV::Header, data, 13); memcpy(FLV::Header, data, 13);
}else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} }else{
FLV::Parse_Error = true;
Error_Str = "Invalid header received.";
return false;
}
} }
}else{ }else{
//if a tag header, calculate length and read tag body //if a tag header, calculate length and read tag body
len = data[3] + 15; len = data[3] + 15;
len += (data[2] << 8); len += (data[2] << 8);
len += (data[1] << 16); len += (data[1] << 16);
if (buf < len){data = (char*)realloc(data, len); buf = len;} if (buf < len){
data = (char*)realloc(data, len);
buf = len;
}
if (data[0] > 0x12){ if (data[0] > 0x12){
data[0] += 32; data[0] += 32;
FLV::Parse_Error = true; FLV::Parse_Error = true;
@ -681,7 +846,11 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){
//read tag body //read tag body
if (MemReadUntil(data, len, sofar, D, S, P)){ if (MemReadUntil(data, len, sofar, D, S, P)){
//calculate keyframeness, next time read header again, return true //calculate keyframeness, next time read header again, return true
if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){
isKeyframe = true;
}else{
isKeyframe = false;
}
done = true; done = true;
sofar = 0; sofar = 0;
return true; return true;
@ -690,7 +859,6 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){
return false; return false;
} //Tag::MemLoader } //Tag::MemLoader
/// Helper function for FLV::FileLoader. /// Helper function for FLV::FileLoader.
/// This function will try to read count bytes from file f into buffer. /// This function will try to read count bytes from file f into buffer.
/// This function should be called repeatedly until true. /// This function should be called repeatedly until true.
@ -700,12 +868,20 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){
/// \param f File to read from. /// \param f File to read from.
/// \return True if count bytes are read succesfully, false otherwise. /// \return True if count bytes are read succesfully, false otherwise.
bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f){ bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f){
if (sofar >= count){return true;} if (sofar >= count){
return true;
}
int r = 0; int r = 0;
r = fread(buffer + sofar, 1, count - sofar, f); r = fread(buffer + sofar, 1, count - sofar, f);
if (r < 0){FLV::Parse_Error = true; Error_Str = "File reading error."; return false;} if (r < 0){
FLV::Parse_Error = true;
Error_Str = "File reading error.";
return false;
}
sofar += r; sofar += r;
if (sofar >= count){return true;} if (sofar >= count){
return true;
}
return false; return false;
} }
@ -718,7 +894,10 @@ bool FLV::Tag::FileLoader(FILE * f){
int preflags = fcntl(fileno(f), F_GETFL, 0); int preflags = fcntl(fileno(f), F_GETFL, 0);
int postflags = preflags | O_NONBLOCK; int postflags = preflags | O_NONBLOCK;
fcntl(fileno(f), F_SETFL, postflags); fcntl(fileno(f), F_SETFL, postflags);
if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (buf < 15){
data = (char*)realloc(data, 15);
buf = 15;
}
if (done){ if (done){
//read a header //read a header
@ -729,14 +908,21 @@ bool FLV::Tag::FileLoader(FILE * f){
if (FLV::check_header(data)){ if (FLV::check_header(data)){
sofar = 0; sofar = 0;
memcpy(FLV::Header, data, 13); memcpy(FLV::Header, data, 13);
}else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} }else{
FLV::Parse_Error = true;
Error_Str = "Invalid header received.";
return false;
}
} }
}else{ }else{
//if a tag header, calculate length and read tag body //if a tag header, calculate length and read tag body
len = data[3] + 15; len = data[3] + 15;
len += (data[2] << 8); len += (data[2] << 8);
len += (data[1] << 16); len += (data[1] << 16);
if (buf < len){data = (char*)realloc(data, len); buf = len;} if (buf < len){
data = (char*)realloc(data, len);
buf = len;
}
if (data[0] > 0x12){ if (data[0] > 0x12){
data[0] += 32; data[0] += 32;
FLV::Parse_Error = true; FLV::Parse_Error = true;
@ -752,7 +938,11 @@ bool FLV::Tag::FileLoader(FILE * f){
//read tag body //read tag body
if (FileReadUntil(data, len, sofar, f)){ if (FileReadUntil(data, len, sofar, f)){
//calculate keyframeness, next time read header again, return true //calculate keyframeness, next time read header again, return true
if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){
isKeyframe = true;
}else{
isKeyframe = false;
}
done = true; done = true;
sofar = 0; sofar = 0;
fcntl(fileno(f), F_SETFL, preflags); fcntl(fileno(f), F_SETFL, preflags);
@ -772,17 +962,31 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){
AMF::Object * tmp = meta_in.getContentP(1); AMF::Object * tmp = meta_in.getContentP(1);
if (tmp->getContentP("videocodecid")){ if (tmp->getContentP("videocodecid")){
switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){
case 2: metadata["video"]["codec"] = "H263"; break; case 2:
case 4: metadata["video"]["codec"] = "VP6"; break; metadata["video"]["codec"] = "H263";
case 7: metadata["video"]["codec"] = "H264"; break; break;
default: metadata["video"]["codec"] = "?"; break; case 4:
metadata["video"]["codec"] = "VP6";
break;
case 7:
metadata["video"]["codec"] = "H264";
break;
default:
metadata["video"]["codec"] = "?";
break;
} }
} }
if (tmp->getContentP("audiocodecid")){ if (tmp->getContentP("audiocodecid")){
switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){
case 2: metadata["audio"]["codec"] = "MP3"; break; case 2:
case 10: metadata["audio"]["codec"] = "AAC"; break; metadata["audio"]["codec"] = "MP3";
default: metadata["audio"]["codec"] = "?"; break; break;
case 10:
metadata["audio"]["codec"] = "AAC";
break;
default:
metadata["audio"]["codec"] = "?";
break;
} }
} }
if (tmp->getContentP("width")){ if (tmp->getContentP("width")){
@ -814,14 +1018,28 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){
} }
} }
} }
if (!metadata.isMember("length")){metadata["length"] = 0;} if ( !metadata.isMember("length")){
metadata["length"] = 0;
}
if (metadata.isMember("video")){ if (metadata.isMember("video")){
if (!metadata["video"].isMember("width")){metadata["video"]["width"] = 0;} if ( !metadata["video"].isMember("width")){
if (!metadata["video"].isMember("height")){metadata["video"]["height"] = 0;} metadata["video"]["width"] = 0;
if (!metadata["video"].isMember("fpks")){metadata["video"]["fpks"] = 0;} }
if (!metadata["video"].isMember("bps")){metadata["video"]["bps"] = 0;} if ( !metadata["video"].isMember("height")){
if (!metadata["video"].isMember("keyms")){metadata["video"]["keyms"] = 0;} metadata["video"]["height"] = 0;
if (!metadata["video"].isMember("keyvar")){metadata["video"]["keyvar"] = 0;} }
if ( !metadata["video"].isMember("fpks")){
metadata["video"]["fpks"] = 0;
}
if ( !metadata["video"].isMember("bps")){
metadata["video"]["bps"] = 0;
}
if ( !metadata["video"].isMember("keyms")){
metadata["video"]["keyms"] = 0;
}
if ( !metadata["video"].isMember("keyvar")){
metadata["video"]["keyvar"] = 0;
}
} }
return pack_out; //empty return pack_out; //empty
} }
@ -839,35 +1057,59 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){
pack_out["time"] = tagTime(); pack_out["time"] = tagTime();
if ( !metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){ if ( !metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){
switch (audiodata & 0xF0){ switch (audiodata & 0xF0){
case 0x20: metadata["audio"]["codec"] = "MP3"; break; case 0x20:
case 0xA0: metadata["audio"]["codec"] = "AAC"; break; metadata["audio"]["codec"] = "MP3";
break;
case 0xA0:
metadata["audio"]["codec"] = "AAC";
break;
} }
} }
if ( !metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ if ( !metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){
switch (audiodata & 0x0C){ switch (audiodata & 0x0C){
case 0x0: metadata["audio"]["rate"] = 5512; break; case 0x0:
case 0x4: metadata["audio"]["rate"] = 11025; break; metadata["audio"]["rate"] = 5512;
case 0x8: metadata["audio"]["rate"] = 22050; break; break;
case 0xC: metadata["audio"]["rate"] = 44100; break; case 0x4:
metadata["audio"]["rate"] = 11025;
break;
case 0x8:
metadata["audio"]["rate"] = 22050;
break;
case 0xC:
metadata["audio"]["rate"] = 44100;
break;
} }
} }
if ( !metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){ if ( !metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){
switch (audiodata & 0x02){ switch (audiodata & 0x02){
case 0x0: metadata["audio"]["size"] = 8; break; case 0x0:
case 0x2: metadata["audio"]["size"] = 16; break; metadata["audio"]["size"] = 8;
break;
case 0x2:
metadata["audio"]["size"] = 16;
break;
} }
} }
if ( !metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){ if ( !metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){
switch (audiodata & 0x01){ switch (audiodata & 0x01){
case 0x0: metadata["audio"]["channels"] = 1; break; case 0x0:
case 0x1: metadata["audio"]["channels"] = 2; break; metadata["audio"]["channels"] = 1;
break;
case 0x1:
metadata["audio"]["channels"] = 2;
break;
} }
} }
if ((audiodata & 0xF0) == 0xA0){ if ((audiodata & 0xF0) == 0xA0){
if (len < 18){return JSON::Value();} if (len < 18){
return JSON::Value();
}
pack_out["data"] = std::string((char*)data + 13, (size_t)len - 17); pack_out["data"] = std::string((char*)data + 13, (size_t)len - 17);
}else{ }else{
if (len < 17){return JSON::Value();} if (len < 17){
return JSON::Value();
}
pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16); pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16);
} }
return pack_out; return pack_out;
@ -876,42 +1118,70 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){
char videodata = data[11]; char videodata = data[11];
if (needsInitData() && isInitData()){ if (needsInitData() && isInitData()){
if ((videodata & 0x0F) == 7){ if ((videodata & 0x0F) == 7){
if (len < 21){return JSON::Value();} if (len < 21){
return JSON::Value();
}
metadata["video"]["init"] = std::string((char*)data + 16, (size_t)len - 20); metadata["video"]["init"] = std::string((char*)data + 16, (size_t)len - 20);
}else{ }else{
if (len < 17){return JSON::Value();} if (len < 17){
return JSON::Value();
}
metadata["video"]["init"] = std::string((char*)data + 12, (size_t)len - 16); metadata["video"]["init"] = std::string((char*)data + 12, (size_t)len - 16);
} }
return pack_out; //skip rest of parsing, get next tag. return pack_out; //skip rest of parsing, get next tag.
} }
if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){ if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){
switch (videodata & 0x0F){ switch (videodata & 0x0F){
case 2: metadata["video"]["codec"] = "H263"; break; case 2:
case 4: metadata["video"]["codec"] = "VP6"; break; metadata["video"]["codec"] = "H263";
case 7: metadata["video"]["codec"] = "H264"; break; break;
case 4:
metadata["video"]["codec"] = "VP6";
break;
case 7:
metadata["video"]["codec"] = "H264";
break;
} }
} }
pack_out["datatype"] = "video"; pack_out["datatype"] = "video";
switch (videodata & 0xF0){ switch (videodata & 0xF0){
case 0x10: pack_out["keyframe"] = 1; break; case 0x10:
case 0x20: pack_out["interframe"] = 1; break; pack_out["keyframe"] = 1;
case 0x30: pack_out["disposableframe"] = 1; break; break;
case 0x40: pack_out["keyframe"] = 1; break; case 0x20:
case 0x50: return JSON::Value(); break;//the video info byte we just throw away - useless to us... pack_out["interframe"] = 1;
break;
case 0x30:
pack_out["disposableframe"] = 1;
break;
case 0x40:
pack_out["keyframe"] = 1;
break;
case 0x50:
return JSON::Value();
break; //the video info byte we just throw away - useless to us...
} }
pack_out["time"] = tagTime(); pack_out["time"] = tagTime();
if ((videodata & 0x0F) == 7){ if ((videodata & 0x0F) == 7){
switch (data[12]){ switch (data[12]){
case 1: pack_out["nalu"] = 1; break; case 1:
case 2: pack_out["nalu_end"] = 1; break; pack_out["nalu"] = 1;
break;
case 2:
pack_out["nalu_end"] = 1;
break;
} }
int offset = (data[13] << 16) + (data[14] << 8) + data[15]; int offset = (data[13] << 16) + (data[14] << 8) + data[15];
offset = (offset << 8) >> 8; offset = (offset << 8) >> 8;
pack_out["offset"] = offset; pack_out["offset"] = offset;
if (len < 21){return JSON::Value();} if (len < 21){
return JSON::Value();
}
pack_out["data"] = std::string((char*)data + 16, (size_t)len - 20); pack_out["data"] = std::string((char*)data + 16, (size_t)len - 20);
}else{ }else{
if (len < 17){return JSON::Value();} if (len < 17){
return JSON::Value();
}
pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16); pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16);
} }
return pack_out; return pack_out;

View file

@ -7,10 +7,11 @@
#include "json.h" #include "json.h"
#include <string> #include <string>
//forward declaration of RTMPStream::Chunk to avoid circular dependencies. //forward declaration of RTMPStream::Chunk to avoid circular dependencies.
namespace RTMPStream { namespace RTMPStream {
class Chunk; class Chunk;
}; }
/// This namespace holds all FLV-parsing related functionality. /// This namespace holds all FLV-parsing related functionality.
namespace FLV { namespace FLV {
@ -59,6 +60,7 @@ namespace FLV {
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val); void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val);
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val); void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val);
bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem); bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem);
};//Tag };
//Tag
};//FLV namespace }//FLV namespace

View file

@ -2,6 +2,7 @@
FTP::User::User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials){ FTP::User::User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials){
Conn = NewConnection; Conn = NewConnection;
MyPassivePort = 0;
USER = ""; USER = "";
PASS = ""; PASS = "";
MODE = MODE_STREAM; MODE = MODE_STREAM;
@ -35,32 +36,98 @@ FTP::User::User( Socket::Connection NewConnection, std::map<std::string,std::str
} }
} }
FTP::User::~User( ) { } FTP::User::~User(){
}
int FTP::User::ParseCommand(std::string Command){ int FTP::User::ParseCommand(std::string Command){
Commands ThisCmd = CMD_NOCMD; Commands ThisCmd = CMD_NOCMD;
if( Command.substr(0,4) == "NOOP" ) { ThisCmd = CMD_NOOP; Command.erase(0,5); } if (Command.substr(0, 4) == "NOOP"){
if( Command.substr(0,4) == "USER" ) { ThisCmd = CMD_USER; Command.erase(0,5); } ThisCmd = CMD_NOOP;
if( Command.substr(0,4) == "PASS" ) { ThisCmd = CMD_PASS; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "QUIT" ) { ThisCmd = CMD_QUIT; Command.erase(0,5); } }
if( Command.substr(0,4) == "PORT" ) { ThisCmd = CMD_PORT; Command.erase(0,5); } if (Command.substr(0, 4) == "USER"){
if( Command.substr(0,4) == "RETR" ) { ThisCmd = CMD_RETR; Command.erase(0,5); } ThisCmd = CMD_USER;
if( Command.substr(0,4) == "STOR" ) { ThisCmd = CMD_STOR; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "TYPE" ) { ThisCmd = CMD_TYPE; Command.erase(0,5); } }
if( Command.substr(0,4) == "MODE" ) { ThisCmd = CMD_MODE; Command.erase(0,5); } if (Command.substr(0, 4) == "PASS"){
if( Command.substr(0,4) == "STRU" ) { ThisCmd = CMD_STRU; Command.erase(0,5); } ThisCmd = CMD_PASS;
if( Command.substr(0,4) == "EPSV" ) { ThisCmd = CMD_EPSV; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "PASV" ) { ThisCmd = CMD_PASV; Command.erase(0,5); } }
if( Command.substr(0,4) == "LIST" ) { ThisCmd = CMD_LIST; Command.erase(0,5); } if (Command.substr(0, 4) == "QUIT"){
if( Command.substr(0,4) == "CDUP" ) { ThisCmd = CMD_CDUP; Command.erase(0,5); } ThisCmd = CMD_QUIT;
if( Command.substr(0,4) == "DELE" ) { ThisCmd = CMD_DELE; Command.erase(0,5); } Command.erase(0, 5);
if( Command.substr(0,4) == "RNFR" ) { ThisCmd = CMD_RNFR; Command.erase(0,5); } }
if( Command.substr(0,4) == "RNTO" ) { ThisCmd = CMD_RNTO; Command.erase(0,5); } if (Command.substr(0, 4) == "PORT"){
if( Command.substr(0,3) == "PWD" ) { ThisCmd = CMD_PWD; Command.erase(0,4); } ThisCmd = CMD_PORT;
if( Command.substr(0,3) == "CWD" ) { ThisCmd = CMD_CWD; Command.erase(0,4); } Command.erase(0, 5);
if( Command.substr(0,3) == "RMD" ) { ThisCmd = CMD_RMD; Command.erase(0,4); } }
if( Command.substr(0,3) == "MKD" ) { ThisCmd = CMD_MKD; Command.erase(0,4); } if (Command.substr(0, 4) == "RETR"){
if( ThisCmd != CMD_RNTO ) { RNFR = ""; } ThisCmd = CMD_RETR;
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){ switch (ThisCmd){
case CMD_NOOP: { case CMD_NOOP: {
return 200; //Command okay. return 200; //Command okay.
@ -69,14 +136,20 @@ int FTP::User::ParseCommand( std::string Command ) {
case CMD_USER: { case CMD_USER: {
USER = ""; USER = "";
PASS = ""; PASS = "";
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
USER = Command; USER = Command;
return 331; //User name okay, need password. return 331; //User name okay, need password.
break; break;
} }
case CMD_PASS: { case CMD_PASS: {
if( USER == "" ) { return 503; }//Bad sequence of commands if (USER == ""){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 503;
} //Bad sequence of commands
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
PASS = Command; PASS = Command;
if ( !LoggedIn()){ if ( !LoggedIn()){
USER = ""; USER = "";
@ -109,14 +182,20 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_PORT: { case CMD_PORT: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
} //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
PORT = atoi(Command.c_str()); PORT = atoi(Command.c_str());
return 200; //Command okay. return 200; //Command okay.
break; break;
} }
case CMD_EPSV: { case CMD_EPSV: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999); MyPassivePort = (rand() % 9999);
std::cout << ":" << MyPassivePort << "\n"; std::cout << ":" << MyPassivePort << "\n";
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true); Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
@ -124,7 +203,9 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_PASV: { case CMD_PASV: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999) + 49152; MyPassivePort = (rand() % 9999) + 49152;
std::cout << ":" << MyPassivePort << "\n"; std::cout << ":" << MyPassivePort << "\n";
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true); Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
@ -132,9 +213,15 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_RETR: { case CMD_RETR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_RETR ) ) { return 550; }//Access denied. } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_RETR)){
return 550;
} //Access denied.
std::cout << "Listening on :" << MyPassivePort << "\n"; std::cout << "Listening on :" << MyPassivePort << "\n";
Socket::Connection Connected = Passive.accept(); Socket::Connection Connected = Passive.accept();
if (Connected.connected()){ if (Connected.connected()){
@ -153,9 +240,15 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_STOR: { case CMD_STOR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_STOR ) ) { return 550; }//Access denied. } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_STOR)){
return 550;
} //Access denied.
std::cout << "Listening on :" << MyPassivePort << "\n"; std::cout << "Listening on :" << MyPassivePort << "\n";
Socket::Connection Connected = Passive.accept(); Socket::Connection Connected = Passive.accept();
if (Connected.connected()){ if (Connected.connected()){
@ -168,7 +261,8 @@ int FTP::User::ParseCommand( std::string Command ) {
} }
fprintf(stderr, "Reading STOR information\n"); fprintf(stderr, "Reading STOR information\n");
std::string Buffer; std::string Buffer;
while( Connected.spool() ) { } while (Connected.spool()){
}
/// \todo Comment me back in. ^_^ /// \todo Comment me back in. ^_^
//Buffer = Connected.Received(); //Buffer = Connected.Received();
MyDir.STOR(Command, Buffer); MyDir.STOR(Command, Buffer);
@ -176,22 +270,36 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_TYPE: { case CMD_TYPE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 && Command.size() != 3 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
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]){ switch (Command[0]){
case 'A': { case 'A': {
if (Command.size() > 1){ if (Command.size() > 1){
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. if (Command[1] != ' '){
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N'){
return 504;
} //Command not implemented for that parameter.
} }
TYPE = TYPE_ASCII_NONPRINT; TYPE = TYPE_ASCII_NONPRINT;
break; break;
} }
case 'I': { case 'I': {
if (Command.size() > 1){ if (Command.size() > 1){
if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. if (Command[1] != ' '){
if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N'){
return 504;
} //Command not implemented for that parameter.
} }
TYPE = TYPE_IMAGE_NONPRINT; TYPE = TYPE_IMAGE_NONPRINT;
break; break;
@ -205,18 +313,32 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_MODE: { case CMD_MODE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
if( Command[0] != 'S' ) { return 504; }//Command not implemented for that parameter. if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1){
return 501;
} //Syntax error in parameters or arguments.
if (Command[0] != 'S'){
return 504;
} //Command not implemented for that parameter.
MODE = MODE_STREAM; MODE = MODE_STREAM;
return 200; //Command okay. return 200; //Command okay.
break; break;
} }
case CMD_STRU: { case CMD_STRU: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. } //Not logged in.
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]){ switch (Command[0]){
case 'F': { case 'F': {
STRU = STRU_FILE; STRU = STRU_FILE;
@ -235,13 +357,19 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_PWD: { case CMD_PWD: {
if( !LoggedIn( ) ) { return 550; }//Not logged in. if ( !LoggedIn()){
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. return 550;
} //Not logged in.
if (Command != ""){
return 501;
} //Syntax error in parameters or arguments.
return 2570; //257 -- 0 to indicate PWD over MKD return 2570; //257 -- 0 to indicate PWD over MKD
break; break;
} }
case CMD_CWD: { case CMD_CWD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
return 530;
} //Not logged in.
Filesystem::Directory TmpDir = MyDir; Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CWD(Command)){ if (TmpDir.CWD(Command)){
if (TmpDir.IsDir()){ if (TmpDir.IsDir()){
@ -253,8 +381,12 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_CDUP: { case CMD_CDUP: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
} //Not logged in.
if (Command != ""){
return 501;
} //Syntax error in parameters or arguments.
Filesystem::Directory TmpDir = MyDir; Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CDUP()){ if (TmpDir.CDUP()){
if (TmpDir.IsDir()){ if (TmpDir.IsDir()){
@ -266,39 +398,73 @@ int FTP::User::ParseCommand( std::string Command ) {
break; break;
} }
case CMD_DELE: { case CMD_DELE: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.DELE( Command ) ) { return 550; } } //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.DELE(Command)){
return 550;
}
return 250; return 250;
break; break;
} }
case CMD_RMD: { case CMD_RMD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_RMD ) ) { return 550; } } //Not logged in.
if( !MyDir.DELE( Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_RMD)){
return 550;
}
if ( !MyDir.DELE(Command)){
return 550;
}
return 250; return 250;
break; break;
} }
case CMD_MKD: { case CMD_MKD: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( !MyDir.HasPermission( Filesystem::P_MKD ) ) { return 550; } } //Not logged in.
if( !MyDir.MKD( Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if ( !MyDir.HasPermission(Filesystem::P_MKD)){
return 550;
}
if ( !MyDir.MKD(Command)){
return 550;
}
return 2571; return 2571;
break; break;
} }
case CMD_RNFR: { case CMD_RNFR: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
} //Not logged in.
if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
RNFR = Command; RNFR = Command;
return 350; //Awaiting further information return 350; //Awaiting further information
} }
case CMD_RNTO: { case CMD_RNTO: {
if( !LoggedIn( ) ) { return 530; }//Not logged in. if ( !LoggedIn()){
if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. return 530;
if( RNFR == "" ) { return 503; } //Bad sequence of commands } //Not logged in.
if( !MyDir.Rename( RNFR, Command ) ) { return 550; } if (Command == ""){
return 501;
} //Syntax error in parameters or arguments.
if (RNFR == ""){
return 503;
} //Bad sequence of commands
if ( !MyDir.Rename(RNFR, Command)){
return 550;
}
return 250; return 250;
} }
default: { default: {
@ -309,7 +475,9 @@ int FTP::User::ParseCommand( std::string Command ) {
} }
bool FTP::User::LoggedIn(){ bool FTP::User::LoggedIn(){
if( USER == "" || PASS == "" ) { return false; } if (USER == "" || PASS == ""){
return false;
}
if ( !AllCredentials.size()){ if ( !AllCredentials.size()){
return true; return true;
} }

View file

@ -16,17 +16,18 @@ namespace FTP {
enum Mode{ enum Mode{
MODE_STREAM, MODE_STREAM,
};//FTP::Mode enumeration };
//FTP::Mode enumeration
enum Structure{ enum Structure{
STRU_FILE, STRU_FILE, STRU_RECORD,
STRU_RECORD, };
};//FTP::Structure enumeration //FTP::Structure enumeration
enum Type{ enum Type{
TYPE_ASCII_NONPRINT, TYPE_ASCII_NONPRINT, TYPE_IMAGE_NONPRINT,
TYPE_IMAGE_NONPRINT, };
};//FTP::Type enumeration //FTP::Type enumeration
enum Commands{ enum Commands{
CMD_NOCMD, CMD_NOCMD,
@ -51,7 +52,8 @@ namespace FTP {
CMD_MKD, CMD_MKD,
CMD_RNFR, CMD_RNFR,
CMD_RNTO, CMD_RNTO,
};//FTP::Commands enumeration };
//FTP::Commands enumeration
class User{ class User{
public: public:
@ -74,6 +76,7 @@ namespace FTP {
Filesystem::Directory MyDir; Filesystem::Directory MyDir;
std::string RNFR; std::string RNFR;
std::vector<std::string> ActiveStreams; std::vector<std::string> ActiveStreams;
};//FTP::User class };
//FTP::User class
};//FTP Namespace }//FTP Namespace

View file

@ -5,7 +5,9 @@
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing. /// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
/// All this constructor does is call HTTP::Parser::Clean(). /// All this constructor does is call HTTP::Parser::Clean().
HTTP::Parser::Parser(){Clean();} HTTP::Parser::Parser(){
Clean();
}
/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage. /// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage.
void HTTP::Parser::Clean(){ void HTTP::Parser::Clean(){
@ -27,7 +29,9 @@ void HTTP::Parser::Clean(){
std::string & HTTP::Parser::BuildRequest(){ std::string & HTTP::Parser::BuildRequest(){
/// \todo Include GET/POST variable parsing? /// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
protocol = "HTTP/1.0";
}
builder = method + " " + url + " " + protocol + "\r\n"; builder = method + " " + url + " " + protocol + "\r\n";
for (it = headers.begin(); it != headers.end(); it++){ for (it = headers.begin(); it != headers.end(); it++){
if (( *it).first != "" && ( *it).second != ""){ if (( *it).first != "" && ( *it).second != ""){
@ -47,7 +51,9 @@ std::string & HTTP::Parser::BuildRequest(){
std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){
/// \todo Include GET/POST variable parsing? /// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){
protocol = "HTTP/1.0";
}
builder = protocol + " " + code + " " + message + "\r\n"; builder = protocol + " " + code + " " + message + "\r\n";
for (it = headers.begin(); it != headers.end(); it++){ for (it = headers.begin(); it != headers.end(); it++){
if (( *it).first != "" && ( *it).second != ""){ if (( *it).first != "" && ( *it).second != ""){
@ -67,7 +73,11 @@ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message)
void HTTP::Parser::Trim(std::string & s){ void HTTP::Parser::Trim(std::string & s){
size_t startpos = s.find_first_not_of(" \t"); size_t startpos = s.find_first_not_of(" \t");
size_t endpos = s.find_last_not_of(" \t"); size_t endpos = s.find_last_not_of(" \t");
if ((std::string::npos == startpos) || (std::string::npos == endpos)){s = "";}else{s = s.substr(startpos, endpos-startpos+1);} if ((std::string::npos == startpos) || (std::string::npos == endpos)){
s = "";
}else{
s = s.substr(startpos, endpos - startpos + 1);
}
} }
/// Function that sets the body of a response or request, along with the correct Content-Length header. /// Function that sets the body of a response or request, along with the correct Content-Length header.
@ -96,9 +106,13 @@ std::string HTTP::Parser::getUrl(){
} }
/// Returns header i, if set. /// Returns header i, if set.
std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} std::string HTTP::Parser::GetHeader(std::string i){
return headers[i];
}
/// Returns POST variable i, if set. /// Returns POST variable i, if set.
std::string HTTP::Parser::GetVar(std::string i){return vars[i];} std::string HTTP::Parser::GetVar(std::string i){
return vars[i];
}
/// Sets header i to string value v. /// Sets header i to string value v.
void HTTP::Parser::SetHeader(std::string i, std::string v){ void HTTP::Parser::SetHeader(std::string i, std::string v){
@ -153,28 +167,38 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){
}else{ }else{
HTTPbuffer.erase(0, f + 1); HTTPbuffer.erase(0, f + 1);
} }
while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));} while (tmpA.find('\r') != std::string::npos){
tmpA.erase(tmpA.find('\r'));
}
if ( !seenReq){ if ( !seenReq){
seenReq = true; seenReq = true;
f = tmpA.find(' '); f = tmpA.find(' ');
if (f != std::string::npos){ if (f != std::string::npos){
method = tmpA.substr(0, f); tmpA.erase(0, f+1); method = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
f = tmpA.find(' '); f = tmpA.find(' ');
if (f != std::string::npos){ if (f != std::string::npos){
url = tmpA.substr(0, f); tmpA.erase(0, f+1); url = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
protocol = tmpA; protocol = tmpA;
if (url.find('?') != std::string::npos){ if (url.find('?') != std::string::npos){
parseVars(url.substr(url.find('?') + 1)); //parse GET variables parseVars(url.substr(url.find('?') + 1)); //parse GET variables
} }
}else{seenReq = false;} }else{
}else{seenReq = false;} seenReq = false;
}
}else{
seenReq = false;
}
}else{ }else{
if (tmpA.size() == 0){ if (tmpA.size() == 0){
seenHeaders = true; seenHeaders = true;
body.clear(); body.clear();
if (GetHeader("Content-Length") != ""){ if (GetHeader("Content-Length") != ""){
length = atoi(GetHeader("Content-Length").c_str()); length = atoi(GetHeader("Content-Length").c_str());
if (body.capacity() < length){body.reserve(length);} if (body.capacity() < length){
body.reserve(length);
}
} }
}else{ }else{
f = tmpA.find(':'); f = tmpA.find(':');
@ -267,7 +291,11 @@ std::string HTTP::Parser::urlunescape(const std::string & in){
} }
out += tmp; out += tmp;
}else{ }else{
if (in[i] == '+'){out += ' ';}else{out += in[i];} if (in[i] == '+'){
out += ' ';
}else{
out += in[i];
}
} }
} }
return out; return out;
@ -284,7 +312,8 @@ std::string HTTP::Parser::urlencode(const std::string &c){
std::string escaped = ""; std::string escaped = "";
int max = c.length(); int max = c.length();
for (int i = 0; i < max; i++){ for (int i = 0; i < max; i++){
if (('0'<=c[i] && c[i]<='9') || ('a'<=c[i] && c[i]<='z') || ('A'<=c[i] && c[i]<='Z') || (c[i]=='~' || c[i]=='!' || c[i]=='*' || c[i]=='(' || c[i]==')' || c[i]=='\'')){ if (('0' <= c[i] && c[i] <= '9') || ('a' <= c[i] && c[i] <= 'z') || ('A' <= c[i] && c[i] <= 'Z')
|| (c[i] == '~' || c[i] == '!' || c[i] == '*' || c[i] == '(' || c[i] == ')' || c[i] == '\'')){
escaped.append( &c[i], 1); escaped.append( &c[i], 1);
}else{ }else{
escaped.append("%"); escaped.append("%");

View file

@ -45,5 +45,7 @@ namespace HTTP{
void Trim(std::string & s); void Trim(std::string & s);
static int unhex(char c); static int unhex(char c);
static std::string hex(char dec); static std::string hex(char dec);
};//HTTP::Parser class };
};//HTTP namespace //HTTP::Parser class
}//HTTP namespace

View file

@ -7,7 +7,6 @@
#include <stdint.h> //for uint64_t #include <stdint.h> //for uint64_t
#include <string.h> //for memcpy #include <string.h> //for memcpy
#include <arpa/inet.h> //for htonl #include <arpa/inet.h> //for htonl
int JSON::Value::c2hex(int c){ int JSON::Value::c2hex(int c){
if (c >= '0' && c <= '9') return c - '0'; if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10;
@ -15,7 +14,6 @@ int JSON::Value::c2hex(int c){
return 0; return 0;
} }
std::string JSON::Value::read_string(int separator, std::istream & fromstream){ std::string JSON::Value::read_string(int separator, std::istream & fromstream){
std::string out; std::string out;
bool escaped = false; bool escaped = false;
@ -27,17 +25,28 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){
} }
if (escaped){ if (escaped){
switch (c){ switch (c){
case 'b': out += '\b'; break; case 'b':
case 'f': out += '\f'; break; out += '\b';
case 'n': out += '\n'; break; break;
case 'r': out += '\r'; break; case 'f':
case 't': out += '\t'; break; out += '\f';
break;
case 'n':
out += '\n';
break;
case 'r':
out += '\r';
break;
case 't':
out += '\t';
break;
case 'u': { case 'u': {
int d1 = fromstream.get(); int d1 = fromstream.get();
int d2 = fromstream.get(); int d2 = fromstream.get();
int d3 = fromstream.get(); int d3 = fromstream.get();
int d4 = fromstream.get(); int d4 = fromstream.get();
c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16);
break;
} }
default: default:
out += (char)c; out += (char)c;
@ -58,14 +67,30 @@ std::string JSON::Value::string_escape(std::string val){
std::string out = "\""; std::string out = "\"";
for (unsigned int i = 0; i < val.size(); ++i){ for (unsigned int i = 0; i < val.size(); ++i){
switch (val[i]){ switch (val[i]){
case '"': out += "\\\""; break; case '"':
case '\\': out += "\\\\"; break; out += "\\\"";
case '\n': out += "\\n"; break; break;
case '\b': out += "\\b"; break; case '\\':
case '\f': out += "\\f"; break; out += "\\\\";
case '\r': out += "\\r"; break; break;
case '\t': out += "\\t"; break; case '\n':
default: out += val[i]; out += "\\n";
break;
case '\b':
out += "\\b";
break;
case '\f':
out += "\\f";
break;
case '\r':
out += "\\r";
break;
case '\t':
out += "\\t";
break;
default:
out += val[i];
break;
} }
} }
out += "\""; out += "\"";
@ -76,9 +101,15 @@ std::string JSON::Value::string_escape(std::string val){
void JSON::Value::skipToEnd(std::istream & fromstream){ void JSON::Value::skipToEnd(std::istream & fromstream){
while (fromstream.good()){ while (fromstream.good()){
char peek = fromstream.peek(); char peek = fromstream.peek();
if (peek == ','){return;} if (peek == ','){
if (peek == ']'){return;} return;
if (peek == '}'){return;} }
if (peek == ']'){
return;
}
if (peek == '}'){
return;
}
peek = fromstream.get(); peek = fromstream.get();
} }
} }
@ -146,11 +177,15 @@ JSON::Value::Value(std::istream & fromstream){
} }
break; break;
case '}': case '}':
if (reading_object){c = fromstream.get();} if (reading_object){
c = fromstream.get();
}
return; return;
break; break;
case ']': case ']':
if (reading_array){c = fromstream.get();} if (reading_array){
c = fromstream.get();
}
return; return;
break; break;
case 't': case 't':
@ -185,12 +220,14 @@ JSON::Value::Value(std::istream & fromstream){
JSON::Value::Value(const std::string & val){ JSON::Value::Value(const std::string & val){
myType = STRING; myType = STRING;
strVal = val; strVal = val;
intVal = 0;
} }
/// Sets this JSON::Value to the given string. /// Sets this JSON::Value to the given string.
JSON::Value::Value(const char * val){ JSON::Value::Value(const char * val){
myType = STRING; myType = STRING;
strVal = val; strVal = val;
intVal = 0;
} }
/// Sets this JSON::Value to the given integer. /// Sets this JSON::Value to the given integer.
@ -202,14 +239,24 @@ JSON::Value::Value(long long int val){
/// Compares a JSON::Value to another for equality. /// Compares a JSON::Value to another for equality.
bool JSON::Value::operator==(const JSON::Value & rhs) const{ bool JSON::Value::operator==(const JSON::Value & rhs) const{
if (myType != rhs.myType) return false; if (myType != rhs.myType) return false;
if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;} if (myType == INTEGER || myType == BOOL){
if (myType == STRING){return strVal == rhs.strVal;} return intVal == rhs.intVal;
if (myType == EMPTY){return true;} }
if (myType == STRING){
return strVal == rhs.strVal;
}
if (myType == EMPTY){
return true;
}
if (myType == OBJECT){ if (myType == OBJECT){
if (objVal.size() != rhs.objVal.size()) return false; if (objVal.size() != rhs.objVal.size()) return false;
for (std::map<std::string, Value>::const_iterator it = objVal.begin(); it != objVal.end(); ++it){ for (std::map<std::string, Value>::const_iterator it = objVal.begin(); it != objVal.end(); ++it){
if (!rhs.isMember(it->first)){return false;} if ( !rhs.isMember(it->first)){
if (it->second != rhs.objVal.find(it->first)->second){return false;} return false;
}
if (it->second != rhs.objVal.find(it->first)->second){
return false;
}
} }
return true; return true;
} }
@ -217,7 +264,9 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{
if (arrVal.size() != rhs.arrVal.size()) return false; if (arrVal.size() != rhs.arrVal.size()) return false;
int i = 0; int i = 0;
for (std::deque<Value>::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){ for (std::deque<Value>::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){
if (*it != rhs.arrVal[i]){return false;} if ( *it != rhs.arrVal[i]){
return false;
}
i++; i++;
} }
return true; return true;
@ -280,7 +329,6 @@ JSON::Value::operator long long int(){
return 0; return 0;
} }
/// Automatic conversion to std::string. /// Automatic conversion to std::string.
/// Returns the raw string value if available, otherwise calls toString(). /// Returns the raw string value if available, otherwise calls toString().
JSON::Value::operator std::string(){ JSON::Value::operator std::string(){
@ -298,12 +346,24 @@ JSON::Value::operator std::string(){
/// Automatic conversion to bool. /// Automatic conversion to bool.
/// Returns true if there is anything meaningful stored into this value. /// Returns true if there is anything meaningful stored into this value.
JSON::Value::operator bool(){ JSON::Value::operator bool(){
if (myType == STRING){return strVal != "";} if (myType == STRING){
if (myType == INTEGER){return intVal != 0;} return strVal != "";
if (myType == BOOL){return intVal != 0;} }
if (myType == OBJECT){return size() > 0;} if (myType == INTEGER){
if (myType == ARRAY){return size() > 0;} return intVal != 0;
if (myType == EMPTY){return false;} }
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... return false; //unimplemented? should never happen...
} }
@ -320,7 +380,6 @@ const bool JSON::Value::asBool(){
return (bool) *this; return (bool) *this;
} }
/// Retrieves or sets the JSON::Value at this position in the object. /// Retrieves or sets the JSON::Value at this position in the object.
/// Converts destructively to object if not already an object. /// Converts destructively to object if not already an object.
JSON::Value & JSON::Value::operator[](const std::string i){ JSON::Value & JSON::Value::operator[](const std::string i){
@ -362,10 +421,14 @@ std::string JSON::Value::toPacked(){
if (isInt() || isNull() || isBool()){ if (isInt() || isNull() || isBool()){
r += 0x01; r += 0x01;
uint64_t numval = intVal; uint64_t numval = intVal;
r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); r += *(((char*) &numval) + 7);
r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); r += *(((char*) &numval) + 6);
r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); r += *(((char*) &numval) + 5);
r += *(((char*)&numval)+1); r += *(((char*)&numval)); r += *(((char*) &numval) + 4);
r += *(((char*) &numval) + 3);
r += *(((char*) &numval) + 2);
r += *(((char*) &numval) + 1);
r += *(((char*) &numval));
} }
if (isString()){ if (isString()){
r += 0x02; r += 0x02;
@ -385,7 +448,9 @@ std::string JSON::Value::toPacked(){
r += it->second.toPacked(); r += it->second.toPacked();
} }
} }
r += (char)0x0; r += (char)0x0; r += (char)0xEE; r += (char)0x0;
r += (char)0x0;
r += (char)0xEE;
strVal.clear(); strVal.clear();
} }
if (isArray()){ if (isArray()){
@ -393,11 +458,14 @@ std::string JSON::Value::toPacked(){
for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){ for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){
r += it->toPacked(); r += it->toPacked();
} }
r += (char)0x0; r += (char)0x0; r += (char)0xEE; r += (char)0x0;
r += (char)0x0;
r += (char)0xEE;
} }
return r; return r;
};//toPacked }
;
//toPacked
/// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header.
/// Non-object-types will print an error to stderr and return an empty string. /// Non-object-types will print an error to stderr and return an empty string.
@ -429,7 +497,6 @@ std::string & JSON::Value::toNetPacked(){
return strVal; return strVal;
} }
/// Converts this JSON::Value to valid JSON notation and returns it. /// Converts this JSON::Value to valid JSON notation and returns it.
/// Makes absolutely no attempts to pretty-print anything. :-) /// Makes absolutely no attempts to pretty-print anything. :-)
std::string JSON::Value::toString(){ std::string JSON::Value::toString(){
@ -449,7 +516,9 @@ std::string JSON::Value::toString(){
if (arrVal.size() > 0){ if (arrVal.size() > 0){
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
tmp += it->toString(); tmp += it->toString();
if (it + 1 != ArrEnd()){tmp += ",";} if (it + 1 != ArrEnd()){
tmp += ",";
}
} }
} }
tmp += "]"; tmp += "]";
@ -464,7 +533,9 @@ std::string JSON::Value::toString(){
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
tmp2 += "\"" + it2->first + "\":"; tmp2 += "\"" + it2->first + "\":";
tmp2 += it2->second.toString(); tmp2 += it2->second.toString();
if (it2 != it3){tmp2 += ",";} if (it2 != it3){
tmp2 += ",";
}
} }
} }
tmp2 += "}"; tmp2 += "}";
@ -502,7 +573,9 @@ std::string JSON::Value::toPrettyString(int indentation){
std::string tmp = "[\n"; std::string tmp = "[\n";
for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){
tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2); tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2);
if (it + 1 != ArrEnd()){tmp += ",\n";} if (it + 1 != ArrEnd()){
tmp += ",\n";
}
} }
tmp += "\n" + std::string(indentation, ' ') + "]"; tmp += "\n" + std::string(indentation, ' ') + "]";
return tmp; return tmp;
@ -519,7 +592,9 @@ std::string JSON::Value::toPrettyString(int indentation){
for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){
tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":"; tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":";
tmp2 += it2->second.toPrettyString(indentation + 2); tmp2 += it2->second.toPrettyString(indentation + 2);
if (it2 != it3){tmp2 += ",\n";} if (it2 != it3){
tmp2 += ",\n";
}
} }
tmp2 += "\n" + std::string(indentation, ' ') + "}"; tmp2 += "\n" + std::string(indentation, ' ') + "}";
return tmp2; return tmp2;
@ -563,11 +638,15 @@ void JSON::Value::prepend(const JSON::Value & rhs){
/// given size. /// given size.
void JSON::Value::shrink(unsigned int size){ void JSON::Value::shrink(unsigned int size){
if (myType == ARRAY){ if (myType == ARRAY){
while (arrVal.size() > size){arrVal.pop_front();} while (arrVal.size() > size){
arrVal.pop_front();
}
return; return;
} }
if (myType == OBJECT){ if (myType == OBJECT){
while (objVal.size() > size){objVal.erase(objVal.begin());} while (objVal.size() > size){
objVal.erase(objVal.begin());
}
return; return;
} }
} }
@ -688,13 +767,15 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne
i += 9; //skip 8(an uint64_t)+1 forwards i += 9; //skip 8(an uint64_t)+1 forwards
uint64_t * d = (uint64_t*)tmpdbl; uint64_t * d = (uint64_t*)tmpdbl;
return JSON::Value((long long int) *d); return JSON::Value((long long int) *d);
} break; }
break;
case 0x02: { //string 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 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 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 i += tmpi + 5; //skip length+size+1 forwards
return JSON::Value(tmpstr); return JSON::Value(tmpstr);
} break; }
break;
case 0xFF: //also object case 0xFF: //also object
case 0xE0: { //object case 0xE0: { //object
++i; ++i;
@ -707,7 +788,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne
} }
i += 3; //skip 0x0000EE i += 3; //skip 0x0000EE
return ret; return ret;
} break; }
break;
case 0x0A: { //array case 0x0A: { //array
JSON::Value ret; JSON::Value ret;
++i; ++i;
@ -716,7 +798,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne
} }
i += 3; //skip 0x0000EE i += 3; //skip 0x0000EE
return ret; return ret;
} break; }
break;
} }
#if DEBUG >= 2 #if DEBUG >= 2
fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]);

View file

@ -7,15 +7,20 @@
#include <istream> #include <istream>
//empty definition of DTSC::Stream so it can be a friend. //empty definition of DTSC::Stream so it can be a friend.
namespace DTSC{class Stream;} namespace DTSC {
class Stream;
}
/// JSON-related classes and functions /// JSON-related classes and functions
namespace JSON { namespace JSON {
/// Lists all types of JSON::Value. /// Lists all types of JSON::Value.
enum ValueType{ EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT }; enum ValueType{
EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT
};
class Value;//forward declaration for below typedef class Value;
//forward declaration for below typedef
typedef std::map<std::string, Value>::iterator ObjIter; typedef std::map<std::string, Value>::iterator ObjIter;
typedef std::deque<Value>::iterator ArrIter; typedef std::deque<Value>::iterator ArrIter;
@ -92,4 +97,4 @@ namespace JSON{
Value fromString(std::string json); Value fromString(std::string json);
Value fromFile(std::string filename); Value fromFile(std::string filename);
}; }

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ namespace MP4{
Box(char * datapointer = 0, bool manage = true); Box(char * datapointer = 0, bool manage = true);
~Box(); ~Box();
std::string getType(); std::string getType();
bool isType( char* boxType ); bool isType(const char* boxType);
bool read(std::string & newData); bool read(std::string & newData);
long long int boxedSize(); long long int boxedSize();
long long int payloadSize(); long long int payloadSize();
@ -52,14 +52,16 @@ namespace MP4{
int data_size; ///< Currently reserved size int data_size; ///< Currently reserved size
bool managed; ///< If false, will not attempt to resize/free the data pointer. bool managed; ///< If false, will not attempt to resize/free the data pointer.
int payloadOffset; ///<The offset of the payload with regards to the data int payloadOffset; ///<The offset of the payload with regards to the data
};//Box Class };
//Box Class
struct afrt_runtable{ struct afrt_runtable{
long firstFragment; long firstFragment;
long long int firstTimestamp; long long int firstTimestamp;
long duration; long duration;
long discontinuity; long discontinuity;
};//fragmentRun };
//fragmentRun
/// AFRT Box class /// AFRT Box class
class AFRT: public Box{ class AFRT: public Box{
@ -78,7 +80,8 @@ namespace MP4{
void setFragmentRun(afrt_runtable newRun, long no); void setFragmentRun(afrt_runtable newRun, long no);
afrt_runtable getFragmentRun(long no); afrt_runtable getFragmentRun(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//AFRT Box };
//AFRT Box
struct asrt_runtable{ struct asrt_runtable{
long firstSegment; long firstSegment;
@ -100,7 +103,8 @@ namespace MP4{
void setSegmentRun(long firstSegment, long fragmentsPerSegment, long no); void setSegmentRun(long firstSegment, long fragmentsPerSegment, long no);
asrt_runtable getSegmentRun(long no); asrt_runtable getSegmentRun(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//ASRT Box };
//ASRT Box
/// ABST Box class /// ABST Box class
class ABST: public Box{ class ABST: public Box{
@ -143,7 +147,8 @@ namespace MP4{
void setFragmentRunTable(AFRT & table, long no); void setFragmentRunTable(AFRT & table, long no);
AFRT & getFragmentRunTable(long no); AFRT & getFragmentRunTable(long no);
std::string toPrettyString(long indent = 0); std::string toPrettyString(long indent = 0);
};//ABST Box };
//ABST Box
class MFHD: public Box{ class MFHD: public Box{
public: public:
@ -151,7 +156,8 @@ namespace MP4{
void setSequenceNumber(long newSequenceNumber); void setSequenceNumber(long newSequenceNumber);
long getSequenceNumber(); long getSequenceNumber();
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//MFHD Box };
//MFHD Box
class MOOF: public Box{ class MOOF: public Box{
public: public:
@ -160,7 +166,8 @@ namespace MP4{
void setContent(Box & newContent, long no); void setContent(Box & newContent, long no);
Box & getContent(long no); Box & getContent(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//MOOF Box };
//MOOF Box
class TRAF: public Box{ class TRAF: public Box{
public: public:
@ -169,7 +176,8 @@ namespace MP4{
void setContent(Box & newContent, long no); void setContent(Box & newContent, long no);
Box & getContent(long no); Box & getContent(long no);
std::string toPrettyString(int indent = 0); std::string toPrettyString(int indent = 0);
};//TRAF Box };
//TRAF Box
struct trunSampleInformation{ struct trunSampleInformation{
long sampleDuration; long sampleDuration;
@ -309,4 +317,5 @@ namespace MP4{
void setValue(long newValue, size_t index); void setValue(long newValue, size_t index);
long getValue(size_t index); long getValue(size_t index);
}; };
}; }

View file

@ -25,7 +25,9 @@ bool Util::Procs::handler_set = false;
/// Used internally to capture child signals and update plist. /// Used internally to capture child signals and update plist.
void Util::Procs::childsig_handler(int signum){ void Util::Procs::childsig_handler(int signum){
if (signum != SIGCHLD){return;} if (signum != SIGCHLD){
return;
}
int status; int status;
pid_t ret = waitpid( -1, &status, WNOHANG); pid_t ret = waitpid( -1, &status, WNOHANG);
if (ret == 0){ //ignore, would block otherwise if (ret == 0){ //ignore, would block otherwise
@ -41,7 +43,9 @@ void Util::Procs::childsig_handler(int signum){
exitcode = WEXITSTATUS(status); exitcode = WEXITSTATUS(status);
}else if (WIFSIGNALED(status)){ }else if (WIFSIGNALED(status)){
exitcode = -WTERMSIG(status); exitcode = -WTERMSIG(status);
}else{/* not possible */return;} }else{/* not possible */
return;
}
#if DEBUG >= 1 #if DEBUG >= 1
std::string pname = plist[ret]; std::string pname = plist[ret];
@ -84,7 +88,9 @@ void Util::Procs::runCmd(std::string & cmd){
++i; ++i;
args[i] = tmp2; args[i] = tmp2;
} }
if (i == 20){args[20] = 0;} if (i == 20){
args[20] = 0;
}
//execute the command //execute the command
execvp(args[0], args); execvp(args[0], args);
#if DEBUG >= 1 #if DEBUG >= 1
@ -98,7 +104,9 @@ void Util::Procs::runCmd(std::string & cmd){
/// \arg name Name for this process - only used internally. /// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for this process. /// \arg cmd Commandline for this process.
pid_t Util::Procs::Start(std::string name, std::string cmd){ pid_t Util::Procs::Start(std::string name, std::string cmd){
if (isActive(name)){return getPid(name);} if (isActive(name)){
return getPid(name);
}
if ( !handler_set){ if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
@ -132,7 +140,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){
/// \arg cmd Commandline for sub (sending) process. /// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for main (receiving) process. /// \arg cmd2 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
if (isActive(name)){return getPid(name);} if (isActive(name)){
return getPid(name);
}
if ( !handler_set){ if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
@ -207,7 +217,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){
/// \arg cmd2 Commandline for sub (middle) process. /// \arg cmd2 Commandline for sub (middle) process.
/// \arg cmd3 Commandline for main (receiving) process. /// \arg cmd3 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){
if (isActive(name)){return getPid(name);} if (isActive(name)){
return getPid(name);
}
if ( !handler_set){ if ( !handler_set){
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = Util::Procs::childsig_handler; new_action.sa_handler = Util::Procs::childsig_handler;
@ -329,7 +341,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st
/// \arg fdout Same as fdin, but for stdout. /// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr. /// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){
if (isActive(name)){return getPid(name);} if (isActive(name)){
return getPid(name);
}
pid_t pid; pid_t pid;
int pipein[2], pipeout[2], pipeerr[2]; int pipein[2], pipeout[2], pipeerr[2];
if ( !handler_set){ if ( !handler_set){
@ -350,15 +364,24 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Pipe (out) creation failed for " << name << std::endl; std::cerr << "Pipe (out) creation failed for " << name << std::endl;
#endif #endif
if (*fdin == -1){close(pipein[0]);close(pipein[1]);} if ( *fdin == -1){
close(pipein[0]);
close(pipein[1]);
}
return 0; return 0;
} }
if (fderr && *fderr == -1 && pipe(pipeerr) < 0){ if (fderr && *fderr == -1 && pipe(pipeerr) < 0){
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Pipe (err) creation failed for " << name << std::endl; std::cerr << "Pipe (err) creation failed for " << name << std::endl;
#endif #endif
if (*fdin == -1){close(pipein [0]);close(pipein [1]);} if ( *fdin == -1){
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
close(pipein[1]);
}
if ( *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
return 0; return 0;
} }
int devnull = -1; int devnull = -1;
@ -368,9 +391,18 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl; std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl;
#endif #endif
if (*fdin == -1){close(pipein [0]);close(pipein [1]);} if ( *fdin == -1){
if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
if (*fderr == -1){close(pipeerr[0]);close(pipeerr[1]);} close(pipein[1]);
}
if ( *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
if ( *fderr == -1){
close(pipeerr[0]);
close(pipeerr[1]);
}
return 0; return 0;
} }
} }
@ -406,7 +438,9 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
dup2( *fderr, STDERR_FILENO); dup2( *fderr, STDERR_FILENO);
close( *fderr); close( *fderr);
} }
if (devnull != -1){close(devnull);} if (devnull != -1){
close(devnull);
}
execvp(argv[0], argv); execvp(argv[0], argv);
#if DEBUG >= 1 #if DEBUG >= 1
perror("execvp failed"); perror("execvp failed");
@ -416,10 +450,21 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
#if DEBUG >= 1 #if DEBUG >= 1
std::cerr << "Failed to fork for pipe: " << name << std::endl; std::cerr << "Failed to fork for pipe: " << name << std::endl;
#endif #endif
if (fdin && *fdin == -1){close(pipein [0]);close(pipein [1]);} if (fdin && *fdin == -1){
if (fdout && *fdout == -1){close(pipeout[0]);close(pipeout[1]);} close(pipein[0]);
if (fderr && *fderr == -1){close(pipeerr[0]);close(pipeerr[1]);} close(pipein[1]);
if (devnull != -1){close(devnull);} }
if (fdout && *fdout == -1){
close(pipeout[0]);
close(pipeout[1]);
}
if (fderr && *fderr == -1){
close(pipeerr[0]);
close(pipeerr[1]);
}
if (devnull != -1){
close(devnull);
}
return 0; return 0;
}else{ //parent }else{ //parent
#if DEBUG >= 1 #if DEBUG >= 1
@ -430,7 +475,9 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
if (devnull != -1) std::cerr << " null=" << devnull; if (devnull != -1) std::cerr << " null=" << devnull;
std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; std::cerr << ", PID " << pid << ": " << argv[0] << std::endl;
#endif #endif
if (devnull != -1){close(devnull);} if (devnull != -1){
close(devnull);
}
if (fdin && *fdin == -1){ if (fdin && *fdin == -1){
close(pipein[0]); // close unused end end close(pipein[0]); // close unused end end
*fdin = pipein[1]; *fdin = pipein[1];
@ -448,7 +495,6 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int *
return pid; return pid;
} }
/// Stops the named process, if running. /// Stops the named process, if running.
/// \arg name (Internal) name of process to stop /// \arg name (Internal) name of process to stop
void Util::Procs::Stop(std::string name){ void Util::Procs::Stop(std::string name){
@ -456,7 +502,9 @@ void Util::Procs::Stop(std::string name){
while (isActive(name)){ while (isActive(name)){
Stop(getPid(name)); Stop(getPid(name));
max--; max--;
if (max <= 0){return;} if (max <= 0){
return;
}
} }
} }
@ -485,7 +533,9 @@ int Util::Procs::Count(){
bool Util::Procs::isActive(std::string name){ bool Util::Procs::isActive(std::string name){
std::map<pid_t, std::string>::iterator it; std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++){ for (it = plist.begin(); it != plist.end(); it++){
if ((*it).second == name){return true;} if (( *it).second == name){
return true;
}
} }
return false; return false;
} }
@ -500,7 +550,9 @@ bool Util::Procs::isActive(pid_t name){
pid_t Util::Procs::getPid(std::string name){ pid_t Util::Procs::getPid(std::string name){
std::map<pid_t, std::string>::iterator it; std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++){ for (it = plist.begin(); it != plist.end(); it++){
if ((*it).second == name){return (*it).first;} if (( *it).second == name){
return ( *it).first;
}
} }
return 0; return 0;
} }

View file

@ -35,4 +35,4 @@ namespace Util{
static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier); static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier);
}; };
}; }

View file

@ -39,19 +39,17 @@ std::map<unsigned int, RTMPStream::Chunk> RTMPStream::Chunk::lastrecv;
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
uint8_t genuineFMSKey[] = { uint8_t genuineFMSKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72,
0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, // Genuine Adobe Flash Media Server 001 0x76, // Genuine Adobe Flash Media Server 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 68
}; // 68
uint8_t genuineFPKey[] = { uint8_t genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x50, 0x6c, 0x61,
0x50, 0x6c, 0x61, 0x79, // Genuine Adobe Flash Player 001 0x79, // Genuine Adobe Flash Player 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 62
}; // 62
inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){
if (scheme == 0){ if (scheme == 0){
@ -104,66 +102,119 @@ DHWrapper::~DHWrapper() {
bool DHWrapper::Initialize(){ bool DHWrapper::Initialize(){
Cleanup(); Cleanup();
_pDH = DH_new(); _pDH = DH_new();
if (!_pDH){Cleanup(); return false;} if ( !_pDH){
Cleanup();
return false;
}
_pDH->p = BN_new(); _pDH->p = BN_new();
if (!_pDH->p){Cleanup(); return false;} if ( !_pDH->p){
Cleanup();
return false;
}
_pDH->g = BN_new(); _pDH->g = BN_new();
if (!_pDH->g){Cleanup(); return false;} if ( !_pDH->g){
if (BN_hex2bn(&_pDH->p, P1024) == 0){Cleanup(); return false;} Cleanup();
if (BN_set_word(_pDH->g, 2) != 1){Cleanup(); return false;} return false;
}
if (BN_hex2bn( &_pDH->p, P1024) == 0){
Cleanup();
return false;
}
if (BN_set_word(_pDH->g, 2) != 1){
Cleanup();
return false;
}
_pDH->length = _bitsCount; _pDH->length = _bitsCount;
if (DH_generate_key(_pDH) != 1){Cleanup(); return false;} if (DH_generate_key(_pDH) != 1){
Cleanup();
return false;
}
return true; return true;
} }
bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength){ bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
return false;
}
return CopyKey(_pDH->pub_key, pDst, dstLength); return CopyKey(_pDH->pub_key, pDst, dstLength);
} }
bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength){ bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
return false;
}
return CopyKey(_pDH->priv_key, pDst, dstLength); return CopyKey(_pDH->priv_key, pDst, dstLength);
} }
bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length){ bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length){
if (!_pDH){return false;} if ( !_pDH){
if (_sharedKeyLength != 0 || _pSharedKey){return false;} return false;
}
if (_sharedKeyLength != 0 || _pSharedKey){
return false;
}
_sharedKeyLength = DH_size(_pDH); _sharedKeyLength = DH_size(_pDH);
if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){return false;} if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){
return false;
}
_pSharedKey = new uint8_t[_sharedKeyLength]; _pSharedKey = new uint8_t[_sharedKeyLength];
_peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0);
if (!_peerPublickey){return false;} if ( !_peerPublickey){
return false;
}
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){return false;} if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){
return false;
}
return true; return true;
} }
bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength){ bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength){
if (!_pDH){return false;} if ( !_pDH){
if (dstLength != _sharedKeyLength){return false;} return false;
}
if (dstLength != _sharedKeyLength){
return false;
}
memcpy(pDst, _pSharedKey, _sharedKeyLength); memcpy(pDst, _pSharedKey, _sharedKeyLength);
return true; return true;
} }
void DHWrapper::Cleanup(){ void DHWrapper::Cleanup(){
if (_pDH){ if (_pDH){
if (_pDH->p){BN_free(_pDH->p); _pDH->p = 0;} if (_pDH->p){
if (_pDH->g){BN_free(_pDH->g); _pDH->g = 0;} BN_free(_pDH->p);
DH_free(_pDH); _pDH = 0; _pDH->p = 0;
}
if (_pDH->g){
BN_free(_pDH->g);
_pDH->g = 0;
}
DH_free(_pDH);
_pDH = 0;
}
if (_pSharedKey){
delete[] _pSharedKey;
_pSharedKey = 0;
} }
if (_pSharedKey){delete[] _pSharedKey; _pSharedKey = 0;}
_sharedKeyLength = 0; _sharedKeyLength = 0;
if (_peerPublickey){BN_free(_peerPublickey); _peerPublickey = 0;} if (_peerPublickey){
BN_free(_peerPublickey);
_peerPublickey = 0;
}
} }
bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength){ bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength){
int32_t keySize = BN_num_bytes(pNum); int32_t keySize = BN_num_bytes(pNum);
if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){return false;} if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){
if (BN_bn2bin(pNum, pDst) != keySize){return false;} return false;
}
if (BN_bn2bin(pNum, pDst) != keySize){
return false;
}
return true; return true;
} }
@ -237,7 +288,9 @@ std::string & RTMPStream::Chunk::Pack(){
} }
} }
//override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel //override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel
if (timestamp < prev.timestamp){chtype = 0x00;} if (timestamp < prev.timestamp){
chtype = 0x00;
}
} }
if (cs_id <= 63){ if (cs_id <= 63){
output += (unsigned char)(chtype | cs_id); output += (unsigned char)(chtype | cs_id);
@ -259,7 +312,10 @@ std::string & RTMPStream::Chunk::Pack(){
}else{ }else{
tmpi = timestamp - prev.timestamp; tmpi = timestamp - prev.timestamp;
} }
if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} if (tmpi >= 0x00ffffff){
ntime = tmpi;
tmpi = 0x00ffffff;
}
output += (unsigned char)((tmpi >> 16) & 0xff); output += (unsigned char)((tmpi >> 16) & 0xff);
output += (unsigned char)((tmpi >> 8) & 0xff); output += (unsigned char)((tmpi >> 8) & 0xff);
output += (unsigned char)(tmpi & 0xff); output += (unsigned char)(tmpi & 0xff);
@ -290,7 +346,9 @@ std::string & RTMPStream::Chunk::Pack(){
len_left = 0; len_left = 0;
while (len_left < len){ while (len_left < len){
tmpi = len - len_left; tmpi = len - len_left;
if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} if (tmpi > RTMPStream::chunk_snd_max){
tmpi = RTMPStream::chunk_snd_max;
}
output.append(data, len_left, tmpi); output.append(data, len_left, tmpi);
len_left += tmpi; len_left += tmpi;
if (len_left < len){ if (len_left < len){
@ -313,8 +371,9 @@ std::string & RTMPStream::Chunk::Pack(){
return output; return output;
} //SendChunk } //SendChunk
/// Default contructor, creates an empty chunk with all values initialized to zero. /// Default constructor, creates an empty chunk with all values initialized to zero.
RTMPStream::Chunk::Chunk(){ RTMPStream::Chunk::Chunk(){
headertype = 0;
cs_id = 0; cs_id = 0;
timestamp = 0; timestamp = 0;
len = 0; len = 0;
@ -438,7 +497,6 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne
return ch.Pack(); return ch.Pack();
} //SendUSR } //SendUSR
/// Parses the argument string into the current chunk. /// Parses the argument string into the current chunk.
/// Tries to read a whole chunk, removing data from the input string as it reads. /// Tries to read a whole chunk, removing data from the input string as it reads.
/// If only part of a chunk is read, it will remove the part and call itself again. /// If only part of a chunk is read, it will remove the part and call itself again.
@ -491,11 +549,15 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
break; break;
case 0x40: case 0x40:
if (indata.size() < i + 7) return false; //can't read whole header if (indata.size() < i + 7) return false; //can't read whole header
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");
}
timestamp = indata[i++ ] * 256 * 256; timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ]; timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = indata[i++ ] * 256 * 256; len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256; len += indata[i++ ] * 256;
len += indata[i++ ]; len += indata[i++ ];
@ -505,18 +567,24 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
break; break;
case 0x80: case 0x80:
if (indata.size() < i + 3) return false; //can't read whole header if (indata.size() < i + 3) return false; //can't read whole header
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");
}
timestamp = indata[i++ ] * 256 * 256; timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ]; timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
msg_type_id = prev.msg_type_id; msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0xC0: case 0xC0:
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");
}
timestamp = prev.timestamp; timestamp = prev.timestamp;
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
@ -581,7 +649,9 @@ bool RTMPStream::Chunk::Parse(std::string & indata){
bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
gettimeofday( &RTMPStream::lastrec, 0); gettimeofday( &RTMPStream::lastrec, 0);
unsigned int i = 0; unsigned int i = 0;
if (!buffer.available(3)){return false;}//we want at least 3 bytes if ( !buffer.available(3)){
return false;
} //we want at least 3 bytes
std::string indata = buffer.copy(3); std::string indata = buffer.copy(3);
unsigned char chunktype = indata[i++ ]; unsigned char chunktype = indata[i++ ];
@ -605,7 +675,9 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
headertype = chunktype & 0xC0; headertype = chunktype & 0xC0;
switch (headertype){ switch (headertype){
case 0x00: case 0x00:
if (!buffer.available(i+11)){return false;} //can't read whole header if ( !buffer.available(i + 11)){
return false;
} //can't read whole header
indata = buffer.copy(i + 11); indata = buffer.copy(i + 11);
timestamp = indata[i++ ] * 256 * 256; timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256; timestamp += indata[i++ ] * 256;
@ -621,13 +693,19 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
msg_stream_id += indata[i++ ] * 256 * 256 * 256; msg_stream_id += indata[i++ ] * 256 * 256 * 256;
break; break;
case 0x40: case 0x40:
if (!buffer.available(i+7)){return false;} //can't read whole header if ( !buffer.available(i + 7)){
return false;
} //can't read whole header
indata = buffer.copy(i + 7); indata = buffer.copy(i + 7);
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} 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 * 256;
timestamp += indata[i++ ] * 256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ]; timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = indata[i++ ] * 256 * 256; len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256; len += indata[i++ ] * 256;
len += indata[i++ ]; len += indata[i++ ];
@ -636,20 +714,28 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0x80: case 0x80:
if (!buffer.available(i+3)){return false;} //can't read whole header if ( !buffer.available(i + 3)){
return false;
} //can't read whole header
indata = buffer.copy(i + 3); indata = buffer.copy(i + 3);
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} 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 * 256;
timestamp += indata[i++ ] * 256; timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ]; timestamp += indata[i++ ];
if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} if (timestamp != 0x00ffffff){
timestamp += prev.timestamp;
}
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
msg_type_id = prev.msg_type_id; msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id; msg_stream_id = prev.msg_stream_id;
break; break;
case 0xC0: case 0xC0:
if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} if (prev.msg_type_id == 0){
fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");
}
timestamp = prev.timestamp; timestamp = prev.timestamp;
len = prev.len; len = prev.len;
len_left = prev.len_left; len_left = prev.len_left;
@ -670,7 +756,9 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
} }
//read extended timestamp, if neccesary //read extended timestamp, if neccesary
if (timestamp == 0x00ffffff){ if (timestamp == 0x00ffffff){
if (!buffer.available(i+4)){return false;} //can't read timestamp if ( !buffer.available(i + 4)){
return false;
} //can't read timestamp
indata = buffer.copy(i + 4); indata = buffer.copy(i + 4);
timestamp = indata[i++ ] * 256 * 256 * 256; timestamp = indata[i++ ] * 256 * 256 * 256;
timestamp += indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256 * 256;
@ -680,7 +768,9 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){
//read data if length > 0, and allocate it //read data if length > 0, and allocate it
if (real_len > 0){ if (real_len > 0){
if (!buffer.available(i+real_len)){return false;}//can't read all data (yet) if ( !buffer.available(i + real_len)){
return false;
} //can't read all data (yet)
buffer.remove(i); //remove the header buffer.remove(i); //remove the header
if (prev.len_left > 0){ if (prev.len_left > 0){
data = prev.data + buffer.remove(real_len); //append the data and remove from buffer data = prev.data + buffer.remove(real_len); //append the data and remove from buffer
@ -720,7 +810,9 @@ bool RTMPStream::doHandshake(){
//Build S1 Packet //Build S1 Packet
*((uint32_t*)Server) = 0; //time zero *((uint32_t*)Server) = 0; //time zero
*(((uint32_t*)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4 *(((uint32_t*)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4
for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%16];}//"random" data for (int i = 8; i < 3072; ++i){
Server[i] = versionstring[i % 16];
} //"random" data
bool encrypted = (Version == 6); bool encrypted = (Version == 6);
#if DEBUG >= 4 #if DEBUG >= 4

View file

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

View file

@ -16,7 +16,6 @@
#define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB #define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB
#include <iostream>//temporary for debugging #include <iostream>//temporary for debugging
std::string uint2string(unsigned int i){ std::string uint2string(unsigned int i){
std::stringstream st; std::stringstream st;
st << i; st << i;
@ -27,7 +26,9 @@ std::string uint2string(unsigned int i){
/// The back is popped as long as it is empty, first - this way this function is /// The back is popped as long as it is empty, first - this way this function is
/// guaranteed to return 0 if the buffer is empty. /// guaranteed to return 0 if the buffer is empty.
unsigned int Socket::Buffer::size(){ unsigned int Socket::Buffer::size(){
while (data.size() > 0 && data.back().empty()){data.pop_back();} while (data.size() > 0 && data.back().empty()){
data.pop_back();
}
return data.size(); return data.size();
} }
@ -45,7 +46,9 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
j = i; j = i;
while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){ while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){
j++; j++;
if (newdata[j-1] == '\n'){break;} if (newdata[j - 1] == '\n'){
break;
}
} }
if (i != j){ if (i != j){
data.push_front(std::string(newdata + i, (size_t)(j - i))); data.push_front(std::string(newdata + i, (size_t)(j - i)));
@ -64,7 +67,9 @@ bool Socket::Buffer::available(unsigned int count){
unsigned int i = 0; unsigned int i = 0;
for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){ for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){
i += ( *it).size(); i += ( *it).size();
if (i >= count){return true;} if (i >= count){
return true;
}
} }
return false; return false;
} }
@ -72,7 +77,9 @@ bool Socket::Buffer::available(unsigned int count){
/// Removes count bytes from the buffer, returning them by value. /// Removes count bytes from the buffer, returning them by value.
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::remove(unsigned int count){ std::string Socket::Buffer::remove(unsigned int count){
if (!available(count)){return "";} if ( !available(count)){
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
ret.reserve(count); ret.reserve(count);
@ -93,7 +100,9 @@ std::string Socket::Buffer::remove(unsigned int count){
/// Copies count bytes from the buffer, returning them by value. /// Copies count bytes from the buffer, returning them by value.
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::copy(unsigned int count){ std::string Socket::Buffer::copy(unsigned int count){
if (!available(count)){return "";} if ( !available(count)){
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ for (std::deque<std::string>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){
@ -118,7 +127,6 @@ std::string & Socket::Buffer::get(){
} }
} }
/// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection.
/// \param sockNo Integer representing the socket to convert. /// \param sockNo Integer representing the socket to convert.
Socket::Connection::Connection(int sockNo){ Socket::Connection::Connection(int sockNo){
@ -172,9 +180,15 @@ void setFDBlocking(int FD, bool blocking){
/// Set this socket to be blocking (true) or nonblocking (false). /// Set this socket to be blocking (true) or nonblocking (false).
void Socket::Connection::setBlocking(bool blocking){ void Socket::Connection::setBlocking(bool blocking){
if (sock >=0){setFDBlocking(sock, blocking);} if (sock >= 0){
if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);} setFDBlocking(sock, blocking);
if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);} }
if (pipes[0] >= 0){
setFDBlocking(pipes[0], blocking);
}
if (pipes[1] >= 0){
setFDBlocking(pipes[1], blocking);
}
} }
/// Close connection. The internal socket is closed and then set to -1. /// Close connection. The internal socket is closed and then set to -1.
@ -187,29 +201,36 @@ void Socket::Connection::close(){
if (sock != -1){ if (sock != -1){
shutdown(sock, SHUT_RDWR); shutdown(sock, SHUT_RDWR);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR){} while (::close(sock) != 0 && errno == EINTR){
}
sock = -1; sock = -1;
} }
if (pipes[0] != -1){ if (pipes[0] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[0]) != 0 && errno == EINTR){} while (::close(pipes[0]) != 0 && errno == EINTR){
}
pipes[0] = -1; pipes[0] = -1;
} }
if (pipes[1] != -1){ if (pipes[1] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[1]) != 0 && errno == EINTR){} while (::close(pipes[1]) != 0 && errno == EINTR){
}
pipes[1] = -1; pipes[1] = -1;
} }
} }
} //Socket::Connection::close } //Socket::Connection::close
/// Returns internal socket number. /// Returns internal socket number.
int Socket::Connection::getSocket(){return sock;} int Socket::Connection::getSocket(){
return sock;
}
/// Returns a string describing the last error that occured. /// Returns a string describing the last error that occured.
/// Simply calls strerror(errno) - not very reliable! /// Simply calls strerror(errno) - not very reliable!
/// \todo Improve getError at some point to be more reliable and only report socket errors. /// \todo Improve getError at some point to be more reliable and only report socket errors.
std::string Socket::Connection::getError(){return strerror(errno);} std::string Socket::Connection::getError(){
return strerror(errno);
}
/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. /// Create a new Unix Socket. This socket will (try to) connect to the given address right away.
/// \param address String containing the location of the Unix socket to connect to. /// \param address String containing the location of the Unix socket to connect to.
@ -282,8 +303,12 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){
for (rp = result; rp != NULL; rp = rp->ai_next){ for (rp = result; rp != NULL; rp = rp->ai_next){
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0){continue;} if (sock < 0){
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} continue;
}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){
break;
}
::close(sock); ::close(sock);
} }
freeaddrinfo(result); freeaddrinfo(result);
@ -357,7 +382,6 @@ bool Socket::Connection::flush(){
} }
} }
/// Returns a reference to the download buffer. /// Returns a reference to the download buffer.
Socket::Buffer & Socket::Connection::Received(){ Socket::Buffer & Socket::Connection::Received(){
return downbuffer; return downbuffer;
@ -390,7 +414,9 @@ void Socket::Connection::SendNow(const char * data, size_t len){
/// This means this function is blocking if the socket is, but nonblocking otherwise. /// This means this function is blocking if the socket is, but nonblocking otherwise.
void Socket::Connection::Send(const char * data, size_t len){ void Socket::Connection::Send(const char * data, size_t len){
while (upbuffer.size() > 0){ while (upbuffer.size() > 0){
if (!iwrite(upbuffer.get())){break;} if ( !iwrite(upbuffer.get())){
break;
}
} }
if (upbuffer.size() > 0){ if (upbuffer.size() > 0){
upbuffer.append(data, len); upbuffer.append(data, len);
@ -442,7 +468,9 @@ void Socket::Connection::Send(std::string & data){
/// \param len Amount of bytes to write. /// \param len Amount of bytes to write.
/// \returns The amount of bytes actually written. /// \returns The amount of bytes actually written.
int Socket::Connection::iwrite(const void * buffer, int len){ int Socket::Connection::iwrite(const void * buffer, int len){
if (!connected() || len < 1){return 0;} if ( !connected() || len < 1){
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = send(sock, buffer, len, 0); r = send(sock, buffer, len, 0);
@ -479,7 +507,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){
/// \param len Amount of bytes to read. /// \param len Amount of bytes to read.
/// \returns The amount of bytes actually read. /// \returns The amount of bytes actually read.
int Socket::Connection::iread(void * buffer, int len){ int Socket::Connection::iread(void * buffer, int len){
if (!connected() || len < 1){return 0;} if ( !connected() || len < 1){
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = recv(sock, buffer, len, 0); r = recv(sock, buffer, len, 0);
@ -518,7 +548,9 @@ int Socket::Connection::iread(void * buffer, int len){
bool Socket::Connection::iread(Buffer & buffer){ bool Socket::Connection::iread(Buffer & buffer){
char cbuffer[BUFFER_BLOCKSIZE]; char cbuffer[BUFFER_BLOCKSIZE];
int num = iread(cbuffer, BUFFER_BLOCKSIZE); int num = iread(cbuffer, BUFFER_BLOCKSIZE);
if (num < 1){return false;} if (num < 1){
return false;
}
buffer.append(cbuffer, num); buffer.append(cbuffer, num);
return true; return true;
} //iread } //iread
@ -529,9 +561,13 @@ bool Socket::Connection::iread(Buffer & buffer){
/// \param buffer std::string to remove data from. /// \param buffer std::string to remove data from.
/// \return True if more data was sent, false otherwise. /// \return True if more data was sent, false otherwise.
bool Socket::Connection::iwrite(std::string & buffer){ bool Socket::Connection::iwrite(std::string & buffer){
if (buffer.size() < 1){return false;} if (buffer.size() < 1){
return false;
}
int tmp = iwrite((void*)buffer.c_str(), buffer.size()); int tmp = iwrite((void*)buffer.c_str(), buffer.size());
if (tmp < 1){return false;} if (tmp < 1){
return false;
}
buffer = buffer.substr(tmp); buffer = buffer.substr(tmp);
return true; return true;
} //iwrite } //iwrite
@ -736,7 +772,9 @@ Socket::Server::Server(std::string address, bool nonblock){
/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). /// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking).
/// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. /// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances.
Socket::Connection Socket::Server::accept(bool nonblock){ Socket::Connection Socket::Server::accept(bool nonblock){
if (sock < 0){return Socket::Connection(-1);} if (sock < 0){
return Socket::Connection( -1);
}
struct sockaddr_in6 addrinfo; struct sockaddr_in6 addrinfo;
socklen_t len = sizeof(addrinfo); socklen_t len = sizeof(addrinfo);
static char addrconv[INET6_ADDRSTRLEN]; static char addrconv[INET6_ADDRSTRLEN];
@ -789,7 +827,8 @@ void Socket::Server::close(){
#endif #endif
shutdown(sock, SHUT_RDWR); shutdown(sock, SHUT_RDWR);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR){} while (::close(sock) != 0 && errno == EINTR){
}
sock = -1; sock = -1;
} }
} //Socket::Server::close } //Socket::Server::close
@ -804,4 +843,6 @@ bool Socket::Server::connected() const{
} //Socket::Server::connected } //Socket::Server::connected
/// Returns internal socket number. /// Returns internal socket number.
int Socket::Server::getSocket(){return sock;} int Socket::Server::getSocket(){
return sock;
}

View file

@ -17,7 +17,9 @@
#include <deque> #include <deque>
//for being friendly with Socket::Connection down below //for being friendly with Socket::Connection down below
namespace Buffer{class user;}; namespace Buffer {
class user;
}
///Holds Socket tools. ///Holds Socket tools.
namespace Socket { namespace Socket {
@ -34,7 +36,8 @@ namespace Socket{
bool available(unsigned int count); bool available(unsigned int count);
std::string remove(unsigned int count); std::string remove(unsigned int count);
std::string copy(unsigned int count); std::string copy(unsigned int count);
};//Buffer };
//Buffer
/// This class is for easy communicating through sockets, either TCP or Unix. /// This class is for easy communicating through sockets, either TCP or Unix.
class Connection{ class Connection{
@ -107,4 +110,4 @@ namespace Socket{
int getSocket(); ///< Returns internal socket number. int getSocket(); ///< Returns internal socket number.
}; };
}; }

View file

@ -19,7 +19,10 @@
void Util::Stream::sanitizeName(std::string & streamname){ void Util::Stream::sanitizeName(std::string & streamname){
//strip anything that isn't numbers, digits or underscores //strip anything that isn't numbers, digits or underscores
for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){ for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){
if (*i == '?'){streamname.erase(i, streamname.end()); break;} if ( *i == '?'){
streamname.erase(i, streamname.end());
break;
}
if ( !isalpha( *i) && !isdigit( *i) && *i != '_'){ if ( !isalpha( *i) && !isdigit( *i) && *i != '_'){
streamname.erase(i); streamname.erase(i);
}else{ }else{

View file

@ -4,11 +4,14 @@
#include "timing.h" #include "timing.h"
#include <sys/time.h>//for gettimeofday #include <sys/time.h>//for gettimeofday
#include <time.h>//for time and nanosleep #include <time.h>//for time and nanosleep
/// Sleeps for the indicated amount of milliseconds or longer. /// Sleeps for the indicated amount of milliseconds or longer.
void Util::sleep(int ms){ void Util::sleep(int ms){
if (ms < 0){return;} if (ms < 0){
if (ms > 10000){return;} return;
}
if (ms > 10000){
return;
}
struct timespec T; struct timespec T;
T.tv_sec = ms / 1000; T.tv_sec = ms / 1000;
T.tv_nsec = 1000000 * (ms % 1000); T.tv_nsec = 1000000 * (ms % 1000);

View file

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