summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
committerDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
commit1cf955a715830130b7add8c1183d65b0f442fd23 (patch)
treec9d03961e9f83b1100ef6010a4a53063f127aa5d /misc
downloadvdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.gz
vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.bz2
Initial commit
Diffstat (limited to 'misc')
-rw-r--r--misc/config.cpp109
-rw-r--r--misc/config.h33
-rw-r--r--misc/menusetup.cpp205
-rw-r--r--misc/menusetup.h78
-rw-r--r--misc/search.cpp795
-rw-r--r--misc/search.h90
-rw-r--r--misc/util.cpp488
-rw-r--r--misc/util.h45
8 files changed, 1843 insertions, 0 deletions
diff --git a/misc/config.cpp b/misc/config.cpp
new file mode 100644
index 0000000..59bf4a6
--- /dev/null
+++ b/misc/config.cpp
@@ -0,0 +1,109 @@
+/*
+ * File: config.cpp
+ * Author: savop
+ *
+ * Created on 15. August 2009, 13:03
+ */
+
+#include <stdio.h>
+#include <vdr/tools.h>
+#include <getopt.h>
+#include "config.h"
+#include "../common.h"
+
+cUPnPConfig::cUPnPConfig(){
+ this->mParsedArgs = NULL;
+ this->mInterface = NULL;
+ this->mAddress = NULL;
+ this->mAutoSetup = 0;
+ this->mEnable = 0;
+ this->mPort = 0;
+}
+
+cUPnPConfig::~cUPnPConfig(){}
+
+cUPnPConfig* cUPnPConfig::get(){
+ if(cUPnPConfig::mInstance == NULL)
+ cUPnPConfig::mInstance = new cUPnPConfig();
+
+ if(cUPnPConfig::mInstance)
+ return cUPnPConfig::mInstance;
+ else return NULL;
+}
+
+cUPnPConfig* cUPnPConfig::mInstance = NULL;
+
+bool cUPnPConfig::processArgs(int argc, char* argv[]){
+ // Implement command line argument processing here if applicable.
+ static struct option long_options[] = {
+ {"int", required_argument, NULL, 'i'},
+ {"address", required_argument, NULL, 'a'},
+ {"port", required_argument, NULL, 'p'},
+ {"autodetect", no_argument, NULL, 'd'}
+ };
+
+ int c = 0;
+
+ // Check if anything went wrong by setting 'success' to false
+ // As there are multiple tests you may get a faulty behavior
+ // if the current value is not considered in latter tests.
+ // Asume that: Success = true;
+ // success && false = false; --> new value of success is false
+ // success && true = true; --> still true.
+ // So, in the case of true and only true, success contains the
+ // desired value.
+ bool success = true;
+ bool ifaceExcistent = false;
+ bool addExcistent = false;
+
+ while((c = getopt_long(argc, argv, "i:a:p:v",long_options, NULL)) != -1){
+ switch(c){
+ case 'i':
+ if(addExcistent) { ERROR("Address given but must be absent!"); return false; }
+ success = this->parseSetup(SETUP_SERVER_INT, optarg) && success;
+ success = this->parseSetup(SETUP_SERVER_ADDRESS, NULL) && success;
+ success = this->parseSetup(SETUP_SERVER_AUTO, "0") && success;
+ ifaceExcistent = true;
+ break;
+ case 'a':
+ if(ifaceExcistent) { ERROR("Interface given but must be absent!"); return false; }
+ success = this->parseSetup(SETUP_SERVER_ADDRESS, optarg) && success;
+ success = this->parseSetup(SETUP_SERVER_INT, NULL) && success;
+ success = this->parseSetup(SETUP_SERVER_AUTO, "0") && success;
+ addExcistent = true;
+ break;
+ case 'p':
+ success = this->parseSetup(SETUP_SERVER_PORT, optarg) && success;
+ success = this->parseSetup(SETUP_SERVER_AUTO, "0") && success;
+ break;
+ case 'd':
+ success = this->parseSetup(SETUP_SERVER_AUTO, optarg) && success;
+ default:
+ return false;
+ }
+ }
+
+ return success;
+}
+
+bool cUPnPConfig::parseSetup(const char *Name, const char *Value)
+{
+ const char* ptr;
+ if(*this->mParsedArgs && (ptr = strstr(this->mParsedArgs,Name))!=NULL){
+ MESSAGE("Skipping %s=%s, was overridden in command line.",Name, Value);
+ return true;
+ }
+
+ MESSAGE("VARIABLE %s has value %s", Name, Value);
+ // Parse your own setup parameters and store their values.
+ if (!strcasecmp(Name, SETUP_SERVER_ENABLED)) this->mEnable = atoi(Value);
+ else if (!strcasecmp(Name, SETUP_SERVER_AUTO)) this->mAutoSetup = atoi(Value);
+ else if (!strcasecmp(Name, SETUP_SERVER_INT)) this->mInterface = strdup0(Value); // (Value) ? strn0cpy(this->mInterface, Value, strlen(this->mInterface)) : NULL;
+ else if (!strcasecmp(Name, SETUP_SERVER_ADDRESS)) this->mAddress = strdup0(Value); //(Value) ? strn0cpy(this->mAddress, Value, strlen(this->mAddress)) : NULL;
+ else if (!strcasecmp(Name, SETUP_SERVER_PORT)) this->mPort = atoi(Value);
+ else return false;
+
+ this->mParsedArgs = cString::sprintf("%s%s",*this->mParsedArgs,Name);
+
+ return true;
+} \ No newline at end of file
diff --git a/misc/config.h b/misc/config.h
new file mode 100644
index 0000000..018213e
--- /dev/null
+++ b/misc/config.h
@@ -0,0 +1,33 @@
+/*
+ * File: config.h
+ * Author: savop
+ *
+ * Created on 15. August 2009, 13:03
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#include <vdr/tools.h>
+#include "../common.h"
+
+class cUPnPConfig {
+private:
+ static cUPnPConfig* mInstance;
+ cString mParsedArgs;
+ cUPnPConfig();
+public:
+ char* mInterface;
+ char* mAddress;
+ int mPort;
+ int mEnable;
+ int mAutoSetup;
+public:
+ virtual ~cUPnPConfig();
+ static cUPnPConfig* get();
+ bool parseSetup(const char* Name, const char* Value);
+ bool processArgs(int argc, char* argv[]);
+};
+
+#endif /* _CONFIG_H */
+
diff --git a/misc/menusetup.cpp b/misc/menusetup.cpp
new file mode 100644
index 0000000..9e8386f
--- /dev/null
+++ b/misc/menusetup.cpp
@@ -0,0 +1,205 @@
+/*
+ * File: menusetup.cpp
+ * Author: savop
+ *
+ * Created on 19. April 2009, 16:50
+ */
+
+#include "config.h"
+#include <vdr/osdbase.h>
+#include "menusetup.h"
+#include "../common.h"
+#include "util.h"
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <vdr/menuitems.h>
+
+
+cMenuSetupUPnP::cMenuSetupUPnP(){
+ // Get server acitve state
+ MESSAGE("Creating menu");
+ this->mCtrlBind = NULL;
+ this->mCtrlAutoMode = NULL;
+ this->mCtrlEnabled = NULL;
+ this->mCtrlPort = NULL;
+ this->mEnable = 0;
+ this->mDetectPort = 0;
+ this->mAutoSetup = 0;
+ this->mPort = 0;
+ this->mAddress = NULL;
+ this->mInterfaceIndex = 0;
+ this->Load();
+ this->Update();
+}
+
+//cMenuSetupUPnP::~cMenuSetupUPnP() {
+// delete this->mCtrlAutoMode;
+// delete this->mCtrlEnabled;
+// delete this->mCtrlPort;
+// free(this->mAddress);
+//}
+
+void cMenuSetupUPnP::Load(void){
+ cUPnPConfig* Config = cUPnPConfig::get();
+ this->mEnable = Config->mEnable;
+ this->mAutoSetup = Config->mAutoSetup;
+ this->mInterfaceIndex = this->getInterfaceIndex(Config->mInterface);
+ this->mAddress = strdup(Config->mAddress?Config->mAddress:"0.0.0.0");
+ this->mPort = Config->mPort;
+
+ if(Config->mPort==0) this->mDetectPort = 1;
+}
+
+const char* const* cMenuSetupUPnP::getInterfaceList(int* count){
+ char** Ifaces = getNetworkInterfaces(count);
+ char** IfaceList = new char*[++(*count)];
+ IfaceList[0] = strdup(_("User defined"));
+ for(int i=0; i < *count-1; i++){
+ IfaceList[i+1] = strdup(Ifaces[i]);
+ }
+ delete [] Ifaces;
+ return IfaceList;
+}
+
+int cMenuSetupUPnP::getInterfaceIndex(const char* Interface){
+ MESSAGE("Getting Index of %s", Interface);
+ if(!Interface) return 0;
+ int count;
+ int Index = 0;
+ const char* const* Ifaces = this->getInterfaceList(&count);
+
+ for(int i=1; i < count; i++){
+ if(!strcmp(Interface, Ifaces[i])){
+ Index = i;
+ break;
+ }
+ }
+ delete [] Ifaces;
+ return Index;
+}
+
+const char* cMenuSetupUPnP::getInterface(int Index){
+ int count;
+ const char* const* Ifaces = this->getInterfaceList(&count);
+
+ if(count < Index || Index < 1) return NULL;
+ const char* Interface = strdup0(Ifaces[Index]);
+ delete [] Ifaces;
+ return Interface;
+}
+
+void cMenuSetupUPnP::Update(void){
+ int Current = this->Current();
+ this->Clear();
+ // Add OSD menu item for enabling UPnP Server
+ this->Add(mCtrlEnabled = new cMenuEditBoolItem(_("Enable UPnP Server"),&this->mEnable,_("disabled"),_("enabled")));
+ if(this->mEnable){
+ cMenuEditIntItem* editPortItem = NULL;
+ this->Add(mCtrlAutoMode = new cMenuEditBoolItem(_("Auto detect settings"),&this->mAutoSetup,_("no"),_("yes")));
+ // Add OSD menu item for IP address
+ int Count;
+ const char* const* Interfaces = this->getInterfaceList(&Count);
+ this->Add(mCtrlBind = new cMenuEditStraItem(_("Bind to network interface"), &this->mInterfaceIndex, Count, Interfaces));
+
+ cMenuEditIpItem* editIpItem;
+ if(this->mInterfaceIndex){
+ const sockaddr_in* addr = getIPFromInterface(this->getInterface(this->mInterfaceIndex));
+ char* IP = strdup(inet_ntoa(addr->sin_addr));
+ editIpItem = new cMenuEditIpItem(_("Current IP address"),IP);
+ editIpItem->SetSelectable(false);
+ free(IP);
+ }
+ else {
+ editIpItem = new cMenuEditIpItem(_("Set IP address"),this->mAddress);
+ }
+ this->Add(editIpItem);
+ this->Add(mCtrlPort = new cMenuEditBoolItem(_("Select port"), &this->mDetectPort, _("auto"), _("user definied")));
+ if(this->mDetectPort){
+ this->Add(editPortItem = new cMenuEditIntItem(_("User specified port"),
+ &this->mPort,
+ SERVER_MIN_PORT,
+ SERVER_MAX_PORT
+ ));
+ }
+
+ if(this->mAutoSetup){
+ if(mCtrlPort) mCtrlPort->SetSelectable(false);
+ if(mCtrlBind) mCtrlBind->SetSelectable(false);
+ if(editPortItem) editPortItem->SetSelectable(false);
+ if(editIpItem) editIpItem->SetSelectable(false);
+ }
+ else {
+ if(mCtrlPort) mCtrlPort->SetSelectable(true);
+ if(mCtrlBind) mCtrlBind->SetSelectable(true);
+ if(editPortItem) editPortItem->SetSelectable(true);
+ if(editIpItem && !this->mInterfaceIndex) editIpItem->SetSelectable(true);
+ }
+ }
+ this->SetCurrent(this->Get(Current));
+ this->Display();
+}
+
+eOSState cMenuSetupUPnP::ProcessKey(eKeys Key){
+
+ cOsdItem *Item = this->Get(this->Current());
+
+ eOSState State = cMenuSetupPage::ProcessKey(Key);
+
+ Key = NORMALKEY(Key);
+
+ if(Key != kRight && Key != kLeft){
+ return State;
+ }
+
+ if(Item == this->mCtrlEnabled){
+ if(this->mEnable){
+ this->Update();
+ }
+ else if (!this->mEnable) {
+ this->Update();
+ }
+ }
+ else if (Item == this->mCtrlPort){
+ if(this->mDetectPort){
+ this->Update();
+ }
+ else if(!this->mDetectPort) {
+ this->Update();
+ }
+ }
+ else if (Item == this->mCtrlAutoMode){
+ if(this->mAutoSetup){
+ this->Update();
+ }
+ else if(!this->mAutoSetup) {
+ this->Update();
+ }
+ }
+ else if(Item == this->mCtrlBind){
+// if(!this->mInterfaceIndex){
+// this->Update();
+// }
+// else if(!this->mInterfaceIndex){
+// this->Update();
+// }
+ this->Update();
+ }
+ return State;
+}
+
+void cMenuSetupUPnP::Store(void){
+ cUPnPConfig* Config = cUPnPConfig::get();
+ Config->mAddress = strdup0(this->mAddress);
+ Config->mAutoSetup = this->mAutoSetup;
+ Config->mEnable = this->mEnable;
+ Config->mInterface = strdup0(this->getInterface(this->mInterfaceIndex));
+ Config->mPort = (this->mDetectPort) ? 0 : this->mPort;
+
+ this->SetupStore(SETUP_SERVER_AUTO, this->mAutoSetup);
+ this->SetupStore(SETUP_SERVER_ENABLED, this->mEnable);
+ this->SetupStore(SETUP_SERVER_INT, this->getInterface(this->mInterfaceIndex));
+ this->SetupStore(SETUP_SERVER_ADDRESS, this->mAddress);
+ this->SetupStore(SETUP_SERVER_PORT, this->mPort);
+
+}
diff --git a/misc/menusetup.h b/misc/menusetup.h
new file mode 100644
index 0000000..ae40e97
--- /dev/null
+++ b/misc/menusetup.h
@@ -0,0 +1,78 @@
+/*
+ * File: menusetup.h
+ * Author: savop
+ *
+ * Created on 19. April 2009, 16:50
+ */
+
+#ifndef _CMENUSETUPUPNP_H
+#define _CMENUSETUPUPNP_H
+
+#include <vdr/plugin.h>
+#include "../server/server.h"
+#include "config.h"
+
+/**
+ * The VDR setup page
+ *
+ * This class shows and manages the settings within the VDR setup OSD
+ *
+ * @author Denis Loh
+ * @version 0.0.1
+ */
+class cMenuSetupUPnP : public cMenuSetupPage {
+public:
+ cMenuSetupUPnP();
+// virtual ~cMenuSetupUPnP();
+ virtual eOSState ProcessKey(eKeys Key);
+protected:
+ virtual void Store(void);
+ void Update(void);
+ void Load(void);
+private:
+ const char* const* getInterfaceList(int *count);
+ int getInterfaceIndex(const char* Interface);
+ const char* getInterface(int Index);
+ cOsdItem *mCtrlBind;
+ cOsdItem *mCtrlEnabled;
+ cOsdItem *mCtrlPort;
+ cOsdItem *mCtrlAutoMode;
+ cUPnPServer* mUpnpServer;
+ /**
+ * Is the server enabled or not
+ *
+ * The server can be switched on or off. If it is turned off, the server
+ * will close open transmissions and ports
+ *
+ */
+ int mEnable;
+ int mAutoSetup;
+ /**
+ * The port to listen to (Default: 0 autodetect)
+ *
+ * The port the server is bound to. The default setting is 0.
+ * So, the server will determine automatically a free random port between
+ * 49152 and 65535. If a server should use a specific port it can be set
+ * to one out of that range.
+ *
+ */
+ int mPort;
+ int mDetectPort;
+ /**
+ * The Interface the server is bound to
+ *
+ * If multiple interfaces exist the server can be bound to a specific
+ * one
+ *
+ */
+ int mInterfaceIndex;
+ /**
+ * The socket address of the server
+ *
+ * The IP address and the port of the server
+ */
+ char *mAddress;
+};
+
+#endif /* _CMENUSETUPUPNP_H */
+
diff --git a/misc/search.cpp b/misc/search.cpp
new file mode 100644
index 0000000..deee8b0
--- /dev/null
+++ b/misc/search.cpp
@@ -0,0 +1,795 @@
+/*
+ * File: search.cpp
+ * Author: savop
+ *
+ * Created on 27. August 2009, 21:21
+ */
+
+// uncomment this to enable debuging of the grammar
+//#define BOOST_SPIRIT_DEBUG
+
+#include <string>
+#include "search.h"
+#include "../common.h"
+#include <boost/spirit.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+using namespace std;
+using namespace boost;
+using namespace boost::spirit;
+
+// This is the standard callback function which will be overloaded
+// with all the specific callbacks
+typedef function2<void, const char*, const char*> expCallback;
+typedef function1<void, const char*> propCallback;
+typedef function1<void, const char*> opCallback;
+typedef function1<void, const char> charCallback;
+typedef function1<void, int> intCallback;
+
+// The defined ColumnNames
+struct cProperties : symbols<const char*> {
+//struct cProperties : symbols<> {
+ cProperties(){
+ add
+// (UPNP_PROP_OBJECTID, UPNP_PROP_OBJECTID)
+// (UPNP_PROP_PARENTID, UPNP_PROP_PARENTID)
+// (UPNP_PROP_RESTRICTED, UPNP_PROP_RESTRICTED)
+// (UPNP_PROP_CLASS, UPNP_PROP_CLASS)
+// (UPNP_PROP_CLASSNAME, UPNP_PROP_CLASSNAME)
+// (UPNP_PROP_WRITESTATUS, UPNP_PROP_WRITESTATUS)
+// (UPNP_PROP_CHILDCOUNT, UPNP_PROP_CHILDCOUNT)
+// (UPNP_PROP_REFERENCEID, UPNP_PROP_REFERENCEID)
+// (UPNP_PROP_TITLE, UPNP_PROP_TITLE)
+// (UPNP_PROP_CREATOR, UPNP_PROP_CREATOR)
+// ("dc:description", "Description")
+// ("dc:date", "Date")
+// ("res", "Resource")
+// ("res@bitrate", "Bitrate")
+// ("res@duration", "Duration")
+// ("res@size", "Size")
+// ("res@sampleFrequency", "SampleFrequency")
+// ("res@resolution", "Resolution")
+// ("res@protocolInfo", "ProtocolInfo")
+// ;
+ (UPNP_PROP_OBJECTID,UPNP_PROP_OBJECTID)
+ (UPNP_PROP_PARENTID,UPNP_PROP_PARENTID)
+ (UPNP_PROP_TITLE,UPNP_PROP_TITLE)
+ (UPNP_PROP_CREATOR,UPNP_PROP_CREATOR)
+ (UPNP_PROP_RESTRICTED,UPNP_PROP_RESTRICTED)
+ (UPNP_PROP_WRITESTATUS,UPNP_PROP_WRITESTATUS)
+ (UPNP_PROP_CLASS,UPNP_PROP_CLASS)
+ (UPNP_PROP_CLASSNAME,UPNP_PROP_CLASSNAME)
+ (UPNP_PROP_SEARCHCLASS,UPNP_PROP_SEARCHCLASS)
+ (UPNP_PROP_SCLASSDERIVED,UPNP_PROP_SCLASSDERIVED)
+ (UPNP_PROP_REFERENCEID,UPNP_PROP_REFERENCEID)
+ (UPNP_PROP_SCLASSNAME,UPNP_PROP_SCLASSNAME)
+ (UPNP_PROP_SEARCHABLE,UPNP_PROP_SEARCHABLE)
+ (UPNP_PROP_CHILDCOUNT,UPNP_PROP_CHILDCOUNT)
+ (UPNP_PROP_RESOURCE,UPNP_PROP_RESOURCE)
+ (UPNP_PROP_PROTOCOLINFO,UPNP_PROP_PROTOCOLINFO)
+ (UPNP_PROP_SIZE,UPNP_PROP_SIZE)
+ (UPNP_PROP_DURATION,UPNP_PROP_DURATION)
+ (UPNP_PROP_BITRATE,UPNP_PROP_BITRATE)
+ (UPNP_PROP_SAMPLEFREQUENCE,UPNP_PROP_SAMPLEFREQUENCE)
+ (UPNP_PROP_BITSPERSAMPLE,UPNP_PROP_BITSPERSAMPLE)
+ (UPNP_PROP_NOAUDIOCHANNELS,UPNP_PROP_NOAUDIOCHANNELS)
+ (UPNP_PROP_COLORDEPTH,UPNP_PROP_COLORDEPTH)
+ (UPNP_PROP_RESOLUTION,UPNP_PROP_RESOLUTION)
+ (UPNP_PROP_GENRE,UPNP_PROP_GENRE)
+ (UPNP_PROP_LONGDESCRIPTION,UPNP_PROP_LONGDESCRIPTION)
+ (UPNP_PROP_PRODUCER,UPNP_PROP_PRODUCER)
+ (UPNP_PROP_RATING,UPNP_PROP_RATING)
+ (UPNP_PROP_ACTOR,UPNP_PROP_ACTOR)
+ (UPNP_PROP_DIRECTOR,UPNP_PROP_DIRECTOR)
+ (UPNP_PROP_DESCRIPTION,UPNP_PROP_DESCRIPTION)
+ (UPNP_PROP_PUBLISHER,UPNP_PROP_PUBLISHER)
+ (UPNP_PROP_LANGUAGE,UPNP_PROP_LANGUAGE)
+ (UPNP_PROP_RELATION,UPNP_PROP_RELATION)
+ (UPNP_PROP_STORAGEMEDIUM,UPNP_PROP_STORAGEMEDIUM)
+ (UPNP_PROP_DVDREGIONCODE,UPNP_PROP_DVDREGIONCODE)
+ (UPNP_PROP_CHANNELNAME,UPNP_PROP_CHANNELNAME)
+ (UPNP_PROP_SCHEDULEDSTARTTIME,UPNP_PROP_SCHEDULEDSTARTTIME)
+ (UPNP_PROP_SCHEDULEDENDTIME,UPNP_PROP_SCHEDULEDENDTIME)
+ (UPNP_PROP_ICON,UPNP_PROP_ICON)
+ (UPNP_PROP_REGION,UPNP_PROP_REGION)
+ (UPNP_PROP_CHANNELNR,UPNP_PROP_CHANNELNR)
+ (UPNP_PROP_RIGHTS,UPNP_PROP_RIGHTS)
+ (UPNP_PROP_RADIOCALLSIGN,UPNP_PROP_RADIOCALLSIGN)
+ (UPNP_PROP_RADIOSTATIONID,UPNP_PROP_RADIOSTATIONID)
+ (UPNP_PROP_RADIOBAND,UPNP_PROP_RADIOBAND)
+ (UPNP_PROP_CONTRIBUTOR,UPNP_PROP_CONTRIBUTOR)
+ (UPNP_PROP_DATE,UPNP_PROP_DATE)
+ (UPNP_PROP_ALBUM,UPNP_PROP_ALBUM)
+ (UPNP_PROP_ARTIST,UPNP_PROP_ARTIST)
+ (UPNP_PROP_DLNA_CONTAINERTYPE,UPNP_PROP_DLNA_CONTAINERTYPE)
+ ;
+ }
+} Properties;
+
+struct cOperators : symbols<const char*> {
+ cOperators(){
+ add
+ ("=", "==")
+ ("!=", "!=")
+ ("<", "<")
+ (">", ">")
+ ("<=", "<=")
+ (">=", ">=")
+ ("contains", "LIKE")
+ ("doesNotContain", "NOT LIKE")
+ ("derivedfrom", "derivedFrom")
+ ;
+ }
+} Operators;
+
+struct cConcatOperators : symbols<const char*> {
+ cConcatOperators(){
+ add
+ ("and", "AND")
+ ("or", "OR")
+ ;
+ }
+} ConcatOperators;
+
+struct cExistanceOperator : symbols<const char*> {
+ cExistanceOperator(){
+ add
+ ("true", "NOT NULL")
+ ("false", "NULL")
+ ;
+ }
+} Existance;
+
+// THE GRAMMAR!
+// This is the grammar including the functors which calls the member functions
+// of search. The callback definitions at the end of the constructor are
+// essential. DO NOT MODIFY if you don't know how!
+struct cSearchGrammar : public boost::spirit::grammar<cSearchGrammar> {
+ // The callbacks members
+ charCallback &endBrackedExp;
+ expCallback &pushSimpleExp;
+ opCallback &pushOperator;
+ expCallback &pushQuotedValue;
+ opCallback &pushExistance;
+ propCallback &pushProperty;
+ opCallback &pushConcatOp;
+ charCallback &startBrackedExp;
+
+ // Constructor with the callback functions
+ cSearchGrammar(
+ charCallback &endBrackedExp,
+ expCallback &pushSimpleExp,
+ opCallback &pushOperator,
+ expCallback &pushQuotedValue,
+ opCallback &pushExistance,
+ propCallback &pushProperty,
+ opCallback &pushConcatOp,
+ charCallback &startBrackedExp):
+ endBrackedExp(endBrackedExp),
+ pushSimpleExp(pushSimpleExp),
+ pushOperator(pushOperator),
+ pushQuotedValue(pushQuotedValue),
+ pushExistance(pushExistance),
+ pushProperty(pushProperty),
+ pushConcatOp(pushConcatOp),
+ startBrackedExp(startBrackedExp){}
+
+ template <typename scanner>
+ struct definition {
+ boost::spirit::rule<scanner> searchCrit, searchExp, logOp, \
+ relExp, binOp, relOp, stringOp, \
+ existsOp, boolVal, quotedVal, \
+ wChar, property, brackedExp, exp;
+ const boost::spirit::rule<scanner> &start(){
+ return searchCrit;
+ }
+ definition(const cSearchGrammar &self){
+ /*************************************************************************\
+ * *
+ * The grammar of a UPnP search expression *
+ * *
+ * searchCrit ::= searchExp | asterisk *
+ * *
+ * searchExp ::= relExp | *
+ * searchExp wChar+ logOp wChar+ searchExp | *
+ * '(' wChar* searchExp wChar* ')' *
+ * *
+ * logOp ::= 'and' | 'or' *
+ * *
+ * relExp ::= property wChar+ binOp wChar+ quotedVal | *
+ * property wChar* existsOp wChar+ boolVal *
+ * *
+ * binOp ::= relOp | stringOp *
+ * *
+ * relOp ::= '=' | '!=' | '<' | '<=' | '>' | '>=' *
+ * *
+ * stringOp ::= 'contains' | 'doesNotContain' | 'derivedfrom' *
+ * *
+ * existsOp ::= 'exists' *
+ * *
+ * boolVal ::= 'true' | 'false' *
+ * *
+ * quotedVal ::= dQuote escapedQuote dQuote *
+ * *
+ * wChar ::= space | hTab | lineFeed | *
+ * vTab | formFeed | return *
+ * *
+ * property ::= See ContentDirectory Section 2.4 *
+ * *
+ * escapedQuote ::= See ContentDirectory Section 2.3.1 *
+ * *
+ \*************************************************************************/
+ searchCrit = searchExp | "*";
+
+ searchExp = exp >> *(+wChar >> logOp >> +wChar >> exp);
+ ;
+
+ exp = relExp
+ | brackedExp
+ ;
+
+ brackedExp = confix_p(
+ ch_p('(')[self.startBrackedExp],
+ *wChar >> searchExp >> *wChar,
+ ch_p(')')[self.endBrackedExp]
+ )
+ ;
+
+ logOp = ConcatOperators[self.pushConcatOp]
+ ;
+
+ relExp = (property >> +wChar >> binOp >> +wChar >> quotedVal) [self.pushSimpleExp]
+ | (property >> +wChar >> existsOp >> +wChar >> boolVal) [self.pushSimpleExp]
+ ;
+
+ binOp = Operators[self.pushOperator]
+ ;
+
+ existsOp = str_p("exists")
+ ;
+
+ boolVal = Existance[self.pushExistance]
+ ;
+
+ quotedVal = confix_p('"', (*c_escape_ch_p)[self.pushQuotedValue], '"');
+
+ wChar = space_p;
+
+ property = Properties[self.pushProperty]
+ ;
+
+ // Debug mode
+ #ifdef BOOST_SPIRIT_DEBUG
+ BOOST_SPIRIT_DEBUG_NODE(searchCrit);
+ BOOST_SPIRIT_DEBUG_NODE(searchExp);
+ BOOST_SPIRIT_DEBUG_NODE(logOp);
+ BOOST_SPIRIT_DEBUG_NODE(relExp);
+ BOOST_SPIRIT_DEBUG_NODE(binOp);
+ BOOST_SPIRIT_DEBUG_NODE(relOp);
+ BOOST_SPIRIT_DEBUG_NODE(stringOp);
+ BOOST_SPIRIT_DEBUG_NODE(existsOp);
+ BOOST_SPIRIT_DEBUG_NODE(boolVal);
+ BOOST_SPIRIT_DEBUG_NODE(quotedVal);
+ BOOST_SPIRIT_DEBUG_NODE(wChar);
+ BOOST_SPIRIT_DEBUG_NODE(property);
+ #endif
+ }
+ };
+};
+
+struct cSortGrammar : public boost::spirit::grammar<cSortGrammar> {
+ // The callback members
+ propCallback &pushProperty;
+ charCallback &pushDirection;
+
+ cSortGrammar(
+ propCallback &pushProperty,
+ charCallback &pushDirection):
+ pushProperty(pushProperty),
+ pushDirection(pushDirection){}
+
+ template <typename scanner>
+ struct definition {
+ boost::spirit::rule<scanner> sortCrit, sortExp, property, direction;
+
+ const boost::spirit::rule<scanner> &start(){
+ return sortCrit;
+ }
+ definition(const cSortGrammar &self){
+ sortCrit = sortExp
+ ;
+
+ sortExp = direction >> property >> *(ch_p(',') >> sortExp)
+ ;
+
+ direction = sign_p[self.pushDirection]
+ ;
+
+ property = Properties[self.pushProperty]
+ ;
+ }
+ };
+};
+
+struct cFilterGrammar : public boost::spirit::grammar<cFilterGrammar> {
+ // The callback members
+ propCallback &pushProperty;
+ charCallback &pushAsterisk;
+
+ cFilterGrammar(
+ propCallback &pushProperty,
+ charCallback &pushAsterisk):
+ pushProperty(pushProperty),
+ pushAsterisk(pushAsterisk){}
+
+ template <typename scanner>
+ struct definition {
+ boost::spirit::rule<scanner> filterCrit, filterExp, property;
+
+ const boost::spirit::rule<scanner> &start(){
+ return filterCrit;
+ }
+ definition(const cFilterGrammar &self){
+ filterCrit = filterExp
+ | ch_p('*')[self.pushAsterisk]
+ ;
+
+ filterExp = property >> *(ch_p(',') >> filterExp)
+ ;
+
+ property = Properties[self.pushProperty]
+ ;
+ }
+ };
+};
+
+ /**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cSearch::pushEndBrackedExp(const char){
+ MESSAGE("Pushing closing bracket");
+ if(asprintf(&this->SQLWhereStmt, "%s)", this->SQLWhereStmt)==-1){
+ ERROR("Unable to allocate SQL Statement");
+ return;
+ }
+}
+
+void cSearch::pushExpression(const char*, const char*){
+
+ const char* Property = this->CurrentProperty;
+ const char* Operator = this->CurrentOperator;
+ const char* Value = this->CurrentValue;
+
+ if(Property && Operator && Value){
+ char* Statement;
+ long int IntegerValue;
+
+ if(sscanf(Value, "%ld", &IntegerValue)!=EOF && sscanf(Value, "%*4d-%*2d-%*2d")==EOF){
+ MESSAGE("Popping '%s %s %ld'",Property, Operator, IntegerValue);
+ if(asprintf(&Statement, "%s %s %ld", Property, Operator, IntegerValue)==-1){
+ ERROR("Failed to allocated memory for statement.");
+ return;
+ }
+ }
+ else if(!strcasecmp(Operator, "IS")){
+ MESSAGE("Popping '%s %s %s'", Property, Operator, Value);
+ if(asprintf(&Statement, "%s %s %s", Property, Operator, Value)==-1){
+ ERROR("Failed to allocated memory for statement.");
+ return;
+ }
+ }
+ else {
+ MESSAGE("Popping '%s %s \"%s\"'",Property, Operator, Value);
+ if(asprintf(&Statement, "%s %s '%s'", Property, Operator, Value)==-1){
+ ERROR("Failed to allocated memory for statement.");
+ return;
+ }
+ }
+
+ if(asprintf(&this->SQLWhereStmt, "%s %s", this->SQLWhereStmt, Statement)==-1){
+ ERROR("Unable to allocate SQL Statement");
+ return;
+ }
+
+ }
+ return;
+}
+
+void cSearch::pushProperty(const char* Property){
+ this->CurrentProperty = strdup(Property);
+ MESSAGE("Property %s added",Property);
+}
+
+void cSearch::pushOperator(const char* Operator){
+ this->CurrentOperator = strdup(Operator);
+ MESSAGE("Operator %s added",Operator);
+}
+
+void cSearch::pushValue(const char* Start, const char* End){
+ const char* Value = string(Start,End).c_str();
+ if(!Value || !strcmp(Value,"")) return;
+
+ this->CurrentValue = strdup(Value);
+ MESSAGE("Value %s added", Value);
+}
+
+void cSearch::pushExistance(const char* Exists){
+ this->CurrentValue = strdup(Exists);
+ this->CurrentOperator = strdup("IS");
+ MESSAGE("Existance expression called. '%s'", Exists);
+}
+
+void cSearch::pushConcatOp(const char* Operator){
+ if(asprintf(&this->SQLWhereStmt, "%s %s ", this->SQLWhereStmt, Operator)==-1){
+ ERROR("Unable to allocate SQL Statement");
+ return;
+ }
+
+ MESSAGE("Concatenation expression called. '%s'", Operator);
+}
+
+void cSearch::pushStartBrackedExp(const char){
+ MESSAGE("Pushing opening bracket");
+ if(asprintf(&this->SQLWhereStmt, "%s(", this->SQLWhereStmt)==-1){
+ ERROR("Unable to allocate SQL Statement");
+ return;
+ }
+}
+
+ /**********************************************\
+ * *
+ * The rest *
+ * *
+ \**********************************************/
+
+cSearch* cSearch::mInstance = NULL;
+
+const char* cSearch::parse(const char* Search){
+ if(!cSearch::mInstance) cSearch::mInstance = new cSearch();
+
+ if(cSearch::mInstance && cSearch::mInstance->parseCriteria(Search)){
+ return cSearch::mInstance->SQLWhereStmt;
+ }
+ return NULL;
+}
+
+bool cSearch::parseCriteria(const char* Search){
+
+ charCallback endBrackedExpCB(bind(&cSearch::pushEndBrackedExp, this, _1));
+ expCallback pushSimpleExpCB(bind(&cSearch::pushExpression, this, _1, _2));
+ opCallback pushOperatorCB(bind(&cSearch::pushOperator, this, _1));
+ expCallback pushQuotedValueCB(bind(&cSearch::pushValue, this, _1, _2));
+ opCallback pushExistanceCB(bind(&cSearch::pushExistance, this, _1));
+ propCallback pushPropertyCB(bind(&cSearch::pushProperty, this, _1));
+ opCallback pushConcatOpCB(bind(&cSearch::pushConcatOp, this, _1));
+ charCallback startBrackedExpCB(bind(&cSearch::pushStartBrackedExp, this, _1));
+
+ // Craft the grammar
+ cSearchGrammar Grammar(endBrackedExpCB,
+ pushSimpleExpCB,
+ pushOperatorCB,
+ pushQuotedValueCB,
+ pushExistanceCB,
+ pushPropertyCB,
+ pushConcatOpCB,
+ startBrackedExpCB);
+
+ MESSAGE("Starting search parsing");
+
+ if(boost::spirit::parse(Search, Grammar).full){
+ MESSAGE("Parsing successful");
+ }
+ else {
+ ERROR("Parsing failed");
+ return false;
+ }
+ return true;
+}
+
+cSearch::cSearch(){
+ this->CurrentOperator = NULL;
+ this->CurrentProperty = NULL;
+ this->CurrentValue = NULL;
+ this->SQLWhereStmt = strdup("");
+}
+
+cSearch::~cSearch(){
+ delete this->CurrentOperator;
+ delete this->CurrentProperty;
+ delete this->CurrentValue;
+}
+
+ /**********************************************\
+ * *
+ * The filter *
+ * *
+ \**********************************************/
+
+cFilterCriteria::cFilterCriteria(){
+ this->mFilterList = NULL;
+}
+
+cFilterCriteria::~cFilterCriteria(){}
+
+cStringList* cFilterCriteria::parse(const char* Filter){
+ cFilterCriteria* FilterParser = new cFilterCriteria;
+ cStringList* List = NULL;
+
+ if(FilterParser && FilterParser->parseFilter(Filter)){
+ List = FilterParser->getFilterList();
+ }
+
+ delete FilterParser;
+
+ return List;
+}
+
+bool cFilterCriteria::parseFilter(const char* Filter){
+ this->mFilterList = new cStringList;
+
+ if(Filter && !strcasecmp(Filter, "")){
+ MESSAGE("Empty filter");
+ return true;
+ }
+
+ charCallback pushAsteriskCB(bind(&cFilterCriteria::pushAsterisk,this,_1));
+ propCallback pushPropertyCB(bind(&cFilterCriteria::pushProperty,this,_1));
+
+ cFilterGrammar Grammar(pushPropertyCB, pushAsteriskCB);
+
+ if(boost::spirit::parse(Filter, Grammar).full){
+ MESSAGE("Parse filter successful");
+ }
+ else {
+ ERROR("Parsing filter failed");
+ return false;
+ }
+ return true;
+}
+
+ /**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cFilterCriteria::pushProperty(const char* Property){
+ MESSAGE("Pushing property");
+ this->mFilterList->Append(strdup(Property));
+}
+
+void cFilterCriteria::pushAsterisk(const char){
+ MESSAGE("Pushing asterisk (*)");
+ if(this->mFilterList) delete this->mFilterList;
+ this->mFilterList = NULL;
+ return;
+}
+
+ /**********************************************\
+ * *
+ * The sorter *
+ * *
+ \**********************************************/
+
+cSortCriteria::cSortCriteria(){
+ this->mCriteriaList = new cList<cSortCrit>;
+ this->mCurrentCrit = NULL;
+}
+
+cSortCriteria::~cSortCriteria(){}
+
+cList<cSortCrit>* cSortCriteria::parse(const char* Sort){
+ cSortCriteria* SortParser = new cSortCriteria;
+ cList<cSortCrit>* List = NULL;
+ if(SortParser && SortParser->parseSort(Sort)){
+ List = SortParser->getSortList();
+ }
+
+ delete SortParser;
+
+ return List;
+}
+
+bool cSortCriteria::parseSort(const char* Sort){
+ if(!Sort || !strcasecmp(Sort, "")){
+ MESSAGE("Empty Sort");
+ return true;
+ }
+
+ charCallback pushDirectionCB(bind(&cSortCriteria::pushDirection,this,_1));
+ propCallback pushPropertyCB(bind(&cSortCriteria::pushProperty,this,_1));
+
+ cSortGrammar Grammar(pushPropertyCB, pushDirectionCB);
+
+ if(boost::spirit::parse(Sort, Grammar).full){
+ MESSAGE("Parse Sort successful");
+ }
+ else {
+ ERROR("Parsing Sort failed");
+ return false;
+ }
+ return true;
+}
+
+ /**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cSortCriteria::pushProperty(const char* Property){
+ MESSAGE("Pushing property '%s'", Property);
+ this->mCurrentCrit->Property = strdup(Property);
+ this->mCriteriaList->Add(this->mCurrentCrit);
+ return;
+}
+
+void cSortCriteria::pushDirection(const char Direction){
+ MESSAGE("Pushing direction '%c'", Direction);
+ this->mCurrentCrit = new cSortCrit;
+ this->mCurrentCrit->SortDescending = (Direction=='-')?true:false;
+ return;
+}
+
+ /**********************************************\
+ * *
+ * The pathparser *
+ * *
+ \**********************************************/
+
+struct cWebserverSections : symbols<> {
+ cWebserverSections(){
+ add
+ (UPNP_DIR_SHARES)
+ ;
+ }
+} WebserverSections;
+
+struct cWebserverMethods : symbols<int> {
+ cWebserverMethods(){
+ add
+ ("browse", UPNP_WEB_METHOD_BROWSE)
+ ("download", UPNP_WEB_METHOD_DOWNLOAD)
+ ("search", UPNP_WEB_METHOD_SEARCH)
+ ("show", UPNP_WEB_METHOD_SHOW)
+ ("get", UPNP_WEB_METHOD_STREAM)
+ ;
+ }
+} WebserverMethods;
+
+struct cPathParserGrammar : public boost::spirit::grammar<cPathParserGrammar> {
+
+ intCallback &pushSection;
+ intCallback &pushMethod;
+ expCallback &pushPropertyKey;
+ expCallback &pushPropertyValue;
+
+ cPathParserGrammar(intCallback &pushSection,
+ intCallback &pushMethod,
+ expCallback &pushPropertyKey,
+ expCallback &pushPropertyValue):
+ pushSection(pushSection),
+ pushMethod(pushMethod),
+ pushPropertyKey(pushPropertyKey),
+ pushPropertyValue(pushPropertyValue){}
+
+ template <typename scanner>
+ struct definition {
+ boost::spirit::rule<scanner> pathExp, section, method, methodProperties,
+ property, key, value, uncriticalChar;
+
+ const boost::spirit::rule<scanner> &start(){
+ return pathExp;
+ }
+ definition(const cPathParserGrammar &self){
+ pathExp = section >> ch_p('/') >> method >> ch_p('?') >> methodProperties
+ ;
+
+ section = WebserverSections[self.pushSection]
+ ;
+
+ method = WebserverMethods[self.pushMethod]
+ ;
+
+ methodProperties = property >> *(ch_p('&') >> methodProperties)
+ ;
+
+ property = key >> ch_p('=') >> value
+ ;
+
+ key = (+alnum_p)[self.pushPropertyKey]
+ ;
+
+ value = (*uncriticalChar)[self.pushPropertyValue]
+ ;
+
+ uncriticalChar = chset_p("-_.%~0-9A-Za-z")
+ ;
+ }
+ };
+};
+
+cPathParser::cPathParser(){
+ this->mSection = 0;
+ this->mMethod = 0;
+}
+
+cPathParser::~cPathParser(){}
+
+bool cPathParser::parse(const char* Path, int* Section, int* Method, propertyMap* Properties){
+ cPathParser* Parser = new cPathParser();
+ bool ret = (Parser && Parser->parsePath(Path, Section, Method, Properties)) ? true : false;
+
+ delete Parser;
+
+ return ret;
+}
+
+bool cPathParser::parsePath(const char* Path, int* Section, int* Method, propertyMap* Properties){
+ if(!Path){
+ return false;
+ }
+
+ intCallback pushSectionCB(bind(&cPathParser::pushSection,this,_1));
+ intCallback pushMethodCB(bind(&cPathParser::pushMethod,this,_1));
+ expCallback pushPropertyKeyCB(bind(&cPathParser::pushPropertyKey, this, _1, _2));
+ expCallback pushPropertyValueCB(bind(&cPathParser::pushPropertyValue, this, _1, _2));
+
+ cPathParserGrammar Grammar(pushSectionCB, pushMethodCB, pushPropertyKeyCB, pushPropertyValueCB);
+
+ if(boost::spirit::parse(Path, Grammar).full){
+ MESSAGE("Parse path successful");
+ *Section = this->mSection;
+ *Method = this->mMethod;
+ *Properties = this->mProperties;
+ return true;
+ }
+ else {
+ ERROR("Parsing path failed");
+ return false;
+ }
+
+ return true;
+}
+
+ /**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cPathParser::pushPropertyKey(const char* Start, const char* End){
+ char* Key = strndup(Start, End-Start);
+
+ MESSAGE("Pushing key '%s'", Key);
+
+ this->mKey = Key;
+
+ free(Key);
+}
+
+void cPathParser::pushPropertyValue(const char* Start, const char* End){
+ char* Value = strndup(Start, End-Start);
+
+ MESSAGE("Pushing value '%s'", Value);
+ // TODO: urlDecode Value
+
+ if(*this->mKey){
+ char* Key = strdup(this->mKey);
+ this->mProperties[Key] = Value;
+ }
+}
+
+void cPathParser::pushMethod(int Method){
+ MESSAGE("Pushing method '%d'", Method);
+ this->mMethod = Method;
+}
+
+void cPathParser::pushSection(int Section){
+ MESSAGE("Pushing section '%d'", Section);
+ this->mSection = Section;
+} \ No newline at end of file
diff --git a/misc/search.h b/misc/search.h
new file mode 100644
index 0000000..df2442e
--- /dev/null
+++ b/misc/search.h
@@ -0,0 +1,90 @@
+/*
+ * File: search.h
+ * Author: savop
+ *
+ * Created on 27. August 2009, 21:21
+ */
+
+#ifndef _SEARCH_H
+#define _SEARCH_H
+
+#include <map>
+#include <vdr/tools.h>
+#include "util.h"
+
+struct cSortCrit : public cListObject {
+ const char* Property;
+ bool SortDescending;
+};
+
+typedef std::map<const char*, const char*, strCmp> propertyMap;
+
+class cPathParser {
+private:
+ cString mKey;
+ propertyMap mProperties;
+ int mSection;
+ int mMethod;
+ bool parsePath(const char* Path, int* Section, int* Method, propertyMap* Properties);
+ void pushPropertyKey(const char* Start, const char* End);
+ void pushPropertyValue(const char* Start, const char* End);
+ void pushMethod(int Method);
+ void pushSection(int Section);
+ cPathParser();
+public:
+ virtual ~cPathParser();
+ static bool parse(const char* Path, int* Section, int* Method, propertyMap* Properties);
+};
+
+class cSortCriteria {
+private:
+ cSortCrit* mCurrentCrit;
+ cList<cSortCrit>* mCriteriaList;
+ bool parseSort(const char* Sort);
+ void pushProperty(const char* Property);
+ void pushDirection(const char Direction);
+ cList<cSortCrit>* getSortList() const { return this->mCriteriaList; }
+ cSortCriteria();
+public:
+ virtual ~cSortCriteria();
+ static cList<cSortCrit>* parse(const char* Sort);
+};
+
+class cFilterCriteria {
+private:
+ cStringList* mFilterList;
+ cFilterCriteria();
+ bool parseFilter(const char* Filter);
+ void pushProperty(const char* Property);
+ void pushAsterisk(const char Asterisk);
+ cStringList* getFilterList() const { return this->mFilterList; }
+public:
+ virtual ~cFilterCriteria();
+ static cStringList* parse(const char* Filter);
+};
+
+class cSearch {
+private:
+ char* SQLWhereStmt;
+ const char* CurrentProperty;
+ const char* CurrentOperator;
+ const char* CurrentValue;
+ static cSearch* mInstance;
+ cSearch();
+ bool parseCriteria(const char* Search);
+ void pushExistance (const char* Exists);
+ void pushProperty (const char* Property);
+ void pushOperator (const char* Operator);
+ void pushConcatOp (const char* Operator);
+ void pushStartBrackedExp(const char);
+ void pushEndBrackedExp(const char);
+ void pushValue (const char* Start, const char* End);
+ void pushExpression(const char* Start, const char* End);
+public:
+ virtual ~cSearch();
+ static const char* parse(const char* Search);
+};
+
+
+#endif /* _SEARCH_H */
+
diff --git a/misc/util.cpp b/misc/util.cpp
new file mode 100644
index 0000000..dc75706
--- /dev/null
+++ b/misc/util.cpp
@@ -0,0 +1,488 @@
+/*
+ * File: util.cpp
+ * Author: savop, andreas
+ *
+ * Created on 21. Mai 2009, 21:25
+ *
+ * Extracted from streamdev-server plugin common.c
+ * $Id: common.c,v 1.6 2008/03/31 10:34:26 schmirl Exp $
+ */
+#include "util.h"
+#include "../common.h"
+#include <string.h>
+#include <string>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <upnp/ixml.h>
+#include <arpa/inet.h>
+#include <iosfwd>
+
+char* substr(const char* str, unsigned int offset, unsigned int length){
+ if(offset > strlen(str)) return NULL;
+ if(length > strlen(str+offset)) length = strlen(str+offset);
+ char* substring = (char*)malloc(sizeof(substring)*length+1);
+ strncpy(substring, str+offset, length);
+ substring[length] = '\0';
+ return substring;
+}
+
+const char* getMACFromInterface(const char* Interface) {
+ int fd;
+ struct ifreq ifr;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, Interface, IFNAMSIZ-1);
+
+ ioctl(fd, SIOCGIFHWADDR, &ifr);
+
+ close(fd);
+
+ char *ret = new char[18];
+
+ sprintf(ret, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+ (unsigned char)ifr.ifr_hwaddr.sa_data[0],
+ (unsigned char)ifr.ifr_hwaddr.sa_data[1],
+ (unsigned char)ifr.ifr_hwaddr.sa_data[2],
+ (unsigned char)ifr.ifr_hwaddr.sa_data[3],
+ (unsigned char)ifr.ifr_hwaddr.sa_data[4],
+ (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
+
+ return ret;
+}
+
+char** getNetworkInterfaces(int *count){
+ int fd;
+ struct ifconf ifc;
+ struct ifreq ifr[10];
+ int nifaces, i;
+ char** ifaces;
+ *count = 0;
+
+ memset(&ifc,0,sizeof(ifc));
+ ifc.ifc_buf = (char*) (ifr);
+ ifc.ifc_len = sizeof(ifr);
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ int ret = ioctl(fd, SIOCGIFCONF, &ifc);
+ close(fd);
+ if(ret==0){
+ nifaces = ifc.ifc_len/sizeof(struct ifreq);
+ ifaces = new char* [nifaces+1];
+ for(i = 0; i < nifaces; i++){
+ ifaces[i] = new char[IFNAMSIZ];
+ ifaces[i] = strdup(ifr[i].ifr_name);
+ }
+ ifaces[i] = NULL;
+ *count = nifaces;
+ return ifaces;
+ }
+ else {
+ return NULL;
+ }
+}
+
+const sockaddr_in* getIPFromInterface(const char* Interface){
+ if(Interface==NULL) return NULL;
+ int fd;
+ struct ifreq ifr;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ /* I want to get an IPv4 IP address */
+ ifr.ifr_addr.sa_family = AF_INET;
+ /* I want IP address attached to "eth0" */
+ strncpy(ifr.ifr_name, Interface, IFNAMSIZ-1);
+ int ret = ioctl(fd, SIOCGIFADDR, &ifr);
+ close(fd);
+ const sockaddr_in* IpAddress = new sockaddr_in;
+ if(ret==0){
+ IpAddress = (sockaddr_in *)&ifr.ifr_addr;
+ return IpAddress;
+ }
+ else {
+ delete IpAddress;
+ return NULL;
+ }
+}
+
+cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value):cMenuEditItem(Name) {
+ value = Value;
+ curNum = -1;
+ pos = -1;
+ step = false;
+ Set();
+}
+
+cMenuEditIpItem::~cMenuEditIpItem() {
+}
+
+void cMenuEditIpItem::Set(void) {
+ char buf[1000];
+ if (pos >= 0) {
+ in_addr_t addr = inet_addr(value);
+ if ((int)addr == -1)
+ addr = 0;
+ int p = 0;
+ for (int i = 0; i < 4; ++i) {
+ p += snprintf(buf + p, sizeof(buf) - p, pos == i ? "[%d]" : "%d",
+ pos == i ? curNum : (addr >> (i * 8)) & 0xff);
+ if (i < 3)
+ buf[p++] = '.';
+ }
+ SetValue(buf);
+ } else
+ SetValue(value);
+}
+
+eOSState cMenuEditIpItem::ProcessKey(eKeys Key) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+
+ switch (Key) {
+ case kUp:
+ if (pos >= 0) {
+ if (curNum < 255) ++curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kDown:
+ if (pos >= 0) {
+ if (curNum > 0) --curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kOk:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ curNum = -1;
+ pos = -1;
+ break;
+
+ case kRight:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos == -1 || pos == 3)
+ pos = 0;
+ else
+ ++pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case kLeft:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos <= 0)
+ pos = 3;
+ else
+ --pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case k0 ... k9: /* Netbeans reports error with this line (.. is okay but wrong) */
+ if (pos == -1)
+ pos = 0;
+
+ if (curNum == -1 || step) {
+ curNum = Key - k0;
+ step = false;
+ } else
+ curNum = curNum * 10 + (Key - k0);
+
+ if ((curNum * 10 > 255) || (curNum == 0)) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ if (++pos == 4)
+ pos = 0;
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ }
+ break;
+
+ default:
+ return cMenuEditItem::ProcessKey(Key);
+ }
+
+ Set();
+ return osContinue;
+}
+
+const char* escapeSQLite(const char* Data, char** Buf){
+ if(!Data){
+ *Buf = NULL;
+ }
+ else {
+ std::string NewData = "";
+ int Char = 0;
+ for(unsigned int i = 0; i < strlen(Data); i++){
+ Char = Data[i];
+ switch(Char){
+ case L'\'': NewData += "''"; break;
+ default: NewData += Data[i]; break;
+ }
+ }
+ *Buf = strdup(NewData.c_str());
+ }
+ return (*Buf);
+}
+
+const char* escapeXMLCharacters(const char* Data, char** Buf){
+ if(Data==NULL){
+ ERROR("Escape XML: No data to escape");
+ return NULL;
+ }
+ std::string NewData = "";
+ int Char = 0;
+ for(unsigned int i = 0; i < strlen(Data); i++){
+ Char = Data[i];
+ switch(Char){
+ case L'€': NewData += "&euro;"; break;
+ case L'"': NewData += "&quot;"; break;
+ case L'&': NewData += "&amp;"; break;
+ case L'<': NewData += "&lt;"; break;
+ case L'>': NewData += "&gt;"; break;
+ case L'¡': NewData += "&iexcl;"; break;
+ case L'¢': NewData += "&cent;"; break;
+ case L'£': NewData += "&pound;"; break;
+ case L'¤': NewData += "&curren;"; break;
+ case L'¥': NewData += "&yen;"; break;
+ case L'¦': NewData += "&brvbar;"; break;
+ case L'§': NewData += "&sect;"; break;
+ case L'¨': NewData += "&uml;"; break;
+ case L'©': NewData += "&copy;"; break;
+ case L'ª': NewData += "&ordf;"; break;
+ case L'¬': NewData += "&not;"; break;
+ case L'­': NewData += "&shy;"; break;
+ case L'®': NewData += "&reg;"; break;
+ case L'¯': NewData += "&macr;"; break;
+ case L'°': NewData += "&deg;"; break;
+ case L'±': NewData += "&plusmn;"; break;
+ case L'²': NewData += "&sup2;"; break;
+ case L'³': NewData += "&sup3;"; break;
+ case L'´': NewData += "&acute;"; break;
+ case L'µ': NewData += "&micro;"; break;
+ case L'¶': NewData += "&para;"; break;
+ case L'·': NewData += "&middot;"; break;
+ case L'¸': NewData += "&cedil;"; break;
+ case L'¹': NewData += "&sup1;"; break;
+ case L'º': NewData += "&ordm;"; break;
+ case L'»': NewData += "&raquo;"; break;
+ case L'«': NewData += "&laquo;"; break;
+ case L'¼': NewData += "&frac14;"; break;
+ case L'½': NewData += "&frac12;"; break;
+ case L'¾': NewData += "&frac34;"; break;
+ case L'¿': NewData += "&iquest;"; break;
+ case L'À': NewData += "&Agrave;"; break;
+ case L'Á': NewData += "&Aacute;"; break;
+ case L'Â': NewData += "&Acirc;"; break;
+ case L'Ã': NewData += "&Atilde;"; break;
+ case L'Ä': NewData += "&Auml;"; break;
+ case L'Å': NewData += "&Aring;"; break;
+ case L'Æ': NewData += "&AElig;"; break;
+ case L'Ç': NewData += "&Ccedil;"; break;
+ case L'È': NewData += "&Egrave;"; break;
+ case L'É': NewData += "&Eacute;"; break;
+ case L'Ê': NewData += "&Ecirc;"; break;
+ case L'Ë': NewData += "&Euml;"; break;
+ case L'Ì': NewData += "&Igrave;"; break;
+ case L'Í': NewData += "&Iacute;"; break;
+ case L'Î': NewData += "&Icirc;"; break;
+ case L'Ï': NewData += "&Iuml;"; break;
+ case L'Ð': NewData += "&ETH;"; break;
+ case L'Ñ': NewData += "&Ntilde;"; break;
+ case L'Ò': NewData += "&Ograve;"; break;
+ case L'Ó': NewData += "&Oacute;"; break;
+ case L'Ô': NewData += "&Ocirc;"; break;
+ case L'Õ': NewData += "&Otilde;"; break;
+ case L'Ö': NewData += "&Ouml;"; break;
+ case L'×': NewData += "&times;"; break;
+ case L'Ø': NewData += "&Oslash;"; break;
+ case L'Ù': NewData += "&Ugrave;"; break;
+ case L'Ú': NewData += "&Uacute;"; break;
+ case L'Û': NewData += "&Ucirc;"; break;
+ case L'Ü': NewData += "&Uuml;"; break;
+ case L'Ý': NewData += "&Yacute;"; break;
+ case L'Þ': NewData += "&THORN;"; break;
+ case L'ß': NewData += "&szlig;"; break;
+ case L'à': NewData += "&agrave;"; break;
+ case L'á': NewData += "&aacute;"; break;
+ case L'â': NewData += "&acirc;"; break;
+ case L'ã': NewData += "&atilde;"; break;
+ case L'ä': NewData += "&auml;"; break;
+ case L'å': NewData += "&aring;"; break;
+ case L'æ': NewData += "&aelig;"; break;
+ case L'ç': NewData += "&ccedil;"; break;
+ case L'è': NewData += "&egrave;"; break;
+ case L'é': NewData += "&eacute;"; break;
+ case L'ê': NewData += "&ecirc;"; break;
+ case L'ë': NewData += "&euml;"; break;
+ case L'ì': NewData += "&igrave;"; break;
+ case L'í': NewData += "&iacute;"; break;
+ case L'î': NewData += "&icirc;"; break;
+ case L'ï': NewData += "&iuml;"; break;
+ case L'ð': NewData += "&eth;"; break;
+ case L'ñ': NewData += "&ntilde;"; break;
+ case L'ò': NewData += "&ograve;"; break;
+ case L'ó': NewData += "&oacute;"; break;
+ case L'ô': NewData += "&ocirc;"; break;
+ case L'õ': NewData += "&otilde;"; break;
+ case L'ö': NewData += "&ouml;"; break;
+ case L'÷': NewData += "&divide;"; break;
+ case L'ø': NewData += "&oslash;"; break;
+ case L'ù': NewData += "&ugrave;"; break;
+ case L'ú': NewData += "&uacute;"; break;
+ case L'û': NewData += "&ucirc;"; break;
+ case L'ü': NewData += "&uuml;"; break;
+ case L'ý': NewData += "&yacute;"; break;
+ case L'þ': NewData += "&thorn;"; break;
+ default: NewData += Data[i]; break;
+ }
+ }
+ *Buf = strdup(NewData.c_str());
+ return (*Buf);
+}
+
+//Function copied from Intel SDK
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2000-2003 Intel Corporation
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither name of Intel Corporation nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////
+/********************************************************************************
+ * SampleUtil_GetFirstDocumentItem
+ *
+ * Description:
+ * Given a document node, this routine searches for the first element
+ * named by the input string item, and returns its value as a string.
+ * String must be freed by caller using free.
+ * Parameters:
+ * doc -- The DOM document from which to extract the value
+ * item -- The item to search for
+ *
+ *
+ ********************************************************************************/
+char* ixmlGetFirstDocumentItem( IN IXML_Document * doc, IN const char *item, int* error ) {
+ IXML_NodeList *nodeList = NULL;
+ IXML_Node *textNode = NULL;
+ IXML_Node *tmpNode = NULL;
+
+ char *ret = NULL;
+ *error = 0;
+
+ nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item );
+
+ if( nodeList != NULL ) {
+ if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) {
+
+ textNode = ixmlNode_getFirstChild( tmpNode );
+
+ if(textNode != NULL){
+ ret = strdup( ixmlNode_getNodeValue( textNode ) );
+ }
+ }
+ } else {
+ *error = -1;
+ }
+
+ if( nodeList != NULL) {
+ ixmlNodeList_free( nodeList );
+ }
+
+
+ return ret;
+}
+
+int ixmlAddProperty(IXML_Document* document, IXML_Element* node, const char* upnpproperty, const char* value){
+ if(!node) return -1;
+ IXML_Element* PropertyNode = NULL;
+
+ const char* attribute = att(upnpproperty);
+ const char* property = prop(upnpproperty);
+ if(attribute){
+ if(strcasecmp(property,"")){
+ ixmlElement_setAttribute(node, attribute, value);
+ }
+ else {
+ IXML_NodeList* NodeList = ixmlElement_getElementsByTagName(node, property);
+ if(NodeList!=NULL){
+ IXML_Node* Node = ixmlNodeList_item(NodeList, 0);
+ PropertyNode = (IXML_Element*) ixmlNode_getFirstChild(Node);
+ if(PropertyNode){
+ ixmlElement_setAttribute(PropertyNode, attribute, value);
+ }
+ else {
+ ixmlNodeList_free(NodeList);
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ else {
+ PropertyNode = ixmlDocument_createElement(document, property);
+ IXML_Node* PropertyText = ixmlDocument_createTextNode(document, value);
+ ixmlNode_appendChild((IXML_Node*) PropertyNode, PropertyText);
+ ixmlNode_appendChild((IXML_Node*) node, (IXML_Node*) PropertyNode);
+ }
+ return 0;
+}
diff --git a/misc/util.h b/misc/util.h
new file mode 100644
index 0000000..ffedd67
--- /dev/null
+++ b/misc/util.h
@@ -0,0 +1,45 @@
+/*
+ * File: util.h
+ * Author: savop
+ *
+ * Created on 21. Mai 2009, 21:25
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+#include <upnp/ixml.h>
+
+#ifdef __cplusplus
+extern "C" {
+struct strCmp { bool operator()(const char* s1, const char* s2) const { return (strcmp(s1,s2) < 0); }};
+const sockaddr_in* getIPFromInterface(const char* Interface);
+const char* getMACFromInterface(const char* Interface);
+char** getNetworkInterfaces(int *count);
+char* ixmlGetFirstDocumentItem( IN IXML_Document * doc, IN const char *item, int* error );
+int ixmlAddProperty(IN IXML_Document* document, IN IXML_Element* node, const char* upnpproperty, const char* value );
+char* substr(const char* str, unsigned int offset, unsigned int length);
+}
+#endif
+
+const char* escapeSQLite(const char* Data, char** Buf);
+const char* escapeXMLCharacters(const char* Data, char** Buf);
+
+class cMenuEditIpItem: public cMenuEditItem {
+private:
+ char *value;
+ int curNum;
+ int pos;
+ bool step;
+protected:
+ virtual void Set(void);
+public:
+ cMenuEditIpItem(const char *Name, char *Value); // Value must be 16 bytes
+ ~cMenuEditIpItem();
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif /* _UTIL_H */
+