From 25755892849b2f098553f8cb003ed5172c7acdda Mon Sep 17 00:00:00 2001 From: David Blain Date: Thu, 24 Mar 2011 15:48:56 -0400 Subject: [PATCH] Server Side Scripting added along with other changes. * Added Qt Script support to the Html server allowing for JSP/ASP type server side scripting. See http://mythbackend:6544/ and select the "Sample Pages" menu choice. * Added API support for built in types. Now, QString, bool, int, QFileInfo, QStringList, etc. types can be returned from API methods. This change modifies some of the XML schema produced by these functions. * When an API method is called via http, if the method name starts with Get or Put, it can be left off and the http method (GET / POST) will be used to determine if the get or put version should be called. * GetPreviewImage & GetRecording now respond with a redirect (301) if the wrong host is called. * WSDL support has changed and is still NOT working. More fixes to come. --- mythtv/html/html.pro | 2 +- mythtv/html/index.html | 3 + mythtv/html/js/inspect.js | 39 +++ mythtv/html/samples/index.qsp | 148 +++++++++ mythtv/html/samples/recorded.qsp | 91 ++++++ mythtv/html/setup/js/storagegroups.js | 8 +- mythtv/libs/libmyth/libmyth.pro | 2 +- .../datacontracthelper.h | 18 -- .../datacontracts/connectionInfo.h | 15 +- .../datacontracts/databaseInfo.h | 13 +- .../datacontracts/encoder.h | 13 +- .../datacontracts/encoderList.h | 20 +- .../datacontracts/programAndChannel.h | 59 +++- .../datacontracts/programGuide.h | 21 +- .../datacontracts/programList.h | 21 +- .../datacontracts/recording.h | 11 +- .../datacontracts/settingList.h | 20 +- .../datacontracts/storageGroupDir.h | 11 +- .../datacontracts/storageGroupDirList.h | 20 +- .../datacontracts/stringList.h | 59 ---- .../datacontracts/successFail.h | 59 ---- .../datacontracts/wolInfo.h | 12 +- .../libmythservicecontracts.pro | 2 - .../libs/libmythservicecontracts/service.cpp | 64 ++-- mythtv/libs/libmythservicecontracts/service.h | 16 +- .../services/contentServices.h | 27 +- .../services/dvrServices.h | 10 + .../services/guideServices.h | 13 +- .../services/mythServices.h | 32 +- mythtv/libs/libmythupnp/htmlserver.cpp | 36 ++- mythtv/libs/libmythupnp/htmlserver.h | 10 +- mythtv/libs/libmythupnp/httprequest.cpp | 6 + mythtv/libs/libmythupnp/httprequest.h | 37 +++ mythtv/libs/libmythupnp/httpserver.cpp | 9 + mythtv/libs/libmythupnp/httpserver.h | 4 + mythtv/libs/libmythupnp/libmythupnp.pro | 8 +- .../serializers/jsonSerializer.cpp | 7 +- .../libmythupnp/serializers/jsonSerializer.h | 8 +- .../libmythupnp/serializers/serializer.cpp | 11 +- .../libs/libmythupnp/serializers/serializer.h | 12 +- .../libmythupnp/serializers/soapSerializer.h | 6 +- .../libmythupnp/serializers/xmlSerializer.cpp | 63 +++- .../libmythupnp/serializers/xmlSerializer.h | 10 +- .../libs/libmythupnp/serverSideScripting.cpp | 297 ++++++++++++++++++ mythtv/libs/libmythupnp/serverSideScripting.h | 112 +++++++ mythtv/libs/libmythupnp/servicehost.cpp | 220 +++++++++---- mythtv/libs/libmythupnp/servicehost.h | 2 +- mythtv/libs/libmythupnp/upnp.cpp | 25 ++ mythtv/libs/libmythupnp/upnp.h | 4 + mythtv/libs/libmythupnp/wsdl.cpp | 269 +++++++++++++--- mythtv/libs/libmythupnp/wsdl.h | 20 +- mythtv/programs/mythbackend/mediaserver.cpp | 21 ++ mythtv/programs/mythbackend/mythbackend.pro | 2 +- .../programs/mythbackend/services/content.cpp | 91 +++--- .../programs/mythbackend/services/content.h | 22 +- mythtv/programs/mythbackend/services/dvr.h | 51 +++ .../programs/mythbackend/services/guide.cpp | 19 +- mythtv/programs/mythbackend/services/guide.h | 55 +++- mythtv/programs/mythbackend/services/myth.cpp | 83 ++--- mythtv/programs/mythbackend/services/myth.h | 97 +++++- mythtv/programs/mythfrontend/main.cpp | 5 +- mythtv/programs/mythfrontend/mythfrontend.pro | 2 +- 62 files changed, 1964 insertions(+), 489 deletions(-) create mode 100644 mythtv/html/js/inspect.js create mode 100644 mythtv/html/samples/index.qsp create mode 100644 mythtv/html/samples/recorded.qsp delete mode 100644 mythtv/libs/libmythservicecontracts/datacontracts/stringList.h delete mode 100644 mythtv/libs/libmythservicecontracts/datacontracts/successFail.h create mode 100644 mythtv/libs/libmythupnp/serverSideScripting.cpp create mode 100644 mythtv/libs/libmythupnp/serverSideScripting.h diff --git a/mythtv/html/html.pro b/mythtv/html/html.pro index 46db8ef24fc..26ce10f25c2 100644 --- a/mythtv/html/html.pro +++ b/mythtv/html/html.pro @@ -10,7 +10,7 @@ win32:QMAKE_COPY_DIR = sh ./cpsimple html.path = $${PREFIX}/share/mythtv/html/ html.files = index.html overview.html -html.files += css images js misc setup +html.files += css images js misc setup samples INSTALLS += html diff --git a/mythtv/html/index.html b/mythtv/html/index.html index 92a018811d7..f2aeaef81b5 100644 --- a/mythtv/html/index.html +++ b/mythtv/html/index.html @@ -65,6 +65,9 @@ -->
  • Backend Status
  • WSDL Links
  • +

  • +
  • Sample Pages
  • +
    www.mythtv.org
    diff --git a/mythtv/html/js/inspect.js b/mythtv/html/js/inspect.js new file mode 100644 index 00000000000..c9a9f1756de --- /dev/null +++ b/mythtv/html/js/inspect.js @@ -0,0 +1,39 @@ +function inspect(obj, maxLevels, level) +{ + var str = '', type, msg; + if(level == null) level = 0; + + if(maxLevels == null) maxLevels = 1; + if(maxLevels < 1) + return 'Error: Levels number must be > 0'; + + if(obj == null) + return 'Error: Object NULL'; + + str += ''; + + return str; +} diff --git a/mythtv/html/samples/index.qsp b/mythtv/html/samples/index.qsp new file mode 100644 index 00000000000..2f14c52b055 --- /dev/null +++ b/mythtv/html/samples/index.qsp @@ -0,0 +1,148 @@ + + + +MythTV mythbackend Internal Web Server + + + + + + + + + + + + + + + + + +
    +
    +

    Sample Server Side Script Page...

    +
    +

    +This implementation of Server Side scripting is a simple adaptation of the old jsp & asp model. +It leverages the Qt Script Engine and exposes all API Classes to the running script. +

    +

    +Server side scripting is accomplished by wrapping script code that you wish to be executed on the server with <% and %> +

    +

    +Values generated from the script can be inserted into the rendered html page using the following approaches: +

    +
  • From within script code:
  • +
    +

    There is an object available with a variable named 'os' that has two methods available write and writeln

    +
    Example:
    +
    os.writeln( "This is text written from within server side script code" );
    +
    +
  • Embedded in html:
  • +
    +

    <%= script variable/statement %>

    +
    + +

    Using the API classes

    +

    To use one of the API classes, it's as simple as assigning a variable to a new instances of the class you want to use. +

    var myth = new Myth()
    +then calling the method: +
    var list = myth.GetHosts();
    +

    +
    + +
    + Hosts: + +
    +
    +
    + List of Settings: + +
    + Value (uses ajax to retrieve value): + +
    +
    +
    + + + diff --git a/mythtv/html/samples/recorded.qsp b/mythtv/html/samples/recorded.qsp new file mode 100644 index 00000000000..b68166dbc50 --- /dev/null +++ b/mythtv/html/samples/recorded.qsp @@ -0,0 +1,91 @@ + + + +MythTV mythbackend Internal Web Server + + + + + + + + + + + + +
    +
    +

    Sample Server Side Script Page... List of Recorded Programs*

    +*This would be better implemented as an ajax call. +
    + +<% + + var oDvr = new Dvr(); + + var list = oDvr.GetRecorded( true, 0, -1); + + for (var nIdx=0; nIdx < list.Programs.length; nIdx++) + { + var program = list.Programs[ nIdx ]; +%> + + + + + + + +<% + } + +%> +
    <%= program.StartTime %><%= ISODateString( program.StartTime ) %><%=program.Description %>
    +<% +%> +
    +
    + +<% + +function ISODateString(d) +{ + function pad(n) + { + return n<10 ? '0'+n : n + } + return d.getFullYear()+'-' + + pad(d.getMonth()+1)+'-' + + pad(d.getDate())+'T' + + pad(d.getHours())+':' + + pad(d.getMinutes())+':' + + pad(d.getSeconds()) +} + +%> + + diff --git a/mythtv/html/setup/js/storagegroups.js b/mythtv/html/setup/js/storagegroups.js index b684292a2be..0d21e7efb60 100644 --- a/mythtv/html/setup/js/storagegroups.js +++ b/mythtv/html/setup/js/storagegroups.js @@ -81,10 +81,10 @@ function addStorageGroupDir( group, dir, host ) { $.post("/Myth/AddStorageGroupDir", { GroupName: group, DirName: dir, HostName: host}, function(data) { - if (data.SuccessFail.Result == "true") + if (data.bool == "true") result = 1; else - alert("data.SuccessFail.Result != true"); + alert("data.bool != true"); }, "json").error(function(data) { alert("Error: unable to add Storage Group Directory"); }); @@ -103,10 +103,10 @@ function removeStorageGroupDir( group, dir, host ) { $.post("/Myth/RemoveStorageGroupDir", { GroupName: group, DirName: dir, HostName: host}, function(data) { - if (data.SuccessFail.Result == "true") + if (data.bool == "true") result = 1; else - alert("data.SuccessFail.Result != true"); + alert("data.bool != true"); }, "json").error(function(data) { alert("Error: unable to remove Storage Group Directory"); }); diff --git a/mythtv/libs/libmyth/libmyth.pro b/mythtv/libs/libmyth/libmyth.pro index 10e06484d89..b5b2db7e19f 100644 --- a/mythtv/libs/libmyth/libmyth.pro +++ b/mythtv/libs/libmyth/libmyth.pro @@ -8,7 +8,7 @@ INSTALLS = target DEFINES += MYTH_API -QT += network xml sql +QT += network xml sql script QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2) diff --git a/mythtv/libs/libmythservicecontracts/datacontracthelper.h b/mythtv/libs/libmythservicecontracts/datacontracthelper.h index bd8738f77ee..ad8f0685de3 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracthelper.h +++ b/mythtv/libs/libmythservicecontracts/datacontracthelper.h @@ -80,24 +80,6 @@ ////////////////////////////////////////////////////////////////////////////// -#define PROPERTYIMP_PTR_Old( type, name ) \ - private: type* m_##name; \ - public: \ - type* name() \ - { \ - return m_##name; \ - } \ - void set##name( QObject* val) \ - { \ - m_##name = qobject_cast< type* >( val ); \ - } \ - void set##name( type* val) \ - { \ - m_##name = val; \ - } - -////////////////////////////////////////////////////////////////////////////// - #define PROPERTYIMP_RO_REF( type, name ) \ private: type m_##name; \ public: \ diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/connectionInfo.h b/mythtv/libs/libmythservicecontracts/datacontracts/connectionInfo.h index d463d62e6ea..0851a32c1e7 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/connectionInfo.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/connectionInfo.h @@ -34,6 +34,7 @@ namespace DTC class SERVICE_PUBLIC ConnectionInfo : public QObject { Q_OBJECT + Q_CLASSINFO( "version" , "1.0" ); Q_PROPERTY( QObject* Database READ Database ) @@ -42,6 +43,17 @@ class SERVICE_PUBLIC ConnectionInfo : public QObject PROPERTYIMP_PTR( DatabaseInfo, Database ) PROPERTYIMP_PTR( WOLInfo , WOL ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< ConnectionInfo >(); + qRegisterMetaType< ConnectionInfo* >(); + + DatabaseInfo::InitializeCustomTypes(); + WOLInfo ::InitializeCustomTypes(); + } + public: ConnectionInfo(QObject *parent = 0) @@ -75,6 +87,7 @@ typedef ConnectionInfo* ConnectionInfoPtr; } // namespace DTC -Q_DECLARE_METATYPE( DTC::ConnectionInfo ) +Q_DECLARE_METATYPE( DTC::ConnectionInfo ) +Q_DECLARE_METATYPE( DTC::ConnectionInfo* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/databaseInfo.h b/mythtv/libs/libmythservicecontracts/datacontracts/databaseInfo.h index 576ccf727a5..e60172caa28 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/databaseInfo.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/databaseInfo.h @@ -55,6 +55,14 @@ class SERVICE_PUBLIC DatabaseInfo : public QObject PROPERTYIMP( bool , LocalEnabled ) PROPERTYIMP( QString, LocalHostName ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< DatabaseInfo >(); + qRegisterMetaType< DatabaseInfo* >(); + } + public: DatabaseInfo(QObject *parent = 0) @@ -82,14 +90,13 @@ class SERVICE_PUBLIC DatabaseInfo : public QObject m_LocalEnabled = src.m_LocalEnabled ; m_LocalHostName= src.m_LocalHostName; } - }; typedef DatabaseInfo * DatabaseInfoPtr; } // namespace DTC -Q_DECLARE_METATYPE( DTC::DatabaseInfo ) - +Q_DECLARE_METATYPE( DTC::DatabaseInfo ) +Q_DECLARE_METATYPE( DTC::DatabaseInfo* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/encoder.h b/mythtv/libs/libmythservicecontracts/datacontracts/encoder.h index 6694f07bb70..75a26eee7c0 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/encoder.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/encoder.h @@ -59,6 +59,16 @@ class SERVICE_PUBLIC Encoder : public QObject PROPERTYIMP_PTR( Program , Recording ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< Encoder >(); + qRegisterMetaType< Encoder* >(); + + Program::InitializeCustomTypes(); + } + public: Encoder(QObject *parent = 0) @@ -97,6 +107,7 @@ class SERVICE_PUBLIC Encoder : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::Encoder ) +Q_DECLARE_METATYPE( DTC::Encoder ) +Q_DECLARE_METATYPE( DTC::Encoder* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h b/mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h index 9f73b37b92e..a67daefbc55 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h @@ -37,10 +37,27 @@ class SERVICE_PUBLIC EncoderList : public QObject Q_OBJECT Q_CLASSINFO( "version", "1.0" ); + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "Encoders_type", "DTC::Encoder"); + Q_PROPERTY( QVariantList Encoders READ Encoders DESIGNABLE true ) PROPERTYIMP_RO_REF( QVariantList, Encoders ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< EncoderList >(); + qRegisterMetaType< EncoderList* >(); + + Encoder::InitializeCustomTypes(); + } + public: EncoderList(QObject *parent = 0) @@ -73,6 +90,7 @@ class SERVICE_PUBLIC EncoderList : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::EncoderList ) +Q_DECLARE_METATYPE( DTC::EncoderList ) +Q_DECLARE_METATYPE( DTC::EncoderList* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h b/mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h index c52c7953891..8aacd6daa18 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h @@ -41,6 +41,13 @@ class SERVICE_PUBLIC ChannelInfo : public QObject Q_OBJECT Q_CLASSINFO( "version", "1.0" ); + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "Programs_type", "DTC::Program"); + Q_PROPERTY( uint ChanId READ ChanId WRITE setChanId ) Q_PROPERTY( QString ChanNum READ ChanNum WRITE setChanNum ) Q_PROPERTY( QString CallSign READ CallSign WRITE setCallSign ) @@ -69,14 +76,19 @@ class SERVICE_PUBLIC ChannelInfo : public QObject // Used only by Serializer PROPERTYIMP( bool, SerializeDetails ) + public: + + static void InitializeCustomTypes(); + public: ChannelInfo(QObject *parent = 0) - : QObject ( parent ), - m_ChanId ( 0 ), - m_SourceId( 0 ), - m_InputId ( 0 ), - m_CommFree( 0 ) + : QObject ( parent ), + m_ChanId ( 0 ), + m_SourceId ( 0 ), + m_InputId ( 0 ), + m_CommFree ( 0 ), + m_SerializeDetails( true ) { } @@ -157,6 +169,20 @@ class SERVICE_PUBLIC Program : public QObject PROPERTYIMP( bool, SerializeChannel ) PROPERTYIMP( bool, SerializeRecording ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< Program >(); + qRegisterMetaType< Program* >(); + + if (QMetaType::type( "DTC::ChannelInfo" ) == 0) + ChannelInfo::InitializeCustomTypes(); + + if (QMetaType::type( "DTC::RecordingInfo" ) == 0) + RecordingInfo::InitializeCustomTypes(); + } + public: Program(QObject *parent = 0) @@ -167,9 +193,9 @@ class SERVICE_PUBLIC Program : public QObject m_ProgramFlags ( 0 ), m_Channel ( NULL ), m_Recording ( NULL ), - m_SerializeDetails ( false ), - m_SerializeChannel ( false ), - m_SerializeRecording ( false ) + m_SerializeDetails ( true ), + m_SerializeChannel ( true ), + m_SerializeRecording ( true ) { } @@ -220,9 +246,22 @@ inline Program *ChannelInfo::AddNewProgram() return pObject; } +inline void ChannelInfo::InitializeCustomTypes() +{ + qRegisterMetaType< ChannelInfo >(); + qRegisterMetaType< ChannelInfo* >(); + + if (QMetaType::type( "DTC::Program" ) == 0) + Program::InitializeCustomTypes(); +} + + } // namespace DTC -Q_DECLARE_METATYPE( DTC::Program ) -Q_DECLARE_METATYPE( DTC::ChannelInfo ) +Q_DECLARE_METATYPE( DTC::Program ) +Q_DECLARE_METATYPE( DTC::Program* ) + +Q_DECLARE_METATYPE( DTC::ChannelInfo ) +Q_DECLARE_METATYPE( DTC::ChannelInfo* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h b/mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h index 770a9e53970..8e838230d8a 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h @@ -54,6 +54,14 @@ class SERVICE_PUBLIC ProgramGuide : public QObject Q_OBJECT Q_CLASSINFO( "version", "1.0" ); + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "Channels_type", "DTC::Channel"); + + Q_PROPERTY( QDateTime StartTime READ StartTime WRITE setStartTime ) Q_PROPERTY( QDateTime EndTime READ EndTime WRITE setEndTime ) Q_PROPERTY( int StartChanId READ StartChanId WRITE setStartChanId ) @@ -82,6 +90,16 @@ class SERVICE_PUBLIC ProgramGuide : public QObject PROPERTYIMP_RO_REF( QVariantList, Channels ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< ProgramGuide >(); + qRegisterMetaType< ProgramGuide* >(); + + ChannelInfo::InitializeCustomTypes(); + } + public: ProgramGuide(QObject *parent = 0) @@ -130,6 +148,7 @@ class SERVICE_PUBLIC ProgramGuide : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::ProgramGuide ) +Q_DECLARE_METATYPE( DTC::ProgramGuide ) +Q_DECLARE_METATYPE( DTC::ProgramGuide* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/programList.h b/mythtv/libs/libmythservicecontracts/datacontracts/programList.h index bca35e297dd..a3d08bed263 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/programList.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/programList.h @@ -39,6 +39,14 @@ class SERVICE_PUBLIC ProgramList : public QObject Q_OBJECT Q_CLASSINFO( "version", "1.0" ); + + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "Programs_type", "DTC::Program"); + Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex ) Q_PROPERTY( int Count READ Count WRITE setCount ) Q_PROPERTY( int TotalAvailable READ TotalAvailable WRITE setTotalAvailable ) @@ -57,6 +65,16 @@ class SERVICE_PUBLIC ProgramList : public QObject PROPERTYIMP_RO_REF( QVariantList, Programs ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< ProgramList >(); + qRegisterMetaType< ProgramList* >(); + + Program::InitializeCustomTypes(); + } + public: ProgramList(QObject *parent = 0) @@ -99,6 +117,7 @@ class SERVICE_PUBLIC ProgramList : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::ProgramList ) +Q_DECLARE_METATYPE( DTC::ProgramList ) +Q_DECLARE_METATYPE( DTC::ProgramList* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/recording.h b/mythtv/libs/libmythservicecontracts/datacontracts/recording.h index 63031cae2e4..4da8ce9ed96 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/recording.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/recording.h @@ -77,6 +77,14 @@ class SERVICE_PUBLIC RecordingInfo : public QObject // Used only by Serializer PROPERTYIMP( bool, SerializeDetails ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< RecordingInfo >(); + qRegisterMetaType< RecordingInfo* >(); + } + public: RecordingInfo(QObject *parent = 0) @@ -117,6 +125,7 @@ class SERVICE_PUBLIC RecordingInfo : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::RecordingInfo ) +Q_DECLARE_METATYPE( DTC::RecordingInfo ) +Q_DECLARE_METATYPE( DTC::RecordingInfo* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/settingList.h b/mythtv/libs/libmythservicecontracts/datacontracts/settingList.h index 98c2b19c13e..a498a50a338 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/settingList.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/settingList.h @@ -34,7 +34,14 @@ namespace DTC class SERVICE_PUBLIC SettingList : public QObject { Q_OBJECT - Q_CLASSINFO( "version", "1.0" ); + Q_CLASSINFO( "version" , "1.0" ); + + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "Settings_type", "QString"); Q_PROPERTY( QString HostName READ HostName WRITE setHostName ) Q_PROPERTY( QVariantMap Settings READ Settings DESIGNABLE true ) @@ -42,6 +49,14 @@ class SERVICE_PUBLIC SettingList : public QObject PROPERTYIMP ( QString , HostName ) PROPERTYIMP_RO_REF( QVariantMap, Settings ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< SettingList >(); + qRegisterMetaType< SettingList* >(); + } + public: SettingList(QObject *parent = 0) @@ -58,6 +73,7 @@ class SERVICE_PUBLIC SettingList : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::SettingList ) +Q_DECLARE_METATYPE( DTC::SettingList ) +Q_DECLARE_METATYPE( DTC::SettingList* ) #endif \ No newline at end of file diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDir.h b/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDir.h index 0e6e615b77c..6693551511e 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDir.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDir.h @@ -26,6 +26,14 @@ class SERVICE_PUBLIC StorageGroupDir : public QObject PROPERTYIMP ( QString , HostName ) PROPERTYIMP ( QString , DirName ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< StorageGroupDir >(); + qRegisterMetaType< StorageGroupDir* >(); + } + public: StorageGroupDir(QObject *parent = 0) @@ -50,6 +58,7 @@ class SERVICE_PUBLIC StorageGroupDir : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::StorageGroupDir ) +Q_DECLARE_METATYPE( DTC::StorageGroupDir ) +Q_DECLARE_METATYPE( DTC::StorageGroupDir* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h b/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h index 10b7a0d7f32..2eccdb51a01 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h @@ -16,10 +16,27 @@ class SERVICE_PUBLIC StorageGroupDirList : public QObject Q_OBJECT Q_CLASSINFO( "version", "1.0" ); + // We need to know the type that will ultimately be contained in + // any QVariantList or QVariantMap. We do his by specifying + // A Q_CLASSINFO entry with "_type" as the key + // and the type name as the value + + Q_CLASSINFO( "StorageGroupDirs_type", "DTC::StorageGroupDir"); + Q_PROPERTY( QVariantList StorageGroupDirs READ StorageGroupDirs DESIGNABLE true ) PROPERTYIMP_RO_REF( QVariantList, StorageGroupDirs ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< StorageGroupDirList >(); + qRegisterMetaType< StorageGroupDirList* >(); + + StorageGroupDir::InitializeCustomTypes(); + } + public: StorageGroupDirList(QObject *parent = 0) @@ -52,6 +69,7 @@ class SERVICE_PUBLIC StorageGroupDirList : public QObject } // namespace DTC -Q_DECLARE_METATYPE( DTC::StorageGroupDirList ) +Q_DECLARE_METATYPE( DTC::StorageGroupDirList ) +Q_DECLARE_METATYPE( DTC::StorageGroupDirList* ) #endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/stringList.h b/mythtv/libs/libmythservicecontracts/datacontracts/stringList.h deleted file mode 100644 index 915da260292..00000000000 --- a/mythtv/libs/libmythservicecontracts/datacontracts/stringList.h +++ /dev/null @@ -1,59 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// Program Name: stringList.h -// Created : Jan. 15, 2010 -// -// Copyright (c) 2010 David Blain -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or at your option any later version of the LGPL. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library. If not, see . -// -////////////////////////////////////////////////////////////////////////////// - -#ifndef STRINGLIST_H_ -#define STRINGLIST_H_ - -#include - -#include "serviceexp.h" -#include "datacontracthelper.h" - -namespace DTC -{ - -class SERVICE_PUBLIC StringList : public QObject -{ - Q_OBJECT - Q_CLASSINFO( "version", "1.0" ); - - Q_PROPERTY( QStringList Values READ Values DESIGNABLE true ) - - PROPERTYIMP_RO_REF( QStringList, Values ) - - public: - - StringList(QObject *parent = 0) - : QObject( parent ) - { - } - - StringList( const StringList &src ) - : m_Values( src.m_Values ) - { - } -}; - -} // namespace DTC - -Q_DECLARE_METATYPE( DTC::StringList ) - -#endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/successFail.h b/mythtv/libs/libmythservicecontracts/datacontracts/successFail.h deleted file mode 100644 index 773583e650a..00000000000 --- a/mythtv/libs/libmythservicecontracts/datacontracts/successFail.h +++ /dev/null @@ -1,59 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// Program Name: successFail.h -// Created : Jan. 15, 2010 -// -// Copyright (c) 2010 David Blain -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or at your option any later version of the LGPL. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library. If not, see . -// -////////////////////////////////////////////////////////////////////////////// - -#ifndef SUCCESSFAIL_H_ -#define SUCCESSFAIL_H_ - -#include - -#include "serviceexp.h" -#include "datacontracthelper.h" - -namespace DTC -{ - -class SERVICE_PUBLIC SuccessFail : public QObject -{ - Q_OBJECT - Q_CLASSINFO( "version", "1.0" ); - - Q_PROPERTY ( bool Result READ Result WRITE setResult ) - - PROPERTYIMP( bool, Result ) - - public: - - SuccessFail(QObject *parent = 0) - : QObject( parent ) - { - } - - SuccessFail( const SuccessFail &src ) - : m_Result( src.m_Result ) - { - } -}; - -} // namespace DTC - -Q_DECLARE_METATYPE( DTC::SuccessFail ) - -#endif diff --git a/mythtv/libs/libmythservicecontracts/datacontracts/wolInfo.h b/mythtv/libs/libmythservicecontracts/datacontracts/wolInfo.h index aafeab9e4f9..9ee17f63971 100644 --- a/mythtv/libs/libmythservicecontracts/datacontracts/wolInfo.h +++ b/mythtv/libs/libmythservicecontracts/datacontracts/wolInfo.h @@ -47,6 +47,14 @@ class SERVICE_PUBLIC WOLInfo : public QObject PROPERTYIMP( int , Retry ) PROPERTYIMP( QString, Command ) + public: + + static void InitializeCustomTypes() + { + qRegisterMetaType< WOLInfo >(); + qRegisterMetaType< WOLInfo* >(); + } + public: WOLInfo(QObject *parent = 0) @@ -75,6 +83,8 @@ typedef WOLInfo* WOLInfoPtr; } // namespace DTC -Q_DECLARE_METATYPE( DTC::WOLInfo ) +Q_DECLARE_METATYPE( DTC::WOLInfo ) +Q_DECLARE_METATYPE( DTC::WOLInfo* ) + #endif diff --git a/mythtv/libs/libmythservicecontracts/libmythservicecontracts.pro b/mythtv/libs/libmythservicecontracts/libmythservicecontracts.pro index 9df71f8c420..ad6dd45885b 100644 --- a/mythtv/libs/libmythservicecontracts/libmythservicecontracts.pro +++ b/mythtv/libs/libmythservicecontracts/libmythservicecontracts.pro @@ -20,7 +20,6 @@ HEADERS += services/contentServices.h services/dvrServices.h HEADERS += datacontracts/connectionInfo.h datacontracts/databaseInfo.h HEADERS += datacontracts/programAndChannel.h datacontracts/programGuide.h HEADERS += datacontracts/recording.h datacontracts/settingList.h -HEADERS += datacontracts/stringList.h datacontracts/successFail.h HEADERS += datacontracts/wolInfo.h datacontracts/programList.h HEADERS += datacontracts/encoder.h datacontracts/encoderList.h HEADERS += datacontracts/storageGroupDir.h datacontracts/storageGroupDirList.h @@ -47,7 +46,6 @@ incDatacontracts.path = $${PREFIX}/include/mythtv/libmythservicecontracts/dataco incDatacontracts.files = datacontracts/connectionInfo.h datacontracts/databaseInfo.h incDatacontracts.files += datacontracts/programAndChannel.h datacontracts/programGuide.h incDatacontracts.files += datacontracts/recording.h datacontracts/settingList.h -incDatacontracts.files += datacontracts/stringList.h datacontracts/successFail.h incDatacontracts.files += datacontracts/wolInfo.h INSTALLS += inc incServices incDatacontracts diff --git a/mythtv/libs/libmythservicecontracts/service.cpp b/mythtv/libs/libmythservicecontracts/service.cpp index 082b80cbf74..ac6f58d26b2 100644 --- a/mythtv/libs/libmythservicecontracts/service.cpp +++ b/mythtv/libs/libmythservicecontracts/service.cpp @@ -27,55 +27,67 @@ // ////////////////////////////////////////////////////////////////////////////// -QVariant Service::ConvertToVariant( const QString &sTypeName, void *pValue ) +QVariant Service::ConvertToVariant( int nType, void *pValue ) { - // -=>NOTE: Only intrinsic or Qt type conversions should be here - // All others should be added to overridden implementation. + // -=>NOTE: This assumes any UserType will be derived from QObject... + // (Exception for QFileInfo ) + if ( nType == QMetaType::type( "QFileInfo" )) + return QVariant::fromValue< QFileInfo >( *((QFileInfo *)pValue) ); - if ( sTypeName.compare( "QFileInfo*", Qt::CaseInsensitive ) == 0 ) + if (nType > QMetaType::User) { - QFileInfo *pInfo = reinterpret_cast< QFileInfo *>( pValue ); + QObject *pObj = *((QObject **)pValue); - return QVariant::fromValue( pInfo ); + return QVariant::fromValue( pObj ); } - // -=>NOTE: Default to being a QObject or derivative... - // Will CRASH if NOT derived from QObject!!! - - QObject *pObjResult = reinterpret_cast< QObject *>( pValue ); - - return QVariant::fromValue( pObjResult ); + return QVariant( nType, pValue ); } + ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// -void* Service::ConvertToParameterPtr( const QString &sParamType, +void* Service::ConvertToParameterPtr( int nTypeId, + const QString &sParamType, + void* pParam, const QString &sValue ) { // -=>NOTE: Only intrinsic or Qt type conversions should be here // All others should be added to overridden implementation. - if (sParamType == "QString") - return new QString( sValue ); - - if (sParamType == "QDateTime") + switch( nTypeId ) { - if ( sValue.isEmpty()) - return new QDateTime(); + case QMetaType::Bool : *(( bool *)pParam) = ToBool( sValue ); break; - return new QDateTime( QDateTime::fromString( sValue, Qt::ISODate )); - } + case QMetaType::Char : *(( char *)pParam) = ( sValue.length() > 0) ? sValue.at( 0 ).toAscii() : 0; break; + case QMetaType::UChar : *(( unsigned char *)pParam) = ( sValue.length() > 0) ? sValue.at( 0 ).toAscii() : 0; break; + case QMetaType::QChar : *(( QChar *)pParam) = ( sValue.length() > 0) ? sValue.at( 0 ) : 0; break; - if (sParamType == "int") - return new int( sValue.toInt() ); + case QMetaType::Short : *(( short *)pParam) = sValue.toShort (); break; + case QMetaType::UShort : *(( ushort *)pParam) = sValue.toUShort (); break; - if (sParamType == "bool") - return new bool( ToBool( sValue )); + case QMetaType::Int : *(( int *)pParam) = sValue.toInt (); break; + case QMetaType::UInt : *(( uint *)pParam) = sValue.toUInt (); break; + + case QMetaType::Long : *(( long *)pParam) = sValue.toLong (); break; + case QMetaType::ULong : *(( ulong *)pParam) = sValue.toULong (); break; + + case QMetaType::LongLong : *(( qlonglong *)pParam) = sValue.toLongLong (); break; + case QMetaType::ULongLong : *(( qulonglong *)pParam) = sValue.toULongLong (); break; + + case QMetaType::Double : *(( double *)pParam) = sValue.toDouble (); break; + case QMetaType::Float : *(( float *)pParam) = sValue.toFloat (); break; + + case QMetaType::QString : *(( QString *)pParam) = sValue; break; + case QMetaType::QByteArray : *(( QByteArray *)pParam) = sValue.toUtf8 (); break; + + case QMetaType::QDateTime : *(( QDateTime *)pParam) = QDateTime::fromString( sValue, Qt::ISODate ); break; + } - return NULL; + return pParam; } ////////////////////////////////////////////////////////////////////////////// diff --git a/mythtv/libs/libmythservicecontracts/service.h b/mythtv/libs/libmythservicecontracts/service.h index 12bc3263ef2..4877ea2b58a 100644 --- a/mythtv/libs/libmythservicecontracts/service.h +++ b/mythtv/libs/libmythservicecontracts/service.h @@ -54,19 +54,28 @@ class SERVICE_PUBLIC Service : public QObject { Q_OBJECT + public: + + Service( QObject *parent = 0 ) : QObject( parent ) + { + qRegisterMetaType< QFileInfo >(); + } + public: ///////////////////////////////////////////////////////////////////// // This method should be overridden to handle non-QObject based custom types ///////////////////////////////////////////////////////////////////// - virtual QVariant ConvertToVariant ( const QString &sTypeName, - void *pValue ); + virtual QVariant ConvertToVariant ( int nType, void *pValue ); - virtual void* ConvertToParameterPtr( const QString &sParamType, + virtual void* ConvertToParameterPtr( int nTypeId, + const QString &sParamType, + void* pParam, const QString &sValue ); static bool ToBool( const QString &sVal ); + }; ////////////////////////////////////////////////////////////////////////////// @@ -74,6 +83,5 @@ class SERVICE_PUBLIC Service : public QObject ////////////////////////////////////////////////////////////////////////////// Q_DECLARE_METATYPE( QFileInfo ) -Q_DECLARE_METATYPE( QFileInfo* ) #endif \ No newline at end of file diff --git a/mythtv/libs/libmythservicecontracts/services/contentServices.h b/mythtv/libs/libmythservicecontracts/services/contentServices.h index f02661c52fd..ff4ec280fc9 100644 --- a/mythtv/libs/libmythservicecontracts/services/contentServices.h +++ b/mythtv/libs/libmythservicecontracts/services/contentServices.h @@ -25,9 +25,9 @@ #define CONTENTSERVICES_H_ #include +#include #include "service.h" -#include "datacontracts/stringList.h" ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -50,28 +50,37 @@ class SERVICE_PUBLIC ContentServices : public Service //, public QScriptable ?? Q_OBJECT Q_CLASSINFO( "version" , "1.0" ); + public: + + // Must call InitializeCustomTypes for each unique Custom Type used + // in public slots below. + + ContentServices( QObject *parent = 0 ) : Service( parent ) + { + } + public slots: - virtual QFileInfo* GetFile ( const QString &StorageGroup, + virtual QFileInfo GetFile ( const QString &StorageGroup, const QString &FileName ) = 0; - virtual DTC::StringList* GetFileList ( const QString &StorageGroup ) = 0; + virtual QStringList GetFileList ( const QString &StorageGroup ) = 0; - virtual QFileInfo* GetVideoArt ( int Id ) = 0; + virtual QFileInfo GetVideoArt ( int Id ) = 0; - virtual QFileInfo* GetAlbumArt ( int Id, int Width, int Height ) = 0; + virtual QFileInfo GetAlbumArt ( int Id, int Width, int Height ) = 0; - virtual QFileInfo* GetPreviewImage ( int ChanId, + virtual QFileInfo GetPreviewImage ( int ChanId, const QDateTime &StartTime, int Width, int Height, int SecsIn ) = 0; - virtual QFileInfo* GetRecording ( int ChanId, + virtual QFileInfo GetRecording ( int ChanId, const QDateTime &StartTime ) = 0; - virtual QFileInfo* GetMusic ( int Id ) = 0; - virtual QFileInfo* GetVideo ( int Id ) = 0; + virtual QFileInfo GetMusic ( int Id ) = 0; + virtual QFileInfo GetVideo ( int Id ) = 0; }; #endif diff --git a/mythtv/libs/libmythservicecontracts/services/dvrServices.h b/mythtv/libs/libmythservicecontracts/services/dvrServices.h index 64d334a43cb..a25b6b7fece 100644 --- a/mythtv/libs/libmythservicecontracts/services/dvrServices.h +++ b/mythtv/libs/libmythservicecontracts/services/dvrServices.h @@ -50,6 +50,16 @@ class SERVICE_PUBLIC DvrServices : public Service //, public QScriptable ??? Q_OBJECT Q_CLASSINFO( "version" , "1.0" ); + public: + + // Must call InitializeCustomTypes for each unique Custom Type used + // in public slots below. + + DvrServices( QObject *parent = 0 ) : Service( parent ) + { + DTC::ProgramList::InitializeCustomTypes(); + DTC::EncoderList::InitializeCustomTypes(); + } public slots: diff --git a/mythtv/libs/libmythservicecontracts/services/guideServices.h b/mythtv/libs/libmythservicecontracts/services/guideServices.h index 5678a93a237..7b91ada69b3 100644 --- a/mythtv/libs/libmythservicecontracts/services/guideServices.h +++ b/mythtv/libs/libmythservicecontracts/services/guideServices.h @@ -51,6 +51,17 @@ class SERVICE_PUBLIC GuideServices : public Service //, public QScriptable ??? Q_OBJECT Q_CLASSINFO( "version" , "1.0" ); + public: + + // Must call InitializeCustomTypes for each unique Custom Type used + // in public slots below. + + GuideServices( QObject *parent = 0 ) : Service( parent ) + { + DTC::ProgramGuide::InitializeCustomTypes(); + DTC::Program ::InitializeCustomTypes(); + } + public slots: virtual DTC::ProgramGuide* GetProgramGuide ( const QDateTime &StartTime , @@ -62,7 +73,7 @@ class SERVICE_PUBLIC GuideServices : public Service //, public QScriptable ??? virtual DTC::Program* GetProgramDetails ( int ChanId, const QDateTime &StartTime ) = 0; - virtual QFileInfo* GetChannelIcon ( int ChanId, + virtual QFileInfo GetChannelIcon ( int ChanId, int Width , int Height ) = 0; }; diff --git a/mythtv/libs/libmythservicecontracts/services/mythServices.h b/mythtv/libs/libmythservicecontracts/services/mythServices.h index 7bca476c50d..5500a127e93 100644 --- a/mythtv/libs/libmythservicecontracts/services/mythServices.h +++ b/mythtv/libs/libmythservicecontracts/services/mythServices.h @@ -28,12 +28,9 @@ #include "service.h" #include "datacontracts/connectionInfo.h" -#include "datacontracts/stringList.h" #include "datacontracts/settingList.h" -#include "datacontracts/successFail.h" #include "datacontracts/storageGroupDirList.h" - ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // @@ -58,32 +55,45 @@ class SERVICE_PUBLIC MythServices : public Service //, public QScriptable ??? Q_CLASSINFO( "AddStorageGroupDir_Method", "POST" ) Q_CLASSINFO( "RemoveStorageGroupDir_Method", "POST" ) + public: + + // Must call InitializeCustomTypes for each unique Custom Type used + // in public slots below. + + MythServices( QObject *parent = 0 ) : Service( parent ) + { + DTC::ConnectionInfo ::InitializeCustomTypes(); + DTC::SettingList ::InitializeCustomTypes(); + DTC::StorageGroupDirList::InitializeCustomTypes(); + } + public slots: virtual DTC::ConnectionInfo* GetConnectionInfo ( const QString &Pin ) = 0; - virtual DTC::StringList* GetHostName ( ) = 0; - virtual DTC::StringList* GetHosts ( ) = 0; - virtual DTC::StringList* GetKeys ( ) = 0; + virtual QString GetHostName ( ) = 0; + virtual QStringList GetHosts ( ) = 0; + virtual QStringList GetKeys ( ) = 0; virtual DTC::StorageGroupDirList* GetStorageGroupDirs ( const QString &GroupName, const QString &HostName ) = 0; - virtual DTC::SuccessFail* AddStorageGroupDir ( const QString &GroupName, + virtual bool AddStorageGroupDir ( const QString &GroupName, const QString &DirName, const QString &HostName ) = 0; - virtual DTC::SuccessFail* RemoveStorageGroupDir ( const QString &GroupName, - const QString &DirName, - const QString &HostName ) = 0; + virtual bool RemoveStorageGroupDir( const QString &GroupName, + const QString &DirName, + const QString &HostName ) = 0; virtual DTC::SettingList* GetSetting ( const QString &HostName, const QString &Key, const QString &Default ) = 0; - virtual DTC::SuccessFail* PutSetting ( const QString &HostName, + virtual bool PutSetting ( const QString &HostName, const QString &Key, const QString &Value ) = 0; + }; #endif diff --git a/mythtv/libs/libmythupnp/htmlserver.cpp b/mythtv/libs/libmythupnp/htmlserver.cpp index 819e52eb928..ec76f145ad7 100644 --- a/mythtv/libs/libmythupnp/htmlserver.cpp +++ b/mythtv/libs/libmythupnp/htmlserver.cpp @@ -91,7 +91,14 @@ bool HtmlServerExtension::ProcessRequest( HttpWorkerThread *, HTTPRequest *pRequ QFileInfo oInfo( m_sAbsoluteSharePath + pRequest->m_sResourceUrl ); if (oInfo.isDir()) - oInfo.setFile( oInfo.filePath() + "/index.html" ); + { + QString sIndexFileName = oInfo.filePath() + "/index.qsp"; + + if (QFile::exists( sIndexFileName )) + oInfo.setFile( sIndexFileName ); + else + oInfo.setFile( oInfo.filePath() + "/index.html" ); + } if (oInfo.exists() == true ) { @@ -108,9 +115,30 @@ bool HtmlServerExtension::ProcessRequest( HttpWorkerThread *, HTTPRequest *pRequ if (oInfo.exists()) { if (oInfo.isSymLink()) - pRequest->FormatFileResponse( oInfo.symLinkTarget() ); - else - pRequest->FormatFileResponse( sResName ); + sResName = oInfo.symLinkTarget(); + + // ------------------------------------------------------ + // Is this a Qt Server Page (File contains script)... + // ------------------------------------------------------ + + if (oInfo.suffix().compare( "qsp", Qt::CaseInsensitive ) == 0) + { + + pRequest->m_eResponseType = ResponseTypeHTML; + + QTextStream stream( &pRequest->m_response ); + + m_Scripting.EvaluatePage( &stream, sResName ); + + return true; + + } + + // ------------------------------------------------------ + // Return the file. + // ------------------------------------------------------ + + pRequest->FormatFileResponse( sResName ); return true; } diff --git a/mythtv/libs/libmythupnp/htmlserver.h b/mythtv/libs/libmythupnp/htmlserver.h index d99fb64ea28..4b9833be02a 100644 --- a/mythtv/libs/libmythupnp/htmlserver.h +++ b/mythtv/libs/libmythupnp/htmlserver.h @@ -25,6 +25,7 @@ #define __HTMLSERVER_H__ #include "httpserver.h" +#include "serverSideScripting.h" ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -38,13 +39,20 @@ class UPNP_PUBLIC HtmlServerExtension : public HttpServerExtension { private: - QString m_sAbsoluteSharePath; + QString m_sAbsoluteSharePath; + ServerSideScripting m_Scripting; public: HtmlServerExtension( const QString sSharePath); virtual ~HtmlServerExtension( ); bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); + + QScriptEngine* ScriptEngine() + { + return &(m_Scripting.m_engine); + } + }; #endif diff --git a/mythtv/libs/libmythupnp/httprequest.cpp b/mythtv/libs/libmythupnp/httprequest.cpp index 6045b79d049..cb1d33e530a 100644 --- a/mythtv/libs/libmythupnp/httprequest.cpp +++ b/mythtv/libs/libmythupnp/httprequest.cpp @@ -1297,6 +1297,8 @@ bool HTTPRequest::ProcessSOAPPayload( const QString &sSOAPAction ) // XML Document Loaded... now parse it // -------------------------------------------------------------- + QString sService; + if (sSOAPAction.contains( '#' )) { m_sNameSpace = sSOAPAction.section( '#', 0, 0).remove( 0, 1); @@ -1308,6 +1310,10 @@ bool HTTPRequest::ProcessSOAPPayload( const QString &sSOAPAction ) int nPos = sSOAPAction.lastIndexOf( '/' ); m_sNameSpace = sSOAPAction.mid( 1, nPos ); m_sMethod = sSOAPAction.mid( nPos + 1, sSOAPAction.length() - nPos - 2 ); + + nPos = m_sNameSpace.lastIndexOf( '/', -2); + sService = m_sNameSpace.mid( nPos + 1, m_sNameSpace.length() - nPos - 2 ); + m_sNameSpace = m_sNameSpace.mid( 0, nPos ); } else { diff --git a/mythtv/libs/libmythupnp/httprequest.h b/mythtv/libs/libmythupnp/httprequest.h index 394bd421a87..a1192b57208 100644 --- a/mythtv/libs/libmythupnp/httprequest.h +++ b/mythtv/libs/libmythupnp/httprequest.h @@ -256,4 +256,41 @@ class BufferedSocketDeviceRequest : public HTTPRequest }; +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +class UPNP_PUBLIC HttpException +{ + public: + int code; + QString msg; + + HttpException( int nCode = -1, const QString &sMsg = "") + : code( nCode ), msg ( sMsg ) + {} + + // Needed to force a v-table. + virtual ~HttpException() + {} +}; + +class UPNP_PUBLIC HttpRedirectException : public HttpException +{ + public: + + QString hostName; + //int port; + + HttpRedirectException( const QString &sHostName = "", + int nCode = -1, + const QString &sMsg = "" ) + : HttpException( nCode, sMsg ), hostName( sHostName ) + {} + + virtual ~HttpRedirectException() + {} + +}; + #endif diff --git a/mythtv/libs/libmythupnp/httpserver.cpp b/mythtv/libs/libmythupnp/httpserver.cpp index 88801ffcd64..0a650e621ee 100644 --- a/mythtv/libs/libmythupnp/httpserver.cpp +++ b/mythtv/libs/libmythupnp/httpserver.cpp @@ -116,6 +116,15 @@ HttpServer::~HttpServer() // ///////////////////////////////////////////////////////////////////////////// +QScriptEngine* HttpServer::ScriptEngine() +{ + return ((HtmlServerExtension *)m_pHtmlServer)->ScriptEngine(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + WorkerThread *HttpServer::CreateWorkerThread( ThreadPool * /*pThreadPool */, const QString &sName ) { diff --git a/mythtv/libs/libmythupnp/httpserver.h b/mythtv/libs/libmythupnp/httpserver.h index f544d4cc923..91be91538db 100644 --- a/mythtv/libs/libmythupnp/httpserver.h +++ b/mythtv/libs/libmythupnp/httpserver.h @@ -36,6 +36,7 @@ #include #include #include +#include // MythTV headers #include "upnputil.h" @@ -77,6 +78,7 @@ class UPNP_PUBLIC HttpServerExtension : public QObject virtual bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) = 0; // virtual bool Uninitialize ( ) = 0; + }; typedef QList > HttpServerExtensionList; @@ -120,6 +122,8 @@ class UPNP_PUBLIC HttpServer : public QTcpServer, void DelegateRequest ( HttpWorkerThread *pThread, HTTPRequest *pRequest ); + QScriptEngine* ScriptEngine(); + }; ///////////////////////////////////////////////////////////////////////////// diff --git a/mythtv/libs/libmythupnp/libmythupnp.pro b/mythtv/libs/libmythupnp/libmythupnp.pro index 6da7ddf7987..0794d81411b 100644 --- a/mythtv/libs/libmythupnp/libmythupnp.pro +++ b/mythtv/libs/libmythupnp/libmythupnp.pro @@ -23,7 +23,7 @@ HEADERS += httpserver.h upnpcds.h upnpcdsobjects.h bufferedsocketdevice.h upnpms HEADERS += eventing.h upnpcmgr.h upnptaskevent.h upnptaskcache.h ssdpcache.h HEADERS += upnpimpl.h multicast.h broadcast.h configuration.h HEADERS += soapclient.h mythxmlclient.h mmembuf.h upnpexp.h -HEADERS += servicehost.h wsdl.h htmlserver.h +HEADERS += servicehost.h wsdl.h htmlserver.h serverSideScripting.h HEADERS += serializers/serializer.h serializers/xmlSerializer.h HEADERS += serializers/jsonSerializer.h serializers/soapSerializer.h @@ -33,7 +33,7 @@ SOURCES += upnpdevice.cpp upnptasknotify.cpp upnptasksearch.cpp threadpool.cpp SOURCES += httpserver.cpp upnpcds.cpp upnpcdsobjects.cpp bufferedsocketdevice.cpp SOURCES += eventing.cpp upnpcmgr.cpp upnpmsrr.cpp upnptaskevent.cpp ssdpcache.cpp SOURCES += configuration.cpp soapclient.cpp mythxmlclient.cpp mmembuf.cpp -SOURCES += multicast.cpp htmlserver.cpp +SOURCES += multicast.cpp htmlserver.cpp serverSideScripting.cpp SOURCES += servicehost.cpp wsdl.cpp SOURCES += serializers/serializer.cpp serializers/xmlSerializer.cpp @@ -61,7 +61,7 @@ inc.files += httpserver.h httpstatus.h upnpcds.h upnpcdsobjects.h inc.files += eventing.h upnpcmgr.h upnptaskevent.h upnptaskcache.h ssdpcache.h inc.files += upnpimpl.h multicast.h broadcast.h configuration.h inc.files += soapclient.h mythxmlclient.h mmembuf.h -inc.files += servicehost.h wsdl.h htmlserver.h +inc.files += servicehost.h wsdl.h htmlserver.h serverSideScripting.h inc.files += serializers/serializer.h serializers/xmlSerializer.h inc.files += serializers/jsonSerializer.h serializers/soapSerializer.h @@ -73,7 +73,7 @@ macx { QMAKE_LFLAGS_SHLIB += -flat_namespace } -QT += network xml sql +QT += network xml sql script use_hidesyms { QMAKE_CXXFLAGS += -fvisibility=hidden diff --git a/mythtv/libs/libmythupnp/serializers/jsonSerializer.cpp b/mythtv/libs/libmythupnp/serializers/jsonSerializer.cpp index 49f1d334a1d..7b7a1c5dbd7 100644 --- a/mythtv/libs/libmythupnp/serializers/jsonSerializer.cpp +++ b/mythtv/libs/libmythupnp/serializers/jsonSerializer.cpp @@ -57,7 +57,7 @@ QString JSONSerializer::GetContentType() // ////////////////////////////////////////////////////////////////////////////// -void JSONSerializer::BeginSerialize() +void JSONSerializer::BeginSerialize( QString &sName ) { m_bCommaNeeded = false; @@ -104,7 +104,10 @@ void JSONSerializer::EndObject ( const QString &sName, const QObject *pObject // ////////////////////////////////////////////////////////////////////////////// -void JSONSerializer::AddProperty( const QString &sName, const QVariant &vValue ) +void JSONSerializer::AddProperty( const QString &sName, + const QVariant &vValue, + const QMetaObject *pMetaParent, + const QMetaProperty *pMetaProp ) { if (m_bCommaNeeded) m_Stream << ", "; diff --git a/mythtv/libs/libmythupnp/serializers/jsonSerializer.h b/mythtv/libs/libmythupnp/serializers/jsonSerializer.h index 7b402747764..d1e062854fb 100644 --- a/mythtv/libs/libmythupnp/serializers/jsonSerializer.h +++ b/mythtv/libs/libmythupnp/serializers/jsonSerializer.h @@ -47,12 +47,16 @@ class UPNP_PUBLIC JSONSerializer : public Serializer QTextStream m_Stream; bool m_bCommaNeeded; - virtual void BeginSerialize(); + virtual void BeginSerialize( QString &sName ); virtual void EndSerialize (); virtual void BeginObject( const QString &sName, const QObject *pObject ); virtual void EndObject ( const QString &sName, const QObject *pObject ); - virtual void AddProperty( const QString &sName, const QVariant &vValue ); + + virtual void AddProperty( const QString &sName, + const QVariant &vValue, + const QMetaObject *pMetaParent, + const QMetaProperty *pMetaProp ); void RenderValue ( const QVariant &vValue ); diff --git a/mythtv/libs/libmythupnp/serializers/serializer.cpp b/mythtv/libs/libmythupnp/serializers/serializer.cpp index 4c6eafd0b59..63c9947cda9 100644 --- a/mythtv/libs/libmythupnp/serializers/serializer.cpp +++ b/mythtv/libs/libmythupnp/serializers/serializer.cpp @@ -56,7 +56,7 @@ void Serializer::Serialize( const QObject *pObject, const QString &_sName ) // --------------------------------------------------------------- - BeginSerialize(); + BeginSerialize( sName ); SerializeObject( pObject, sName ); @@ -67,11 +67,12 @@ void Serializer::Serialize( const QObject *pObject, const QString &_sName ) // ////////////////////////////////////////////////////////////////////////////// -void Serializer::Serialize( const QVariant &vValue, const QString &sName ) +void Serializer::Serialize( const QVariant &vValue, const QString &_sName ) { - BeginSerialize(); + QString sName( _sName ); + BeginSerialize( sName ); - AddProperty( sName, vValue ); + AddProperty( sName, vValue, NULL, NULL ); EndSerialize(); } @@ -115,7 +116,7 @@ void Serializer::SerializeObjectProperties( const QObject *pObject ) QVariant value( pObject->property( pszPropName ) ); - AddProperty( sPropName, value ); + AddProperty( sPropName, value, pMetaObject, &metaProperty ); } } } diff --git a/mythtv/libs/libmythupnp/serializers/serializer.h b/mythtv/libs/libmythupnp/serializers/serializer.h index f3a4f187433..abeb73c2207 100644 --- a/mythtv/libs/libmythupnp/serializers/serializer.h +++ b/mythtv/libs/libmythupnp/serializers/serializer.h @@ -42,12 +42,16 @@ class UPNP_PUBLIC Serializer { protected: - virtual void BeginSerialize() {} + virtual void BeginSerialize( QString &sName ) {} virtual void EndSerialize () {} virtual void BeginObject( const QString &sName, const QObject *pObject ) = 0; virtual void EndObject ( const QString &sName, const QObject *pObject ) = 0; - virtual void AddProperty( const QString &sName, const QVariant &vValue ) = 0; + + virtual void AddProperty( const QString &sName, + const QVariant &vValue, + const QMetaObject *pMetaParent, + const QMetaProperty *pMetaProp ) = 0; ////////////////////////////////////////////////////////////////////// @@ -56,8 +60,8 @@ class UPNP_PUBLIC Serializer public: - void Serialize( const QObject *pObject, const QString &_sName = QString() ); - void Serialize( const QVariant &vValue, const QString &sName ); + virtual void Serialize( const QObject *pObject, const QString &_sName = QString() ); + virtual void Serialize( const QVariant &vValue, const QString &sName ); ////////////////////////////////////////////////////////////////////// // Helper Methods diff --git a/mythtv/libs/libmythupnp/serializers/soapSerializer.h b/mythtv/libs/libmythupnp/serializers/soapSerializer.h index 8ced4a9ec5b..b10aae0855c 100644 --- a/mythtv/libs/libmythupnp/serializers/soapSerializer.h +++ b/mythtv/libs/libmythupnp/serializers/soapSerializer.h @@ -60,6 +60,7 @@ class UPNP_PUBLIC SoapSerializer : public XmlSerializer headers.insert( "EXT", "" ); } + protected: QString m_sNamespace; @@ -68,18 +69,19 @@ class UPNP_PUBLIC SoapSerializer : public XmlSerializer // // ------------------------------------------------------------------ - virtual void BeginSerialize() + virtual void BeginSerialize( QString &sName) { m_pXmlWriter->writeStartDocument( "1.0" ); m_pXmlWriter->writeStartElement("http://schemas.xmlsoap.org/soap/envelope/", "Envelope" ); - m_pXmlWriter->writeAttribute( "encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/" ); + // m_pXmlWriter->writeAttribute( "encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/" ); m_pXmlWriter->writeStartElement( "http://schemas.xmlsoap.org/soap/envelope/", "Body" ); m_pXmlWriter->writeStartElement( m_sRequestName + "Response" ); m_pXmlWriter->writeAttribute( "xmlns", m_sNamespace ); + sName = m_sRequestName + "Result"; } }; diff --git a/mythtv/libs/libmythupnp/serializers/xmlSerializer.cpp b/mythtv/libs/libmythupnp/serializers/xmlSerializer.cpp index a953212008f..8c66b432b8a 100644 --- a/mythtv/libs/libmythupnp/serializers/xmlSerializer.cpp +++ b/mythtv/libs/libmythupnp/serializers/xmlSerializer.cpp @@ -25,6 +25,13 @@ #include +// -------------------------------------------------------------------------- +// This version should be bumped if the serializer code is changed in a way +// that changes the schema layout of the rendered XML. +// -------------------------------------------------------------------------- + +#define XML_SERIALIZER_VERSION "1.1" + ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// @@ -62,7 +69,7 @@ QString XmlSerializer::GetContentType() // ////////////////////////////////////////////////////////////////////////////// -void XmlSerializer::BeginSerialize() +void XmlSerializer::BeginSerialize( QString &sName ) { m_pXmlWriter->writeStartDocument( "1.0" ); // m_pXmlWriter->writeStartElement( m_sRequestName + "Response" ); @@ -91,6 +98,9 @@ void XmlSerializer::BeginObject( const QString &sName, const QObject *pObject ) if (nIdx >=0) m_pXmlWriter->writeAttribute( "version", pMeta->classInfo( nIdx ).value() ); + + m_pXmlWriter->writeAttribute( "serializerVersion", XML_SERIALIZER_VERSION ); + } ////////////////////////////////////////////////////////////////////////////// @@ -106,13 +116,16 @@ void XmlSerializer::EndObject ( const QString &sName, const QObject *pObject ) // ////////////////////////////////////////////////////////////////////////////// -void XmlSerializer::AddProperty( const QString &sName, const QVariant &vValue ) +void XmlSerializer::AddProperty( const QString &sName, + const QVariant &vValue, + const QMetaObject *pMetaParent, + const QMetaProperty *pMetaProp ) { if (sName != "Description") { m_pXmlWriter->writeStartElement( sName ); - RenderValue( sName, vValue ); + RenderValue( GetContentName( sName, pMetaParent, pMetaProp ), vValue ); m_pXmlWriter->writeEndElement(); } @@ -128,7 +141,7 @@ void XmlSerializer::RenderValue( const QString &sName, const QVariant &vValue ) { // ----------------------------------------------------------------------- - // See if this value is actually a child object + // See if this value is actually a QObject // ----------------------------------------------------------------------- if ( vValue.canConvert< QObject* >()) @@ -139,7 +152,6 @@ void XmlSerializer::RenderValue( const QString &sName, const QVariant &vValue ) return; } - // ----------------------------------------------------------------------- // Handle QVariant special cases... // ----------------------------------------------------------------------- @@ -179,14 +191,19 @@ void XmlSerializer::RenderValue( const QString &sName, const QVariant &vValue ) void XmlSerializer::RenderList( const QString &sName, const QVariantList &list ) { - QString sItemName = GetItemName( sName ); +// QString sItemName; QListIterator< QVariant > it( list ); while (it.hasNext()) { - m_pXmlWriter->writeStartElement( sItemName ); - RenderValue( sName, it.next() ); + QVariant vValue = it.next(); + +// if (sItemName.isEmpty()) +// sItemName = GetItemName( QMetaType::typeName( vValue.userType() ) ); + + m_pXmlWriter->writeStartElement( sName ); + RenderValue( sName, vValue ); m_pXmlWriter->writeEndElement(); } } @@ -203,7 +220,7 @@ void XmlSerializer::RenderStringList( const QString &sName, const QStringList &l while (it.hasNext()) { - m_pXmlWriter->writeStartElement( sItemName ); + m_pXmlWriter->writeStartElement( "String" ); m_pXmlWriter->writeCharacters ( it.next() ); m_pXmlWriter->writeEndElement(); } @@ -238,9 +255,31 @@ void XmlSerializer::RenderMap( const QString &sName, const QVariantMap &map ) QString XmlSerializer::GetItemName( const QString &sName ) { - if (sName.endsWith( "s" )) - return sName.left( sName.length() - 1); + QString sTypeName( sName ); + + if (sName.at(0) == 'Q') + sTypeName = sName.mid( 1 ); + + sTypeName.remove( "DTC::" ); + sTypeName.remove( QChar('*') ); - return "Value"; + return sTypeName; } +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QString XmlSerializer::GetContentName( const QString &sName, + const QMetaObject *pMetaObject, + const QMetaProperty *pMetaProp ) +{ + QString sMethodClassInfo = sName + "_type"; + + int nClassIdx = pMetaObject->indexOfClassInfo( sMethodClassInfo.toAscii() ); + + if (nClassIdx >=0) + return GetItemName( pMetaObject->classInfo( nClassIdx ).value() ); + + return sName; +} \ No newline at end of file diff --git a/mythtv/libs/libmythupnp/serializers/xmlSerializer.h b/mythtv/libs/libmythupnp/serializers/xmlSerializer.h index 6302bd82876..162717e77bd 100644 --- a/mythtv/libs/libmythupnp/serializers/xmlSerializer.h +++ b/mythtv/libs/libmythupnp/serializers/xmlSerializer.h @@ -48,13 +48,16 @@ class UPNP_PUBLIC XmlSerializer : public Serializer QXmlStreamWriter *m_pXmlWriter; QString m_sRequestName; - virtual void BeginSerialize(); + virtual void BeginSerialize( QString &sName ); virtual void EndSerialize (); virtual void BeginObject( const QString &sName, const QObject *pObject ); virtual void EndObject ( const QString &sName, const QObject *pObject ); - virtual void AddProperty( const QString &sName, const QVariant &vValue ); + virtual void AddProperty( const QString &sName, + const QVariant &vValue, + const QMetaObject *pMetaParent, + const QMetaProperty *pMetaProp ); void RenderValue ( const QString &sName, const QVariant &vValue ); @@ -64,6 +67,9 @@ class UPNP_PUBLIC XmlSerializer : public Serializer QString GetItemName ( const QString &sName ); + QString GetContentName ( const QString &sName, + const QMetaObject *pMetaObject, + const QMetaProperty *pMetaProp ); public: diff --git a/mythtv/libs/libmythupnp/serverSideScripting.cpp b/mythtv/libs/libmythupnp/serverSideScripting.cpp new file mode 100644 index 00000000000..46f667f2c5b --- /dev/null +++ b/mythtv/libs/libmythupnp/serverSideScripting.cpp @@ -0,0 +1,297 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: serverSideScripting.cpp +// Created : Mar. 22, 2011 +// +// Purpose : Server Side Scripting support for Html Server +// +// Copyright (c) 2011 David Blain +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or at your option any later version of the LGPL. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see . +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "serverSideScripting.h" +#include "mythverbose.h" + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +ServerSideScripting::ServerSideScripting() +{ + + // ---------------------------------------------------------------------- + // Add Scriptable Objects + // ---------------------------------------------------------------------- + + // Q_SCRIPT_DECLARE_QMETAOBJECT( DTC::MythService, QObject*) + // QScriptValue oClass = engine.scriptValueFromQMetaObject< DTC::MythService >(); + // engine.globalObject().setProperty("Myth", oClass); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +ServerSideScripting::~ServerSideScripting() +{ + Lock(); + + QMap::iterator it = m_mapScripts.begin(); + + for (; it != m_mapScripts.end(); ++it) + { + if (*it) + delete (*it); + } + + m_mapScripts.clear(); + Unlock(); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void ServerSideScripting::RegisterMetaObjectType( const QString &sName, + const QMetaObject *pMetaObject, + QScriptEngine::FunctionSignature pFunction) +{ + QScriptValue ctor = m_engine.newFunction( pFunction ); + + QScriptValue metaObject = m_engine.newQMetaObject( pMetaObject, ctor ); + m_engine.globalObject().setProperty( sName, metaObject ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool ServerSideScripting::EvaluatePage( QTextStream *pOutStream, const QString &sFileName ) +{ + try + { + bool bFound( false ); + ScriptInfo *pInfo = NULL; + + // ------------------------------------------------------------------ + // See if page has already been loaded + // ------------------------------------------------------------------ + + Lock(); + + if ( (bFound = m_mapScripts.contains( sFileName )) == true ) + pInfo = m_mapScripts[ sFileName ]; + + Unlock(); + + // ------------------------------------------------------------------ + // Load Script File and Create Function + // ------------------------------------------------------------------ + + QFileInfo fileInfo ( sFileName ); + QDateTime dtLastModified = fileInfo.lastModified(); + + if ((pInfo == NULL) || (pInfo->m_dtTimeStamp != dtLastModified )) + { + QString sCode = CreateMethodFromFile( sFileName ); + + QScriptValue func = m_engine.evaluate( sCode, sFileName ); + + if ( m_engine.hasUncaughtException() ) + { + VERBOSE( VB_IMPORTANT, QString( "Error Loading QSP File: %1 - (%2)%3" ) + .arg( sFileName ) + .arg( m_engine.uncaughtExceptionLineNumber() ) + .arg( m_engine.uncaughtException().toString() )); + + return false; + } + + if (pInfo != NULL) + { + pInfo->m_oFunc = func; + pInfo->m_dtTimeStamp = dtLastModified; + } + else + { + pInfo = new ScriptInfo( func, dtLastModified ); + Lock(); + m_mapScripts[ sFileName ] = pInfo; + Unlock(); + } + } + + // ------------------------------------------------------------------ + // Execute function to render output + // ------------------------------------------------------------------ + + OutputStream outStream( pOutStream ); + + QScriptValueList args; + args << m_engine.newQObject( &outStream ); + + pInfo->m_oFunc.call( QScriptValue(), args ); + + if (m_engine.hasUncaughtException()) + { + VERBOSE( VB_IMPORTANT, QString( "Error calling QSP File: %1 - %2" ) + .arg( sFileName ) + .arg( m_engine.uncaughtException().toString() )); + return false; + } + + } + catch( ... ) + { + VERBOSE( VB_IMPORTANT, QString( "Exception while evaluating QSP File: %1" ) + .arg( sFileName )); + + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QString ServerSideScripting::CreateMethodFromFile( const QString &sFileName ) +{ + bool bInCode = false; + QString sBuffer; + QTextStream sCode( &sBuffer ); + + QFile scriptFile( sFileName ); + + if (!scriptFile.open( QIODevice::ReadOnly )) + throw "Unable to open file"; + + try + { + QTextStream stream( &scriptFile ); + + sCode << "(function( os ) {\n"; + + while( !stream.atEnd() ) + { + QString sLine = stream.readLine(); + + bInCode = ProcessLine( sCode, sLine, bInCode ); + } + + sCode << "})"; + } + catch( ... ) + { + VERBOSE( VB_IMPORTANT, QString( "Exception while reading QSP File: %1" ) + .arg( sFileName )); + } + + scriptFile.close(); + + sCode.flush(); + + return sBuffer; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool ServerSideScripting::ProcessLine( QTextStream &sCode, + QString &sLine, + bool bInCode ) +{ + int nStartPos = 0; + int nEndPos = 0; + int nMatchPos = 0; + bool bMatchFound = false; + + sLine = sLine.trimmed(); + + QString sExpecting = bInCode ? "%>" : "<%"; + bool bNewLine = !(sLine.startsWith( sExpecting )); + + while (nStartPos < sLine.length()) + { + nEndPos = sLine.length() - 1; + + sExpecting = bInCode ? "%>" : "<%"; + nMatchPos = sLine.indexOf( sExpecting, nStartPos ); + + // ------------------------------------------------------------------ + // If not found, Adjust to Save entire line + // ------------------------------------------------------------------ + + if (nMatchPos < 0) + { + nMatchPos = nEndPos + 1; + bMatchFound = false; + } + else + bMatchFound = true; + + // ------------------------------------------------------------------ + // Add Code or Text to Line + // ------------------------------------------------------------------ + + QString sSegment = sLine.mid( nStartPos, nMatchPos - nStartPos ); + + if ( !sSegment.isEmpty()) + { + if (bInCode) + { + // Add Code + + if (sSegment.startsWith( "=" )) + sCode << "os.write( " << sSegment.mid( 1 ) << " ); " << "\n"; + else + sCode << sSegment << "\n"; + + if (bMatchFound) + bInCode = false; + } + else + { + // Add Text + + sSegment.replace( '"', "\\\"" ); + + sCode << "os.write( \"" << sSegment << "\" );\n"; + + if (bMatchFound) + bInCode = true; + else + bNewLine = false; + } + } + else + { + if (bMatchFound) + bInCode = !bInCode; + } + + nStartPos = nMatchPos + 2; + } + + if ((bNewLine) && !bInCode ) + sCode << "os.writeln( \"\" );\n"; + + return bInCode; +} diff --git a/mythtv/libs/libmythupnp/serverSideScripting.h b/mythtv/libs/libmythupnp/serverSideScripting.h new file mode 100644 index 00000000000..b832aea98d7 --- /dev/null +++ b/mythtv/libs/libmythupnp/serverSideScripting.h @@ -0,0 +1,112 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: serverSideScripting.h +// Created : Mar. 22, 2011 +// +// Purpose : Server Side Scripting support for Html Server +// +// Copyright (c) 2011 David Blain +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or at your option any later version of the LGPL. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see . +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef SERVERSIDESCRIPTING_H_ +#define SERVERSIDESCRIPTING_H_ + +#include "upnpexp.h" + +#include +#include +#include +#include +#include +#include +#include + +class ScriptInfo +{ + public: + QScriptValue m_oFunc; + QDateTime m_dtTimeStamp; + + ScriptInfo() {} + + ScriptInfo( QScriptValue func, QDateTime dt ) + : m_oFunc( func ), m_dtTimeStamp( dt ) + {} +}; + +class UPNP_PUBLIC ServerSideScripting +{ + protected: + + QMutex m_mutex; + QMap< QString, ScriptInfo* > m_mapScripts; + + void Lock () { m_mutex.lock(); } + void Unlock () { m_mutex.unlock(); } + + public: + + QScriptEngine m_engine; + + public: + + ServerSideScripting(); + ~ServerSideScripting(); + + void RegisterMetaObjectType( const QString &sName, + const QMetaObject *pMetaObject, + QScriptEngine::FunctionSignature pFunction); + + bool EvaluatePage( QTextStream *pOutStream, const QString &sFileName ); + + protected: + + QString CreateMethodFromFile( const QString &sFileName ); + + bool ProcessLine ( QTextStream &sCode, + QString &sLine, + bool bInCode ); +}; + + +class OutputStream : public QObject, public QScriptable +{ + Q_OBJECT + + public: + + OutputStream( QTextStream *pStream, QObject *parent = 0 ) + : QObject( parent ), m_pTextStream( pStream ) {} + + ~OutputStream() {} + + public slots: + + void write( const QString &sValue ) + { + *m_pTextStream << sValue; + } + + void writeln( const QString &sValue ) + { + *m_pTextStream << sValue << "\n"; + } + + private: + QTextStream *m_pTextStream; +}; + +#endif diff --git a/mythtv/libs/libmythupnp/servicehost.cpp b/mythtv/libs/libmythupnp/servicehost.cpp index b3baee5f8c5..01ca7e3b8a4 100644 --- a/mythtv/libs/libmythupnp/servicehost.cpp +++ b/mythtv/libs/libmythupnp/servicehost.cpp @@ -27,6 +27,8 @@ #include "servicehost.h" #include "wsdl.h" +#define _MAX_PARAMS 256 + ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// @@ -42,9 +44,8 @@ MethodInfo::MethodInfo() QVariant MethodInfo::Invoke( Service *pService, const QStringMap &reqParams ) { - QVariant vResult; - - void *pResult = NULL; + HttpRedirectException exception; + bool bExceptionThrown = false; if (!pService) throw; @@ -52,62 +53,129 @@ QVariant MethodInfo::Invoke( Service *pService, const QStringMap &reqParams ) QList paramNames = m_oMethod.parameterNames(); QList paramTypes = m_oMethod.parameterTypes(); - // -------------------------------------------------------------- - // Create Parameter array.... - // -------------------------------------------------------------- + // ---------------------------------------------------------------------- + // Create Parameter array (Can't have more than _MAX_PARAMS parameters).... + // switched to static array for performance. + // ---------------------------------------------------------------------- - void **param = new void*[ paramNames.length()+1 ]; + void *param[ _MAX_PARAMS ]; + int types[ _MAX_PARAMS ]; - // -------------------------------------------------------------- - // Add a place for the Return value - // -------------------------------------------------------------- + memset( param, 0, sizeof( param )); + memset( types, 0, sizeof( types )); - param[ 0 ] = &pResult; + try + { + // -------------------------------------------------------------- + // Add a place for the Return value + // -------------------------------------------------------------- - // -------------------------------------------------------------- - // Fill in parameters from request values - // -------------------------------------------------------------- + int nRetIdx = QMetaType::type( m_oMethod.typeName() ); - for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ ) - { - QString sValue = reqParams[ paramNames[ nIdx ] ]; - QString sParamType = paramTypes[ nIdx ]; + if (nRetIdx != 0) + { + param[ 0 ] = QMetaType::construct( nRetIdx ); + types[ 0 ] = nRetIdx; + } + else + { + param[ 0 ] = NULL; + types[ 0 ] = 0; + } - param[nIdx+1] = pService->ConvertToParameterPtr( sParamType, sValue ); - } + // -------------------------------------------------------------- + // Fill in parameters from request values + // -------------------------------------------------------------- - /* ******************************** - QThread *currentThread = QThread::currentThread(); - QThread *objectThread = pService->thread(); + for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ ) + { + QString sValue = reqParams[ paramNames[ nIdx ] ]; + QString sParamType = paramTypes[ nIdx ]; - if (currentThread == objectThread) - VERBOSE( VB_UPNP, "*** Threads are same ***" ); - else - VERBOSE( VB_UPNP, "*** Threads are Different!!! ***" ); - ******************************** */ + int nId = QMetaType::type( paramTypes[ nIdx ] ); + void *pParam = NULL; + + if (nId != 0) + { + pParam = QMetaType::construct( nId ); + } + else + { + VERBOSE( VB_IMPORTANT, QString( "MethodInfo::Invoke - Type unknown '%1'" ) + .arg( sParamType )); + } + + types[nIdx+1] = nId; + param[nIdx+1] = pService->ConvertToParameterPtr( nId, sParamType, pParam, sValue ); + } + + /* ******************************** + QThread *currentThread = QThread::currentThread(); + QThread *objectThread = pService->thread(); + + if (currentThread == objectThread) + VERBOSE( VB_UPNP, "*** Threads are same ***" ); + else + VERBOSE( VB_UPNP, "*** Threads are Different!!! ***" ); + ******************************** */ + + pService->qt_metacall( QMetaObject::InvokeMetaMethod, + m_nMethodIndex, + param ); - bool bSuccess = pService->qt_metacall( QMetaObject::InvokeMetaMethod, - m_nMethodIndex, - param ); + // -------------------------------------------------------------- + // Delete param array, skip return parameter since not dynamically created. + // -------------------------------------------------------------- + + for (int nIdx=1; nIdx < paramNames.length()+1; nIdx++) + { + if ((types[ nIdx ] != 0) && (param[ nIdx ] != NULL)) + QMetaType::destroy( types[ nIdx ], param[ nIdx ] ); + } + } + catch( QString sMsg) + { + VERBOSE( VB_IMPORTANT, QString( "MethodInfo::Invoke - An Exception Occurred: %1" ) + .arg( sMsg )); + + if ((types[ 0 ] != 0) && (param[ 0 ] != NULL )) + QMetaType::destroy( types[ 0 ], param[ 0 ] ); + + throw sMsg; + } + catch( HttpRedirectException ex ) + { + bExceptionThrown = true; + exception = ex; + } + catch( ... ) + { + VERBOSE( VB_IMPORTANT, "MethodInfo::Invoke - An Exception Occurred" ); + } // -------------------------------------------------------------- - // Delete param array, skip return parameter since not dynamically created. + // return the result after converting to a QVariant // -------------------------------------------------------------- - for (int nIdx=1; nIdx < paramNames.length()+1; nIdx++) + QVariant vReturn; + + if ( param[ 0 ] != NULL) { - if (param[ nIdx ] != NULL) - delete param[ nIdx ]; - } + vReturn = pService->ConvertToVariant( types[ 0 ], param[ 0 ] ); - if (param) - delete[] param; + if (types[ 0 ] != 0) + QMetaType::destroy( types[ 0 ], param[ 0 ] ); + + } // -------------------------------------------------------------- - // return the result after converting to a QVariant + // Re-throw exception if needed. // -------------------------------------------------------------- - return pService->ConvertToVariant( m_oMethod.typeName(), pResult ); + if (bExceptionThrown) + throw exception; + + return vReturn; } ////////////////////////////////////////////////////////////////////////////// @@ -208,9 +276,14 @@ bool ServiceHost::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pReque if (( pRequest->m_eType == RequestTypeGet ) && ( pRequest->m_sMethod == "wsdl" )) { + pService = qobject_cast< Service* >( m_oMetaObject.newInstance()); + Wsdl wsdl( this ); - return wsdl.GetWSDL( pRequest ); + wsdl.GetWSDL( pRequest ); + + delete pService; + return true; } if (( pRequest->m_eType == RequestTypeGet ) && @@ -227,11 +300,32 @@ bool ServiceHost::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pReque } } + // -------------------------------------------------------------- + // Allow a more REST like calling convention. If the Method + // Name isn't found, search for one with the request method + // appended to the name ( "Get" or "Put" for POST) // -------------------------------------------------------------- - if (m_Methods.contains( pRequest->m_sMethod )) + QString sMethodName = pRequest->m_sMethod; + bool bMethodFound = false; + + if (m_Methods.contains( sMethodName )) + bMethodFound = true; + else { - MethodInfo oInfo = m_Methods.value( pRequest->m_sMethod ); + switch( pRequest->m_eType ) + { + case RequestTypeGet : sMethodName = "Get" + sMethodName; break; + case RequestTypePost: sMethodName = "Put" + sMethodName; break; + } + + if (m_Methods.contains( sMethodName )) + bMethodFound = true; + } + + if (bMethodFound) + { + MethodInfo oInfo = m_Methods.value( sMethodName ); if (( pRequest->m_eType & oInfo.m_eRequestType ) != 0) { @@ -253,11 +347,27 @@ bool ServiceHost::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pReque UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); } } + catch( HttpRedirectException ex ) + { + UPnp::FormatRedirectResponse( pRequest, ex.hostName ); + bHandled = true; + } + catch( HttpException ex ) + { + VERBOSE( VB_IMPORTANT, ex.msg ); + + UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, ex.msg ); + + bHandled = true; + + } catch( QString sMsg ) { VERBOSE( VB_IMPORTANT, sMsg ); UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg ); + + bHandled = true; } catch( ... ) { @@ -266,6 +376,8 @@ bool ServiceHost::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pReque VERBOSE( VB_IMPORTANT, sMsg ); UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg ); + + bHandled = true; } if (pService != NULL) @@ -302,16 +414,16 @@ bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QObject *pResults ) // ///////////////////////////////////////////////////////////////////////////// -bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QFileInfo *pInfo ) +bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QFileInfo oInfo ) { - if (pInfo != NULL) + QString sName = oInfo.absoluteFilePath(); + + if (oInfo.exists()) { - if (pInfo->isSymLink()) - pRequest->FormatFileResponse( pInfo->symLinkTarget() ); + if (oInfo.isSymLink()) + pRequest->FormatFileResponse( oInfo.symLinkTarget() ); else - pRequest->FormatFileResponse( pInfo->absoluteFilePath() ); - - delete pInfo; + pRequest->FormatFileResponse( oInfo.absoluteFilePath() ); } else { @@ -336,11 +448,11 @@ bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QVariant vValue ) return FormatResponse( pRequest, (QObject *)pObject ); } - if ( vValue.canConvert< QFileInfo* >()) + if ( vValue.canConvert< QFileInfo >()) { - const QFileInfo *pFileInfo = vValue.value< QFileInfo* >(); + const QFileInfo oFileInfo = vValue.value< QFileInfo >(); - return FormatResponse( pRequest, (QFileInfo*)pFileInfo ); + return FormatResponse( pRequest, oFileInfo ); } // ---------------------------------------------------------------------- @@ -349,7 +461,7 @@ bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QVariant vValue ) Serializer *pSer = pRequest->GetSerializer(); - pSer->Serialize( vValue, "value" ); + pSer->Serialize( vValue, vValue.typeName() ); pRequest->FormatActionResponse( pSer ); diff --git a/mythtv/libs/libmythupnp/servicehost.h b/mythtv/libs/libmythupnp/servicehost.h index 5b7e0334a04..d62fd95e809 100644 --- a/mythtv/libs/libmythupnp/servicehost.h +++ b/mythtv/libs/libmythupnp/servicehost.h @@ -80,7 +80,7 @@ class UPNP_PUBLIC ServiceHost : public HttpServerExtension protected: virtual bool FormatResponse( HTTPRequest *pRequest, QObject *pResults ); - virtual bool FormatResponse( HTTPRequest *pRequest, QFileInfo *pResults ); + virtual bool FormatResponse( HTTPRequest *pRequest, QFileInfo oInfo ); virtual bool FormatResponse( HTTPRequest *pRequest, QVariant vValue ); public: diff --git a/mythtv/libs/libmythupnp/upnp.cpp b/mythtv/libs/libmythupnp/upnp.cpp index 4df9896f8af..96811327332 100644 --- a/mythtv/libs/libmythupnp/upnp.cpp +++ b/mythtv/libs/libmythupnp/upnp.cpp @@ -340,3 +340,28 @@ void UPnp::FormatErrorResponse( HTTPRequest *pRequest, else VERBOSE( VB_IMPORTANT, "UPnp::FormatErrorResponse : Response not created - pRequest == NULL" ); } + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void UPnp::FormatRedirectResponse( HTTPRequest *pRequest, + const QString &hostName ) +{ + pRequest->m_eResponseType = ResponseTypeOther; + pRequest->m_nResponseStatus = 301; + + QStringList sItems = pRequest->m_sRawRequest.split( ' ' ); + + QString sUrl = "http://" + pRequest->m_mapHeaders[ "host" ] + sItems[1]; + + QUrl url( sUrl ); + + url.setHost( hostName ); + + pRequest->m_mapRespHeaders[ "Location" ] = url.toString(); + + VERBOSE( VB_UPNP, QString( "Sending http redirect to: %1").arg( url.toString() ) ); + + pRequest->SendResponse(); +} diff --git a/mythtv/libs/libmythupnp/upnp.h b/mythtv/libs/libmythupnp/upnp.h index 714ad9883ab..41e9350d502 100644 --- a/mythtv/libs/libmythupnp/upnp.h +++ b/mythtv/libs/libmythupnp/upnp.h @@ -155,6 +155,10 @@ class UPNP_PUBLIC UPnp UPnPResultCode eCode, const QString &sMsg = "" ); + static void FormatRedirectResponse( HTTPRequest *pRequest, + const QString &hostName ); + + }; #endif diff --git a/mythtv/libs/libmythupnp/wsdl.cpp b/mythtv/libs/libmythupnp/wsdl.cpp index e05e956f51d..6ba6421cc57 100644 --- a/mythtv/libs/libmythupnp/wsdl.cpp +++ b/mythtv/libs/libmythupnp/wsdl.cpp @@ -45,8 +45,8 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) QDomElement oNode; QString sClassName = m_pServiceHost->GetServiceMetaObject().className(); - QString sTargetNamespace = QString( "http://mythtv.org/%1/" ).arg( sClassName ); - + QString sTargetNamespace = "http://mythtv.org"; + m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions"); m_oRoot.setAttribute( "targetNamespace", sTargetNamespace ); @@ -55,6 +55,9 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) m_oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema" ); m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" ); m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace ); + m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" ); + + m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) ); m_oTypes = createElement( "types" ); m_oLastMsg = m_oTypes; @@ -71,7 +74,8 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) m_oPortType.setAttribute( "name", sClassName ); oNode = createElement( "xs:schema" ); - oNode.setAttribute( "targetNamespace", sTargetNamespace ); + oNode.setAttribute( "targetNamespace" , sTargetNamespace ); + oNode.setAttribute( "elementFormDefault", "qualified" ); m_oTypes.appendChild( oNode ); m_oTypes = oNode; @@ -80,12 +84,11 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) // Add Bindings... // ---------------------------------------------------------------------- - m_oBindings.setAttribute( "name", "Soap" ); - m_oBindings.setAttribute( "type", QString( "tns:%1" ) - .arg( sClassName )); + m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName )); + m_oBindings.setAttribute( "type", QString( "tns:%1" ).arg( sClassName )); oNode = createElement( "soap:binding" ); - oNode.setAttribute( "style" , "document" ); + //oNode.setAttribute( "style" , "document" ); oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" ); m_oBindings.appendChild( oNode ); @@ -105,8 +108,12 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) QString sRequestTypeName = oInfo.m_sName; QString sResponseTypeName = oInfo.m_sName + "Response"; - QString sInputMsgName = QString( "%1InputMessage" ).arg( oInfo.m_sName ); - QString sOutputMsgName = QString( "%1OutputMessage" ).arg( oInfo.m_sName ); + QString sInputMsgName = QString( "%1_%2_InputMessage" ) + .arg( sClassName ) + .arg( oInfo.m_sName ); + QString sOutputMsgName = QString( "%1_%2_OutputMessage" ) + .arg( sClassName ) + .arg( oInfo.m_sName ); // ------------------------------------------------------------------ // Create PortType Operations @@ -121,7 +128,11 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) // ------------------------------------------------------------------ oNode = createElement( "input" ); - oNode.setAttribute( "message", "tns:" + sInputMsgName ); + oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" ) + .arg( sTargetNamespace ) + .arg( sClassName ) + .arg( oInfo.m_sName )); + oNode.setAttribute( "message" , "tns:" + sInputMsgName ); oOp.appendChild( oNode ); @@ -130,6 +141,10 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) // ------------------------------------------------------------------ oNode = createElement( "output" ); + oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" ) + .arg( sTargetNamespace ) + .arg( sClassName ) + .arg( oInfo.m_sName )); oNode.setAttribute( "message", "tns:" + sOutputMsgName ); oOp.appendChild( oNode ); @@ -165,7 +180,7 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) // ------------------------------------------------------------------ m_oTypes.appendChild( CreateMethodType( oInfo, sResponseTypeName, true ) ); - + // ------------------------------------------------------------------ // Create Soap Binding Operations // ------------------------------------------------------------------ @@ -177,23 +192,18 @@ bool Wsdl::GetWSDL( HTTPRequest *pRequest ) // Add Service Details // ---------------------------------------------------------------------- - QString sServiceName = QString( "%1Service" ).arg( sClassName ); + QString sServiceName = QString( "%1Services" ).arg( sClassName ); m_oService.setAttribute( "name", sServiceName ); - oNode = createElement( "documentation" ); - oNode.appendChild( createTextNode( sServiceName )); - - m_oService.appendChild( oNode ); - // ------------------------------------------------------------------ // Add Service Port // ------------------------------------------------------------------ QDomElement oPort = createElement( "port" ); - oPort.setAttribute( "name" , "Soap" ); - oPort.setAttribute( "binding", "tns:Soap" ); + oPort.setAttribute( "name" , QString("BasicHttpBinding_%1" ).arg( sClassName )); + oPort.setAttribute( "binding", QString("tns:BasicHttpBinding_%1").arg( sClassName )); oNode = createElement( "soap:address" ); oNode.setAttribute( "location", "http://localhost:6544/" + m_pServiceHost->GetServiceControlURL() ); @@ -314,20 +324,18 @@ QDomElement Wsdl::CreateMethodType( MethodInfo &oInfo, QString sType = oInfo.m_oMethod.typeName(); - int nPos = sType.lastIndexOf( ':' ); - - if (nPos >=0 ) - sType = sType.mid( nPos +1 ); - +// sType.remove( "DTC::" ); sType.remove( QChar('*') ); + sTypeName.remove( "Response" ); + oNode.setAttribute( "minOccurs", 0 ); oNode.setAttribute( "name" , sTypeName + "Result" ); oNode.setAttribute( "nillable" , true ); //-=>TODO: This may need to be determined by sParamType - oNode.setAttribute( "type" , "xs:string" ); - oNode.setAttribute( "type" , ConvertTypeToXSD( sType )); - CreateType( sType ); + bool bCustomType = CreateType( NULL, sType ); + + oNode.setAttribute( "type" , "tns:" + ConvertTypeToXSD( sType, bCustomType )); oSeqNode.appendChild( oNode ); } @@ -359,17 +367,27 @@ QDomElement Wsdl::CreateMethodType( MethodInfo &oInfo, // ///////////////////////////////////////////////////////////////////////////// -void Wsdl::CreateType( QString sTypeName ) +bool Wsdl::CreateType( QObject *pParent, QString &sTypeName ) { if ( m_typesCreated.contains( sTypeName )) - return; + return true; + +// sTypeName.remove( "DTC::" ); + sTypeName.remove( QChar('*') ); - sTypeName = "DTC::" + sTypeName; - - int id = QMetaType::type( sTypeName.toLatin1() ); + int id = QMetaType::type( sTypeName.toUtf8() ); + + switch( id ) + { + case QMetaType::QStringList: + return CreateArrayType( pParent, sTypeName, "Value", "string", true ); + + case QMetaType::QVariantMap: + return CreateMapType( pParent, sTypeName, "Settings", "string", true ); + } if ((id == -1) || (id < QMetaType::User)) - return; + return false; // ------------------------------------------------------------------ // Need to create an instance of the class to access it's metadata. @@ -385,14 +403,12 @@ void Wsdl::CreateType( QString sTypeName ) // Create xsd element structure // ------------------------------------------------------------------ - QDomElement oElementNode = createElement( "xs:element" ); - - oElementNode.setAttribute( "name", ConvertTypeToXSD( sTypeName )); - QDomElement oTypeNode = createElement( "xs:complexType" ); QDomElement oSeqNode = createElement( "xs:sequence" ); - - oElementNode.appendChild( oTypeNode ); + + oTypeNode.setAttribute( "name", ConvertTypeToXSD( sTypeName, true)); + +// oElementNode.appendChild( oTypeNode ); oTypeNode .appendChild( oSeqNode ); // ------------------------------------------------------------------ @@ -414,35 +430,186 @@ void Wsdl::CreateType( QString sTypeName ) continue; QDomElement oNode = createElement( "xs:element" ); - + QString sType = metaProperty.typeName(); + // if this is a child object, sType will be QObject* + // which we can't use, so we need to read the + // properties value, and read it's metaObject data + + if (sType == "QObject*") + { + QVariant val = metaProperty.read( pClass ); + const QObject *pObject = val.value< QObject* >(); + + sType = pObject->metaObject()->className(); + +// sType.remove( "DTC::" ); + } + oNode.setAttribute( "minOccurs", 0 ); oNode.setAttribute( "name" , metaProperty.name() ); oNode.setAttribute( "nillable" , true ); //-=>TODO: This may need to be determined by sParamType - oNode.setAttribute( "type" , ConvertTypeToXSD( sType )); - CreateType( sType ); + bool bCustomType = CreateType( pClass, sType ); + + oNode.setAttribute( "type" , ((bCustomType) ? "tns:" : "") + ConvertTypeToXSD( sType, bCustomType )); oSeqNode.appendChild( oNode ); } } + QDomElement oElementNode = createElement( "xs:element" ); + + oElementNode.setAttribute( "name" , ConvertTypeToXSD( sTypeName, true)); + oElementNode.setAttribute( "nillable", "true" ); + oElementNode.setAttribute( "type" , "tns:" + ConvertTypeToXSD( sTypeName, true)); + + m_oTypes.appendChild( oTypeNode ); m_oTypes.appendChild( oElementNode ); QMetaType::destroy( id, pClass ); } + m_typesCreated.insert( sTypeName, true ); + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// +// +// +// +// +// +// +/* + + + + + + + */ +///////////////////////////////////////////////////////////////////////////// + +bool Wsdl::CreateArrayType( QObject *pParent, + QString &sTypeName, + QString sElementName, + QString sElementType, + bool bCustomType ) +{ + QString sOrigTypeName( sTypeName ); + + sTypeName = "ArrayOf" + sElementType; + + if ( m_typesCreated.contains( sTypeName )) + return bCustomType; + + QDomElement oTypeNode = createElement( "xs:complexType" ); + QDomElement oSeqNode = createElement( "xs:sequence" ); + + oTypeNode.setAttribute( "name", sTypeName ); + + oTypeNode.appendChild( oSeqNode ); + + QDomElement oNode = createElement( "xs:element" ); + + oNode.setAttribute( "name" , sElementName ); + oNode.setAttribute( "type" , ConvertTypeToXSD( sElementType, false )); + + oNode.setAttribute( "nillable" , true ); //-=>TODO: This may need to be determined by sParamType + oNode.setAttribute( "minOccurs", 0 ); + oNode.setAttribute( "maxOccurs", "unbounded" ); + + oSeqNode.appendChild( oNode ); + + QDomElement oElementNode = createElement( "xs:element" ); + + oElementNode.setAttribute( "name" , sTypeName ); + oElementNode.setAttribute( "nillable", "true" ); + oElementNode.setAttribute( "type" , "tns:" + sTypeName ); + + m_oTypes.appendChild( oTypeNode ); + m_oTypes.appendChild( oElementNode ); + + m_typesCreated.insert( sTypeName, true ); + + return bCustomType; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool Wsdl::CreateMapType( QObject *pParent, + QString &sTypeName, + QString sElementName, + QString sElementType, + bool bCustomType ) +{ +/* + QString sOrigTypeName( sTypeName ); + + sTypeName = "MapOf" + sElementType; + + if ( m_typesCreated.contains( sTypeName )) + return bCustomType; + + QString sMapType = ReadClassInfo( pParent, sElementName + "_type" ); + + CreateType( NULL, sMapType ); + + QDomElement oTypeNode = createElement( "xs:complexType" ); + QDomElement oSeqNode = createElement( "xs:all" ); + + oTypeNode.setAttribute( "name", sTypeName ); + + oTypeNode.appendChild( oSeqNode ); + + QDomElement oNode = createElement( "xs:element" ); + + oNode.setAttribute( "name" , sElementName ); + oNode.setAttribute( "type" , ConvertTypeToXSD( sElementType, false )); + + oNode.setAttribute( "nillable" , true ); //-=>TODO: This may need to be determined by sParamType + oNode.setAttribute( "minOccurs", 0 ); + oNode.setAttribute( "maxOccurs", "unbounded" ); + + oSeqNode.appendChild( oNode ); + + QDomElement oElementNode = createElement( "xs:element" ); + + oElementNode.setAttribute( "name" , sTypeName ); + oElementNode.setAttribute( "nillable", "true" ); + oElementNode.setAttribute( "type" , "tns:" + sTypeName ); + + m_oTypes.appendChild( oTypeNode ); + m_oTypes.appendChild( oElementNode ); m_typesCreated.insert( sTypeName, true ); +*/ + return bCustomType; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QString Wsdl::ConvertTypeToXSD( const QString &sType ) +QString Wsdl::ConvertTypeToXSD( const QString &sType, bool bCustomType ) { + if (bCustomType || sType.startsWith( "DTC::")) + { + QString sTypeName( sType ); + + sTypeName.remove( "DTC::" ); + sTypeName.remove( QChar('*') ); + + return sTypeName; + } + if (sType == "QDateTime") return "xs:dateTime"; @@ -454,3 +621,19 @@ QString Wsdl::ConvertTypeToXSD( const QString &sType ) return "xs:" + sType.toLower(); } + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString Wsdl::ReadClassInfo( QObject *pObject, QString sKey ) +{ + const QMetaObject *pMeta = pObject->metaObject(); + + int nIdx = pMeta->indexOfClassInfo( sKey.toUtf8() ); + + if (nIdx >=0) + return pMeta->classInfo( nIdx ).value(); + + return QString(); +} \ No newline at end of file diff --git a/mythtv/libs/libmythupnp/wsdl.h b/mythtv/libs/libmythupnp/wsdl.h index 6cfdda9b817..04aae5b1ca8 100644 --- a/mythtv/libs/libmythupnp/wsdl.h +++ b/mythtv/libs/libmythupnp/wsdl.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ class UPNP_PUBLIC Wsdl : public QDomDocument protected: - QString ConvertTypeToXSD( const QString &sType ); + QString ConvertTypeToXSD( const QString &sType, bool bCustomType = false ); QDomElement CreateBindingOperation( MethodInfo &oInfo, const QString &sClassName ); @@ -70,8 +71,23 @@ class UPNP_PUBLIC Wsdl : public QDomDocument QString sTypeName, bool bReturnType = false ); + bool CreateType ( QObject *pParent, + QString &sTypeName ); - void CreateType ( QString sTypeName ); + bool CreateArrayType ( QObject *pParent, + QString &sTypeName, + QString sElementName, + QString sElementType, + bool bCustomType ); + + bool CreateMapType ( QObject *pParent, + QString &sTypeName, + QString sElementName, + QString sElementType, + bool bCustomType ); + + QString ReadClassInfo ( QObject *pObject, + QString sKey ); public: diff --git a/mythtv/programs/mythbackend/mediaserver.cpp b/mythtv/programs/mythbackend/mediaserver.cpp index 14a24e817f1..66d27a2ba6d 100644 --- a/mythtv/programs/mythbackend/mediaserver.cpp +++ b/mythtv/programs/mythbackend/mediaserver.cpp @@ -17,6 +17,8 @@ #include "upnpcdsmusic.h" #include "upnpcdsvideo.h" +#include + #include "serviceHosts/mythServiceHost.h" #include "serviceHosts/guideServiceHost.h" #include "serviceHosts/contentServiceHost.h" @@ -118,6 +120,25 @@ void MediaServer::Init(bool bIsMaster, bool bDisableUPnp /* = FALSE */) return; } + // ------------------------------------------------------------------ + // Register Service Types with Scripting Engine + // + // -=>NOTE: We need to know the actual type at compile time for this + // to work, so it needs to be done here. I'm still looking + // into ways that we may encapsulate this in the service + // classes. + // ------------------------------------------------------------------ + + + QScriptEngine* pEngine = m_pHttpServer->ScriptEngine(); + + pEngine->globalObject().setProperty("Myth" , pEngine->scriptValueFromQMetaObject< ScriptableMyth >() ); + pEngine->globalObject().setProperty("Guide" , pEngine->scriptValueFromQMetaObject< ScriptableGuide>() ); + pEngine->globalObject().setProperty("Content", pEngine->scriptValueFromQMetaObject< Content >() ); + pEngine->globalObject().setProperty("Dvr" , pEngine->scriptValueFromQMetaObject< ScriptableDvr >() ); + + // ------------------------------------------------------------------ + if (sIP == "localhost" || sIP.startsWith("127.")) { VERBOSE(VB_IMPORTANT, "MediaServer:: Loopback address specified - " diff --git a/mythtv/programs/mythbackend/mythbackend.pro b/mythtv/programs/mythbackend/mythbackend.pro index d9fec789232..a2a35a63648 100644 --- a/mythtv/programs/mythbackend/mythbackend.pro +++ b/mythtv/programs/mythbackend/mythbackend.pro @@ -2,7 +2,7 @@ include ( ../../settings.pro ) include ( ../../version.pro ) include ( ../programs-libs.pro ) -QT += network xml sql +QT += network xml sql script TEMPLATE = app CONFIG += thread diff --git a/mythtv/programs/mythbackend/services/content.cpp b/mythtv/programs/mythbackend/services/content.cpp index c280b85a193..c81c62e029c 100644 --- a/mythtv/programs/mythbackend/services/content.cpp +++ b/mythtv/programs/mythbackend/services/content.cpp @@ -32,12 +32,13 @@ #include "programinfo.h" #include "previewgenerator.h" #include "backendutil.h" +#include "httprequest.h" ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetFile( const QString &sStorageGroup, const QString &sFileName ) +QFileInfo Content::GetFile( const QString &sStorageGroup, const QString &sFileName ) { QString sGroup = sStorageGroup; @@ -67,7 +68,7 @@ QFileInfo* Content::GetFile( const QString &sStorageGroup, const QString &sFileN { VERBOSE( VB_UPNP, QString("GetFile - Unable to find %1.").arg(sFileName)); - return NULL; + return QFileInfo(); } // ---------------------------------------------------------------------- @@ -76,19 +77,19 @@ QFileInfo* Content::GetFile( const QString &sStorageGroup, const QString &sFileN if (QFile::exists( sFullFileName )) { - return new QFileInfo( sFullFileName ); + return QFileInfo( sFullFileName ); } VERBOSE( VB_UPNP, QString("GetFile - File Does not exist %1.").arg(sFullFileName)); - return NULL; + return QFileInfo(); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -DTC::StringList* Content::GetFileList( const QString &sStorageGroup ) +QStringList Content::GetFileList( const QString &sStorageGroup ) { if (sStorageGroup.isEmpty()) @@ -101,7 +102,7 @@ DTC::StringList* Content::GetFileList( const QString &sStorageGroup ) QStringList sStorageGroupDirs; QString hostname = gCoreContext->GetHostName(); - DTC::StringList *pStringList = new DTC::StringList(); + QStringList oStringList; if (StorageGroup::FindDirs(sStorageGroup, hostname, &sStorageGroupDirs)) { @@ -129,19 +130,19 @@ DTC::StringList* Content::GetFileList( const QString &sStorageGroup ) } else */ - pStringList->Values().append( (*fit).fileName() ); + oStringList.append( (*fit).fileName() ); } } } - return pStringList; + return oStringList; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetVideoArt( int nId ) +QFileInfo Content::GetVideoArt( int nId ) { VERBOSE(VB_UPNP, QString("GetVideoArt ID = %1").arg(nId)); @@ -158,7 +159,7 @@ QFileInfo* Content::GetVideoArt( int nId ) MythDB::DBError("GetVideoArt ", query); if (!query.next()) - return NULL; + return QFileInfo(); QString sFileName = query.value(0).toString(); @@ -167,7 +168,7 @@ QFileInfo* Content::GetVideoArt( int nId ) // ---------------------------------------------------------------------- if (QFile::exists( sFileName )) - return new QFileInfo( sFileName ); + return QFileInfo( sFileName ); // ---------------------------------------------------------------------- // Not there? Perhaps we need to look in a storage group? @@ -180,7 +181,7 @@ QFileInfo* Content::GetVideoArt( int nId ) // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetAlbumArt( int nId, int nWidth, int nHeight ) +QFileInfo Content::GetAlbumArt( int nId, int nWidth, int nHeight ) { QString sFullFileName; @@ -211,10 +212,10 @@ QFileInfo* Content::GetAlbumArt( int nId, int nWidth, int nHeight ) } if (!QFile::exists( sFullFileName )) - return NULL; + return QFileInfo(); if ((nWidth == 0) && (nHeight == 0)) - return new QFileInfo( sFullFileName ); + return QFileInfo( sFullFileName ); QString sNewFileName = QString( "%1.%2x%3.png" ) .arg( sFullFileName ) @@ -226,7 +227,7 @@ QFileInfo* Content::GetAlbumArt( int nId, int nWidth, int nHeight ) // ---------------------------------------------------------------------- if (QFile::exists( sNewFileName )) - return new QFileInfo( sNewFileName ); + return QFileInfo( sNewFileName ); // ---------------------------------------------------------------------- // Must generate Albumart Image, Generate Image and save. @@ -237,7 +238,7 @@ QFileInfo* Content::GetAlbumArt( int nId, int nWidth, int nHeight ) QImage *pImage = new QImage( sFullFileName); if (!pImage) - return NULL; + return QFileInfo(); if (fAspect <= 0) fAspect = (float)(pImage->width()) / pImage->height(); @@ -256,14 +257,14 @@ QFileInfo* Content::GetAlbumArt( int nId, int nWidth, int nHeight ) delete pImage; - return new QFileInfo( sNewFileName ); + return QFileInfo( sNewFileName ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetPreviewImage( int nChanId, +QFileInfo Content::GetPreviewImage( int nChanId, const QDateTime &dtStartTime, int nWidth, int nHeight, @@ -289,7 +290,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, { VERBOSE(VB_IMPORTANT, QString( "GetPreviewImage: no recording for start time '%1'" ) .arg( dtStartTime.toString() )); - return NULL; + return QFileInfo(); } if ( pginfo.GetHostname() != gCoreContext->GetHostName()) @@ -298,9 +299,9 @@ QFileInfo* Content::GetPreviewImage( int nChanId, .arg( gCoreContext->GetHostName()) .arg( pginfo.GetHostname() ); - VERBOSE(VB_IMPORTANT, sMsg); + VERBOSE(VB_UPNP, sMsg); - throw sMsg; + throw HttpRedirectException( pginfo.GetHostname() ); } QString sFileName = GetPlaybackURL(&pginfo); @@ -330,7 +331,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, pginfo.SetPathname(sFileName); if (!pginfo.IsLocal()) - return NULL; + return QFileInfo(); PreviewGenerator *previewgen = new PreviewGenerator( &pginfo, QString(), @@ -343,7 +344,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, previewgen->deleteLater(); if (!ok) - return NULL; + return QFileInfo(); } float fAspect = 0.0; @@ -351,7 +352,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, QImage *pImage = new QImage(sPreviewFileName); if (!pImage) - return NULL; + return QFileInfo(); if (fAspect <= 0) fAspect = (float)(pImage->width()) / pImage->height(); @@ -359,7 +360,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, if (fAspect == 0) { delete pImage; - return NULL; + return QFileInfo(); } bool bDefaultPixmap = (nWidth == 0) && (nHeight == 0); @@ -388,7 +389,7 @@ QFileInfo* Content::GetPreviewImage( int nChanId, if (QFile::exists( sNewFileName )) { delete pImage; - return new QFileInfo( sNewFileName ); + return QFileInfo( sNewFileName ); } QImage img = pImage->scaled( nWidth, nHeight, Qt::IgnoreAspectRatio, @@ -401,15 +402,15 @@ QFileInfo* Content::GetPreviewImage( int nChanId, delete pImage; - return new QFileInfo( sNewFileName ); + return QFileInfo( sNewFileName ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetRecording( int nChanId, - const QDateTime &dtStartTime ) +QFileInfo Content::GetRecording( int nChanId, + const QDateTime &dtStartTime ) { if (!dtStartTime.isValid()) throw( "StartTime is invalid" ); @@ -426,22 +427,24 @@ QFileInfo* Content::GetRecording( int nChanId, "failed" ) .arg( nChanId ) .arg( dtStartTime.toString() )); - return NULL; + return QFileInfo(); } if ( pginfo.GetHostname() != gCoreContext->GetHostName()) { // We only handle requests for local resources - QString sMsg = QString( "GetRecording - To access this " - "recording, send request to %1." ) + QString sMsg = QString( "GetRecording: Wrong Host '%1' request from '%2'." ) + .arg( gCoreContext->GetHostName()) .arg( pginfo.GetHostname() ); VERBOSE( VB_UPNP, sMsg ); - - throw sMsg; + + throw HttpRedirectException( pginfo.GetHostname() ); } + + QString sFileName( GetPlaybackURL(&pginfo) ); // ---------------------------------------------------------------------- @@ -449,16 +452,16 @@ QFileInfo* Content::GetRecording( int nChanId, // ---------------------------------------------------------------------- if (QFile::exists( sFileName )) - return new QFileInfo( sFileName ); + return QFileInfo( sFileName ); - return NULL; + return QFileInfo(); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetMusic( int nId ) +QFileInfo Content::GetMusic( int nId ) { QString sBasePath = gCoreContext->GetSetting( "MusicLocation", ""); QString sFileName; @@ -483,7 +486,7 @@ QFileInfo* Content::GetMusic( int nId ) if (!query.exec()) { MythDB::DBError("GetMusic()", query); - return NULL; + return QFileInfo(); } if (query.next()) @@ -499,16 +502,16 @@ QFileInfo* Content::GetMusic( int nId ) // ---------------------------------------------------------------------- if (QFile::exists( sFileName )) - return new QFileInfo( sFileName ); + return QFileInfo( sFileName ); - return NULL; + return QFileInfo(); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Content::GetVideo( int nId ) +QFileInfo Content::GetVideo( int nId ) { QString sFileName; @@ -526,7 +529,7 @@ QFileInfo* Content::GetVideo( int nId ) if (!query.exec()) { MythDB::DBError("GetVideo()", query); - return NULL; + return QFileInfo(); } if (query.next()) @@ -534,11 +537,11 @@ QFileInfo* Content::GetVideo( int nId ) } if (sFileName.isEmpty()) - return NULL; + return QFileInfo(); if (!QFile::exists( sFileName )) return GetFile( "Videos", sFileName ); - return new QFileInfo( sFileName ); + return QFileInfo( sFileName ); } diff --git a/mythtv/programs/mythbackend/services/content.h b/mythtv/programs/mythbackend/services/content.h index 2d5d552c489..08d65a37cc9 100644 --- a/mythtv/programs/mythbackend/services/content.h +++ b/mythtv/programs/mythbackend/services/content.h @@ -22,6 +22,8 @@ #ifndef CONTENT_H #define CONTENT_H +#include + #include "services/contentServices.h" class Content : public ContentServices @@ -34,29 +36,31 @@ class Content : public ContentServices public: - QFileInfo* GetFile ( const QString &StorageGroup, + QFileInfo GetFile ( const QString &StorageGroup, const QString &FileName ); - DTC::StringList* GetFileList ( const QString &StorageGroup ); + QStringList GetFileList ( const QString &StorageGroup ); - QFileInfo* GetVideoArt ( int Id ); + QFileInfo GetVideoArt ( int Id ); - QFileInfo* GetAlbumArt ( int Id, int Width, int Height ); + QFileInfo GetAlbumArt ( int Id, int Width, int Height ); - QFileInfo* GetPreviewImage ( int ChanId, - const QDateTime &tStartTime, + QFileInfo GetPreviewImage ( int ChanId, + const QDateTime &StartTime, int Width, int Height, int SecsIn ); - QFileInfo* GetRecording ( int ChanId, + QFileInfo GetRecording ( int ChanId, const QDateTime &StartTime ); - QFileInfo* GetMusic ( int Id ); - QFileInfo* GetVideo ( int Id ); + QFileInfo GetMusic ( int Id ); + QFileInfo GetVideo ( int Id ); }; +Q_SCRIPT_DECLARE_QMETAOBJECT( Content, QObject*); + #endif diff --git a/mythtv/programs/mythbackend/services/dvr.h b/mythtv/programs/mythbackend/services/dvr.h index 20c70ec9d2e..9b5d384c295 100644 --- a/mythtv/programs/mythbackend/services/dvr.h +++ b/mythtv/programs/mythbackend/services/dvr.h @@ -22,6 +22,8 @@ #ifndef DVR_H #define DVR_H +#include + #include "services/dvrServices.h" class Dvr : public DvrServices @@ -44,4 +46,53 @@ class Dvr : public DvrServices DTC::EncoderList* Encoders ( ); }; +// -------------------------------------------------------------------------- +// The following class wrapper is due to a limitation in Qt Script Engine. It +// requires all methods that return pointers to user classes that are derived from +// QObject actually return QObject* (not the user class *). If the user class pointer +// is returned, the script engine treats it as a QVariant and doesn't create a +// javascript prototype wrapper for it. +// +// This class allows us to keep the rich return types in the main API class while +// offering the script engine a class it can work with. +// +// Only API Classes that return custom classes needs to implement these wrappers. +// +// We should continue to look for a cleaning solution to this problem. +// -------------------------------------------------------------------------- + +class ScriptableDvr : public QObject +{ + Q_OBJECT + + private: + + Dvr m_obj; + + public: + + Q_INVOKABLE ScriptableDvr( QObject *parent = 0 ) : QObject( parent ) {} + + public slots: + + QObject* GetExpiring ( int StartIndex, + int Count ) + { + return m_obj.GetExpiring( StartIndex, Count ); + } + + QObject* GetRecorded ( bool Descending, + int StartIndex, + int Count ) + { + return m_obj.GetRecorded( Descending, StartIndex, Count ); + } + + QObject* Encoders () { return m_obj.Encoders(); } + + +}; + +Q_SCRIPT_DECLARE_QMETAOBJECT( ScriptableDvr, QObject*); + #endif diff --git a/mythtv/programs/mythbackend/services/guide.cpp b/mythtv/programs/mythbackend/services/guide.cpp index bb9eb8a0974..fb2ff435163 100644 --- a/mythtv/programs/mythbackend/services/guide.cpp +++ b/mythtv/programs/mythbackend/services/guide.cpp @@ -239,21 +239,21 @@ DTC::Program* Guide::GetProgramDetails( int nChanId, // ///////////////////////////////////////////////////////////////////////////// -QFileInfo* Guide::GetChannelIcon( int nChanId, - int nWidth /* = 0 */, - int nHeight /* = 0 */ ) +QFileInfo Guide::GetChannelIcon( int nChanId, + int nWidth /* = 0 */, + int nHeight /* = 0 */ ) { // Get Icon file path QString sFileName = ChannelUtil::GetIcon( nChanId ); if (sFileName.isEmpty()) - return NULL; + return QFileInfo(); if ((nWidth <= 0) && (nHeight <= 0)) { // Use default pixmap - return new QFileInfo( sFileName ); + return QFileInfo( sFileName ); } @@ -267,7 +267,7 @@ QFileInfo* Guide::GetChannelIcon( int nChanId, // ---------------------------------------------------------------------- if (QFile::exists( sNewFileName )) - return new QFileInfo( sNewFileName ); + return QFileInfo( sNewFileName ); // ---------------------------------------------------------------------- // We need to create it... @@ -278,7 +278,7 @@ QFileInfo* Guide::GetChannelIcon( int nChanId, QImage *pImage = new QImage( sFileName ); if (!pImage) - return NULL; + return QFileInfo(); if (fAspect <= 0) fAspect = (float)(pImage->width()) / pImage->height(); @@ -286,7 +286,7 @@ QFileInfo* Guide::GetChannelIcon( int nChanId, if (fAspect == 0) { delete pImage; - return NULL; + return QFileInfo(); } if ( nWidth == 0 ) @@ -302,7 +302,6 @@ QFileInfo* Guide::GetChannelIcon( int nChanId, delete pImage; - return new QFileInfo( sNewFileName ); - + return QFileInfo( sNewFileName ); } diff --git a/mythtv/programs/mythbackend/services/guide.h b/mythtv/programs/mythbackend/services/guide.h index 80d6c6a52e2..f2b9baf321d 100644 --- a/mythtv/programs/mythbackend/services/guide.h +++ b/mythtv/programs/mythbackend/services/guide.h @@ -22,6 +22,8 @@ #ifndef GUIDE_H #define GUIDE_H +#include + #include "serviceUtil.h" #include "services/guideServices.h" @@ -49,9 +51,60 @@ class Guide : public GuideServices DTC::Program* GetProgramDetails ( int ChanId, const QDateTime &StartTime ); - QFileInfo* GetChannelIcon ( int ChanId, + QFileInfo GetChannelIcon ( int ChanId, int Width , int Height ); }; +// -------------------------------------------------------------------------- +// The following class wrapper is due to a limitation in Qt Script Engine. It +// requires all methods that return pointers to user classes that are derived from +// QObject actually return QObject* (not the user class *). If the user class pointer +// is returned, the script engine treats it as a QVariant and doesn't create a +// javascript prototype wrapper for it. +// +// This class allows us to keep the rich return types in the main API class while +// offering the script engine a class it can work with. +// +// Only API Classes that return custom classes needs to implement these wrappers. +// +// We should continue to look for a cleaning solution to this problem. +// -------------------------------------------------------------------------- + +class ScriptableGuide : public QObject +{ + Q_OBJECT + + private: + + Guide m_obj; + + public: + + Q_INVOKABLE ScriptableGuide( QObject *parent = 0 ) : QObject( parent ) {} + + public slots: + + QObject* GetProgramGuide( const QDateTime &StartTime , + const QDateTime &EndTime , + int StartChanId, + int NumChannels, + bool Details ) + { + return m_obj.GetProgramGuide( StartTime, EndTime, StartChanId, NumChannels, Details ); + } + + QObject* GetProgramDetails( int ChanId, const QDateTime &StartTime ) + { + return m_obj.GetProgramDetails( ChanId, StartTime ); + } + + QFileInfo GetChannelIcon( int ChanId, int Width, int Height ) + { + return m_obj.GetChannelIcon( ChanId, Width, Height ); + } +}; + +Q_SCRIPT_DECLARE_QMETAOBJECT( ScriptableGuide, QObject*); + #endif diff --git a/mythtv/programs/mythbackend/services/myth.cpp b/mythtv/programs/mythbackend/services/myth.cpp index 118f065b79c..8648374b1f9 100644 --- a/mythtv/programs/mythbackend/services/myth.cpp +++ b/mythtv/programs/mythbackend/services/myth.cpp @@ -92,28 +92,18 @@ DTC::ConnectionInfo* Myth::GetConnectionInfo( const QString &sPin ) // ///////////////////////////////////////////////////////////////////////////// -DTC::StringList* Myth::GetHostName( ) +QString Myth::GetHostName( ) { if (!gCoreContext) - { throw( QString( "No MythCoreContext in GetHostName." )); - } - - // ---------------------------------------------------------------------- - // return the results of the query - // ---------------------------------------------------------------------- - DTC::StringList *pResults = new DTC::StringList(); - - pResults->Values().append( gCoreContext->GetHostName() ); - - return pResults; + return gCoreContext->GetHostName(); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -DTC::StringList* Myth::GetHosts( ) +QStringList Myth::GetHosts( ) { MSqlQuery query(MSqlQuery::InitCon()); @@ -136,21 +126,19 @@ DTC::StringList* Myth::GetHosts( ) // return the results of the query // ---------------------------------------------------------------------- - DTC::StringList *pResults = new DTC::StringList(); - - //pResults->setObjectName( "HostList" ); + QStringList oList; while (query.next()) - pResults->Values().append( query.value(0).toString() ); + oList.append( query.value(0).toString() ); - return pResults; + return oList; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -DTC::StringList *Myth::GetKeys( ) +QStringList Myth::GetKeys() { MSqlQuery query(MSqlQuery::InitCon()); @@ -170,14 +158,14 @@ DTC::StringList *Myth::GetKeys( ) // return the results of the query // ---------------------------------------------------------------------- - DTC::StringList *pResults = new DTC::StringList(); - + QStringList oResults; + //pResults->setObjectName( "KeyList" ); while (query.next()) - pResults->Values().append( query.value(0).toString() ); + oResults.append( query.value(0).toString() ); - return pResults; + return oResults; } ///////////////////////////////////////////////////////////////////////////// @@ -253,9 +241,9 @@ DTC::StorageGroupDirList *Myth::GetStorageGroupDirs( const QString &sGroupName, // ///////////////////////////////////////////////////////////////////////////// -DTC::SuccessFail *Myth::AddStorageGroupDir( const QString &sGroupName, - const QString &sDirName, - const QString &sHostName ) +bool Myth::AddStorageGroupDir( const QString &sGroupName, + const QString &sDirName, + const QString &sHostName ) { MSqlQuery query(MSqlQuery::InitCon()); @@ -287,16 +275,10 @@ DTC::SuccessFail *Myth::AddStorageGroupDir( const QString &sGroupName, throw( QString( "Database Error executing query." )); } - DTC::SuccessFail *pResults = new DTC::SuccessFail(); - if (query.next()) { if (query.value(0).toInt() > 0) - { - pResults->setResult( false ); - - return pResults; - } + return false; } query.prepare("INSERT storagegroup " @@ -309,25 +291,21 @@ DTC::SuccessFail *Myth::AddStorageGroupDir( const QString &sGroupName, if (!query.exec()) { - delete pResults; - MythDB::DBError("MythAPI::AddStorageGroupDir()", query); throw( QString( "Database Error executing query." )); } - pResults->setResult( true ); - - return pResults; + return true; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -DTC::SuccessFail *Myth::RemoveStorageGroupDir( const QString &sGroupName, - const QString &sDirName, - const QString &sHostName ) +bool Myth::RemoveStorageGroupDir( const QString &sGroupName, + const QString &sDirName, + const QString &sHostName ) { MSqlQuery query(MSqlQuery::InitCon()); @@ -359,11 +337,7 @@ DTC::SuccessFail *Myth::RemoveStorageGroupDir( const QString &sGroupName, throw( QString( "Database Error executing query." )); } - DTC::SuccessFail *pResults = new DTC::SuccessFail(); - - pResults->setResult( true ); - - return pResults; + return true; } ///////////////////////////////////////////////////////////////////////////// @@ -478,21 +452,18 @@ DTC::SettingList *Myth::GetSetting( const QString &sHostName, // ///////////////////////////////////////////////////////////////////////////// -DTC::SuccessFail *Myth::PutSetting( const QString &sHostName, - const QString &sKey, - const QString &sValue ) - +bool Myth::PutSetting( const QString &sHostName, + const QString &sKey, + const QString &sValue ) { + bool bResult = false; + if (!sKey.isEmpty()) { - DTC::SuccessFail *pResults = new DTC::SuccessFail(); - if ( gCoreContext->SaveSettingOnHost( sKey, sValue, sHostName ) ) - pResults->setResult( true ); - else - pResults->setResult( false ); + bResult = true; - return pResults; + return bResult; } throw ( QString( "Key Required" )); diff --git a/mythtv/programs/mythbackend/services/myth.h b/mythtv/programs/mythbackend/services/myth.h index de14d9e8910..025c29e5ae4 100644 --- a/mythtv/programs/mythbackend/services/myth.h +++ b/mythtv/programs/mythbackend/services/myth.h @@ -22,6 +22,8 @@ #ifndef MYTH_H #define MYTH_H +#include + #include "services/mythServices.h" class Myth : public MythServices @@ -30,35 +32,112 @@ class Myth : public MythServices public: - Q_INVOKABLE Myth( QObject *parent = 0 ) {} + Q_INVOKABLE Myth( QObject *parent = 0 ) : MythServices( parent ) {} public: DTC::ConnectionInfo* GetConnectionInfo ( const QString &Pin ); - DTC::StringList* GetHostName ( ); - DTC::StringList* GetHosts ( ); - DTC::StringList* GetKeys ( ); + QString GetHostName ( ); + QStringList GetHosts ( ); + QStringList GetKeys ( ); DTC::StorageGroupDirList* GetStorageGroupDirs ( const QString &GroupName, const QString &HostName ); - DTC::SuccessFail* AddStorageGroupDir ( const QString &GroupName, + bool AddStorageGroupDir ( const QString &GroupName, const QString &DirName, const QString &HostName ); - DTC::SuccessFail* RemoveStorageGroupDir ( const QString &GroupName, - const QString &DirName, - const QString &HostName ); + bool RemoveStorageGroupDir( const QString &GroupName, + const QString &DirName, + const QString &HostName ); DTC::SettingList* GetSetting ( const QString &HostName, const QString &Key, const QString &Default ); - DTC::SuccessFail* PutSetting ( const QString &HostName, + bool PutSetting ( const QString &HostName, const QString &Key, const QString &Value ); +}; + +// -------------------------------------------------------------------------- +// The following class wrapper is due to a limitation in Qt Script Engine. It +// requires all methods that return pointers to user classes that are derived from +// QObject actually return QObject* (not the user class *). If the user class pointer +// is returned, the script engine treats it as a QVariant and doesn't create a +// javascript prototype wrapper for it. +// +// This class allows us to keep the rich return types in the main API class while +// offering the script engine a class it can work with. +// +// Only API Classes that return custom classes needs to implement these wrappers. +// +// We should continue to look for a cleaning solution to this problem. +// -------------------------------------------------------------------------- + +class ScriptableMyth : public QObject +{ + Q_OBJECT + + private: + + Myth m_obj; + + public: + + Q_INVOKABLE ScriptableMyth( QObject *parent = 0 ) : QObject( parent ) {} + public slots: + + QObject* GetConnectionInfo ( const QString &Pin ) + { + return m_obj.GetConnectionInfo( Pin ); + } + + QString GetHostName() { return m_obj.GetHostName(); } + QStringList GetHosts () { return m_obj.GetHosts(); } + QStringList GetKeys () { return m_obj.GetKeys (); } + + QObject* GetStorageGroupDirs ( const QString &GroupName, + const QString &HostName ) + { + return m_obj.GetStorageGroupDirs( GroupName, HostName ); + } + + bool AddStorageGroupDir ( const QString &GroupName, + const QString &DirName, + const QString &HostName ) + { + return m_obj.AddStorageGroupDir( GroupName, DirName, HostName ); + } + + bool RemoveStorageGroupDir ( const QString &GroupName, + const QString &DirName, + const QString &HostName ) + { + return m_obj.RemoveStorageGroupDir( GroupName, DirName, HostName ); + } + + QObject* GetSetting ( const QString &HostName, + const QString &Key, + const QString &Default ) + { + return m_obj.GetSetting( HostName, Key, Default ); + } + + bool PutSetting( const QString &HostName, + const QString &Key, + const QString &Value ) + { + return m_obj.PutSetting( HostName, Key, Value ); + } }; + +Q_SCRIPT_DECLARE_QMETAOBJECT( ScriptableMyth, QObject*); + + + #endif diff --git a/mythtv/programs/mythfrontend/main.cpp b/mythtv/programs/mythfrontend/main.cpp index 88ae219abc2..4953d10a542 100644 --- a/mythtv/programs/mythfrontend/main.cpp +++ b/mythtv/programs/mythfrontend/main.cpp @@ -170,8 +170,10 @@ namespace delete gContext; gContext = NULL; +#ifndef _MSC_VER signal(SIGHUP, SIG_DFL); signal(SIGUSR1, SIG_DFL); +#endif } class CleanupGuard @@ -1473,11 +1475,12 @@ int main(int argc, char **argv) return GENERIC_EXIT_NO_THEME; } +#ifndef _MSC_VER // Setup handler for USR1 signals to reload theme signal(SIGUSR1, &signal_USR1_handler); // Setup handler for USR2 signals to restart LIRC signal(SIGUSR2, &signal_USR2_handler); - +#endif ThemeUpdateChecker *themeUpdateChecker = NULL; if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1)) themeUpdateChecker = new ThemeUpdateChecker(); diff --git a/mythtv/programs/mythfrontend/mythfrontend.pro b/mythtv/programs/mythfrontend/mythfrontend.pro index 9df4684f778..a2e4450d849 100644 --- a/mythtv/programs/mythfrontend/mythfrontend.pro +++ b/mythtv/programs/mythfrontend/mythfrontend.pro @@ -2,7 +2,7 @@ include ( ../../settings.pro ) include ( ../../version.pro ) include ( ../programs-libs.pro ) -QT += network xml sql webkit +QT += network xml sql webkit script TEMPLATE = app CONFIG += thread