26 #include <QMetaMethod> 44 MethodParameters(
int Index, QMetaMethod Method,
int AllowedRequestTypes,
const QString &ReturnType)
55 static QList<int> unsupportedtypes;
56 static QList<int> unsupportedparameters;
57 static bool initialised =
false;
63 unsupportedtypes << QMetaType::UnknownType;
64 unsupportedtypes << QMetaType::VoidStar << QMetaType::QObjectStar << QMetaType::QVariantHash;
65 unsupportedtypes << QMetaType::QRect << QMetaType::QRectF << QMetaType::QSize << QMetaType::QSizeF << QMetaType::QLine << QMetaType::QLineF << QMetaType::QPoint << QMetaType::QPointF;
67 unsupportedparameters << unsupportedtypes;
68 unsupportedparameters << QMetaType::QVariantMap << QMetaType::QStringList << QMetaType::QVariantList;
72 int returntype = QMetaType::type(Method.typeName());
75 if (unsupportedtypes.contains(returntype))
77 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Method '%1' has unsupported return type '%2'").arg(Method.name().constData(), Method.typeName()));
82 if (Method.parameterCount() > 10)
84 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Method '%1' takes more than 10 parameters - ignoring").arg(Method.name().constData()));
88 m_types.append(returntype > 0 ? returntype : 0);
91 QList<QByteArray> names = Method.parameterNames();
92 QList<QByteArray> types = Method.parameterTypes();
95 for (
int i = 0; i < names.size(); ++i)
97 int type = QMetaType::type(types[i]);
100 if (unsupportedparameters.contains(type))
102 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Method '%1' has unsupported parameter type '%2'")
103 .arg(Method.name().constData(), types[i].constData()));
120 QVariant
Invoke(QObject *Object,
const QMap<QString,QString> &Queries, QString &ReturnType,
bool &VoidResult)
124 void* parameters[11];
125 memset(parameters, 0, 11 *
sizeof(
void*));
126 int size = qMin(11,
m_types.size());
129 if (Queries.size() != size - 1)
133 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Method '%1' expects %2 parameters, sent %3")
134 .arg(
m_names.value(0).constData()).arg(size - 1).arg(Queries.size()));
140 QMap<QString,QString>::const_iterator it;
141 for (
int i = 0; i < size; ++i)
143 parameters[i] = QMetaType::create(
m_types.value(i));
146 it = Queries.constFind(
m_names.value(i));
147 if (it == Queries.end())
149 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Parameter '%1' for method '%2' is missing")
150 .arg(
m_names.value(i).constData(),
m_names.value(0).constData()));
157 if (Object->qt_metacall(QMetaObject::InvokeMetaMethod,
m_index, parameters) > -1)
158 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"qt_metacall error"));
162 if (type == QMetaType::QVariant)
164 int newtype =
static_cast<int>(
reinterpret_cast<QVariant*
>(parameters[0])->type());
171 VoidResult = type == QMetaType::Void;
172 QVariant result = type == QMetaType::Void ? QVariant() : QVariant(type, parameters[0]);
175 for (
int i = 0; i < size; ++i)
177 QMetaType::destroy(
m_types.at(i), parameters[i]);
183 static void SetValue(
void* Pointer,
const QString &Value,
int Type)
190 case QMetaType::QVariant: *((QVariant*)Pointer) = QVariant(Value);
return;
191 case QMetaType::Char: *((
char*)Pointer) = Value.isEmpty() ? 0 : Value.at(0).toLatin1();
return;
192 case QMetaType::UChar: *((
unsigned char*)Pointer) = Value.isEmpty() ? 0 :Value.at(0).toLatin1();
return;
193 case QMetaType::QChar: *((QChar*)Pointer) = Value.isEmpty() ? 0 : Value.at(0);
return;
194 case QMetaType::Bool: *((
bool*)Pointer) =
ToBool(Value);
return;
195 case QMetaType::Short: *((
short*)Pointer) = Value.toShort();
return;
196 case QMetaType::UShort: *((ushort*)Pointer) = Value.toUShort();
return;
197 case QMetaType::Int: *((
int*)Pointer) = Value.toInt();
return;
198 case QMetaType::UInt: *((uint*)Pointer) = Value.toUInt();
return;
199 case QMetaType::Long: *((
long*)Pointer) = Value.toLong();
return;
200 case QMetaType::ULong: *((ulong*)Pointer) = Value.toULong();
return;
201 case QMetaType::LongLong: *((qlonglong*)Pointer) = Value.toLongLong();
return;
202 case QMetaType::ULongLong: *((qulonglong*)Pointer) = Value.toULongLong();
return;
203 case QMetaType::Double: *((
double*)Pointer) = Value.toDouble();
return;
204 case QMetaType::Float: *((
float*)Pointer) = Value.toFloat();
return;
205 case QMetaType::QString: *((QString*)Pointer) = Value;
return;
206 case QMetaType::QByteArray: *((QByteArray*)Pointer) = Value.toUtf8();
return;
207 case QMetaType::QTime: *((QTime*)Pointer) = QTime::fromString(Value, Qt::ISODate);
return;
208 case QMetaType::QDate: *((QDate*)Pointer) = QDate::fromString(Value, Qt::ISODate);
return;
209 case QMetaType::QDateTime:
211 QDateTime dt = QDateTime::fromString(Value, Qt::ISODate);
212 dt.setTimeSpec(Qt::UTC);
213 *((QDateTime*)Pointer) = dt;
222 if (Value.compare(QStringLiteral(
"1"), Qt::CaseInsensitive) == 0)
224 if (Value.compare(QStringLiteral(
"true"), Qt::CaseInsensitive) == 0)
226 if (Value.compare(QStringLiteral(
"y"), Qt::CaseInsensitive) == 0)
228 if (Value.compare(QStringLiteral(
"yes"), Qt::CaseInsensitive) == 0)
263 const QMetaObject &MetaObject,
const QString &Blacklist)
265 m_httpServiceLock(QReadWriteLock::Recursive),
267 m_version(QStringLiteral(
"Unknown")),
271 m_subscriberLock(QMutex::Recursive)
273 static const QString defaultblacklisted(QStringLiteral(
"deleteLater,SubscriberDeleted,"));
274 QStringList blacklist = (defaultblacklisted + Blacklist).split(
',');
276 m_parent->setObjectName(Name);
279 if (MetaObject.indexOfSlot(QMetaObject::normalizedSignature(
"SubscriberDeleted(QObject*)")) < 0)
281 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Service '%1' has no SubscriberDeleted slot. This is a programmer error - exiting").arg(Name));
287 int index = MetaObject.indexOfClassInfo(
"Version");
289 m_version = MetaObject.classInfo(index).value();
291 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Service '%1' is missing version information").arg(Name));
294 bool secure = MetaObject.indexOfClassInfo(
"Secure") > -1;
297 QList<const QMetaObject*> metas;
298 metas.append(&MetaObject);
299 const QMetaObject* super = MetaObject.superClass();
303 super = super->superClass();
308 QListIterator<const QMetaObject*> it(metas);
310 while (it.hasPrevious())
312 const QMetaObject *meta = it.previous();
314 for (
int i = 0; i < meta->methodCount(); ++i)
316 QMetaMethod method = meta->method(i);
318 if ((method.methodType() == QMetaMethod::Slot) &&
319 (method.access() == QMetaMethod::Public))
321 QString name(method.methodSignature());
322 name = name.section(
'(', 0, 0);
325 if (blacklist.contains(name))
336 int index = MetaObject.indexOfClassInfo(name.toLatin1());
339 QStringList infos = QString(MetaObject.classInfo(index).value()).split(
',', QString::SkipEmptyParts);
340 foreach (
const QString &info, infos)
342 if (info.startsWith(QStringLiteral(
"methods=")))
344 else if (info.startsWith(QStringLiteral(
"type=")))
345 returntype = info.mid(5);
355 allowed |= customallowed;
357 else if (name.startsWith(QStringLiteral(
"Get"), Qt::CaseInsensitive))
361 else if (name.startsWith(QStringLiteral(
"Set"), Qt::CaseInsensitive))
368 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Unable to determine request types of method '%1' for '%2' - ignoring").arg(name,
m_name));
373 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"%1 requires authentication").arg(Signature + name));
381 if (m_methods.contains(name))
384 if (existing->
m_method.methodSignature() == method.methodSignature() &&
385 existing->
m_method.returnType() == method.returnType())
387 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Method '%1' in class '%2' already found in superclass - overriding")
388 .arg(method.methodSignature().constData(), meta->className()));
389 existing = m_methods.take(name);
394 m_methods.insert(name, parameters);
405 int invalidindex = -1;
406 foreach (
const QMetaObject* meta, metas)
408 for (
int i = meta->propertyOffset(); i < meta->propertyCount(); ++i)
410 QMetaProperty
property = meta->property(i);
411 QString propertyname(property.name());
413 if (propertyname != QStringLiteral(
"objectName") &&
property.isReadable() && ((
property.hasNotifySignal() &&
property.notifySignalIndex() > -1) || property.isConstant()))
416 if (property.notifySignalIndex() > -1)
418 m_properties.insert(property.notifySignalIndex(),
property.propertyIndex());
419 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Adding property '%1' with signal index %2").arg(property.name()).arg(property.notifySignalIndex()));
423 m_properties.insert(invalidindex--, property.propertyIndex());
424 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Adding constant property '%1'").arg(property.name()));
433 qDeleteAll(m_methods);
456 if (method.compare(QStringLiteral(
"GetServiceVersion")) == 0)
478 QMap<QString,MethodParameters*>::const_iterator it = m_methods.constFind(method);
479 if (it != m_methods.constEnd())
482 if ((!(type & (*it)->m_allowedRequestTypes)) ||
504 QVariant result = (*it)->Invoke(m_parent, Request.
Queries(), type, voidresult);
510 if (result.type() == QVariant::Invalid)
532 int index = Method.lastIndexOf(
'/');
534 method = Method.mid(index + 1).trimmed();
536 if (Connection && !method.isEmpty())
539 QMap<QString,MethodParameters*>::const_iterator it = m_methods.constFind(method);
540 if (it != m_methods.constEnd())
543 int types = it.value()->m_allowedRequestTypes;
547 if (disabled || unauthorised)
550 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"'%1' method '%2' is disabled").arg(
m_signature, method));
552 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"'%1' method '%2' unauthorised").arg(
m_signature, method));
555 error.insert(QStringLiteral(
"code"), -401);
556 error.insert(QStringLiteral(
"message"), QStringLiteral(
"Method not authorised"));
557 result.insert(QStringLiteral(
"error"), error);
565 QMap<QString,QString> params;
566 if (Parameters.type() == QVariant::Map)
568 QVariantMap map = Parameters.toMap();
569 QVariantMap::iterator it = map.begin();
570 for ( ; it != map.end(); ++it)
571 params.insert(it.key(), it.value().toString());
573 else if (Parameters.type() == QVariant::List)
575 QVariantList list = Parameters.toList();
576 if (list.size() <= (*it)->m_names.size())
578 for (
int i = 0; i < list.size(); ++i)
579 params.insert((*it)->m_names[i], list[i].toString());
583 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Too many parameters"));
586 else if (!Parameters.isNull())
588 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Unknown parameter variant"));
592 bool voidresult =
false;
593 QVariant results = (*it)->Invoke(m_parent, params, type, voidresult);
599 if (results.type() != QVariant::Invalid)
602 result.insert(QStringLiteral(
"result"), results);
608 error.insert(QStringLiteral(
"code"), -32602);
609 error.insert(QStringLiteral(
"message"), QStringLiteral(
"Invalid params"));
610 result.insert(QStringLiteral(
"error"), error);
616 result.insert(QStringLiteral(
"result"), QStringLiteral(
"null"));
622 if (method.compare(QStringLiteral(
"GetServiceVersion")) == 0)
627 result.insert(QStringLiteral(
"result"), version);
631 else if (method.compare(QStringLiteral(
"Subscribe")) == 0)
634 int change = Connection->metaObject()->indexOfSlot(QMetaObject::normalizedSignature(
"PropertyChanged()"));
638 QMutexLocker locker(&m_subscriberLock);
640 if (!m_subscribers.contains(Connection))
642 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"New subscription for '%1'").arg(
m_signature));
643 m_subscribers.append(Connection);
646 QMap<int,int>::const_iterator it = m_properties.constBegin();
647 for ( ; it != m_properties.constEnd(); ++it)
652 QObject::connect(m_parent, m_parent->metaObject()->method(it.key()), Connection, Connection->metaObject()->method(change));
655 QObject::connect(Connection, SIGNAL(destroyed(QObject*)), m_parent, SLOT(SubscriberDeleted(QObject*)));
664 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Connection is already subscribed to '%1'").arg(
m_signature));
669 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Subscription request for connection without correct methods"));
674 error.insert(QStringLiteral(
"code"), -32603);
675 error.insert(QStringLiteral(
"message"),
"Internal error");
676 result.insert(QStringLiteral(
"error"), error);
680 else if (method.compare(QStringLiteral(
"Unsubscribe")) == 0)
682 QMutexLocker locker(&m_subscriberLock);
684 if (m_subscribers.contains(Connection))
686 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Removed subscription for '%1'").arg(
m_signature));
689 m_parent->disconnect(Connection);
692 m_subscribers.removeAll(Connection);
696 result.insert(QStringLiteral(
"result"), 1);
700 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Connection is not subscribed to '%1'").arg(
m_signature));
704 error.insert(QStringLiteral(
"code"), -32603);
705 error.insert(QStringLiteral(
"message"),
"Internal error");
706 result.insert(QStringLiteral(
"error"), error);
711 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"'%1' service has no '%2' method").arg(
m_signature, method));
715 error.insert(QStringLiteral(
"code"), -32601);
716 error.insert(QStringLiteral(
"message"), QStringLiteral(
"Method not found"));
717 result.insert(QStringLiteral(
"error"), error);
734 QVariantMap properties;
737 QMap<int,int>::const_iterator it = m_properties.constBegin();
738 for ( ; it != m_properties.constEnd(); ++it)
742 QMetaProperty
property = m_parent->metaObject()->property(it.value());
743 QVariantMap description;
744 QString name = QString::fromLatin1(property.name());
748 description.insert(QStringLiteral(
"notification"), QString::fromLatin1(m_parent->metaObject()->method(it.key()).name()));
751 QString camelname = name.at(0).toUpper() + name.mid(1);
752 QString read = QString(QStringLiteral(
"Get")) + camelname;
754 if (m_parent->metaObject()->indexOfSlot(QMetaObject::normalizedSignature(QString(read +
"()").toLatin1())) > -1)
755 description.insert(QStringLiteral(
"read"), read);
757 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to deduce 'read' slot for property '%1' in service '%2'").arg(name,
m_signature));
760 if (property.isWritable())
762 QString write = QString(QStringLiteral(
"Set")) + camelname;
763 if (m_parent->metaObject()->indexOfSlot(QMetaObject::normalizedSignature(QStringLiteral(
"%1(%2)").arg(write, property.typeName()).toLatin1())) > -1)
764 description.insert(QStringLiteral(
"write"), write);
766 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to deduce 'write' slot for property '%1' in service '%2'").arg(name,
m_signature));
770 description.insert(QStringLiteral(
"value"), property.read(m_parent));
772 properties.insert(name, description);
776 QMap<QString,MethodParameters*>::const_iterator it2 = m_methods.constBegin();
777 for ( ; it2 != m_methods.constEnd(); ++it2)
782 for (
int i = 1; i < parameters->
m_types.size(); ++i)
783 params.append(parameters->
m_names.value(i).constData());
784 map.insert(QStringLiteral(
"params"), params);
786 methods.insert(it2.key(), map);
795 QVariant returns(
"object");
797 QVariantMap subscribe;
798 subscribe.insert(QStringLiteral(
"params"), params);
799 subscribe.insert(QStringLiteral(
"returns"), returns);
800 methods.insert(QStringLiteral(
"Subscribe"), subscribe);
802 QVariantMap unsubscribe;
803 unsubscribe.insert(QStringLiteral(
"params"), params);
804 unsubscribe.insert(QStringLiteral(
"returns"), returns);
805 methods.insert(QStringLiteral(
"Unsubscribe"), unsubscribe);
807 QVariantMap serviceversion;
808 serviceversion.insert(QStringLiteral(
"params"), params);
810 methods.insert(QStringLiteral(
"GetServiceVersion"), serviceversion);
813 QVariantMap description;
814 description.insert(QStringLiteral(
"read"), QStringLiteral(
"GetServiceVersion"));
815 description.insert(QStringLiteral(
"value"), m_version);
816 properties.insert(QStringLiteral(
"serviceVersion"), description);
818 details.insert(QStringLiteral(
"properties"), properties);
819 details.insert(QStringLiteral(
"methods"), methods);
825 if (Index > -1 && Index < m_parent->metaObject()->methodCount())
826 return QString::fromLatin1(m_parent->metaObject()->method(Index).name());
839 if (Index > -1 && m_properties.contains(Index))
840 result = m_parent->metaObject()->property(m_properties.value(Index)).read(m_parent);
842 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to retrieve property"));
849 QMutexLocker locker(&m_subscriberLock);
851 if (Subscriber && m_subscribers.contains(Subscriber))
853 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Subscriber deleted - cancelling subscription"));
854 m_subscribers.removeAll(Subscriber);
void SetAllowed(int Allowed)
A class to encapsulate an incoming HTTP request.
static void SetValue(void *Pointer, const QString &Value, int Type)
HTTPRequestType GetHTTPRequestType(void) const
Base HTTP response handler class.
virtual ~TorcHTTPService()
QVector< QByteArray > m_names
#define TORC_SERVICES_DIR
MethodParameters(int Index, QMetaMethod Method, int AllowedRequestTypes, const QString &ReturnType)
void Enable(void)
Enable this method.
void ProcessHTTPRequest(const QString &PeerAddress, int PeerPort, const QString &LocalAddress, int LocalPort, TorcHTTPRequest &Request) override
#define TORC_EXIT_UNKOWN_ERROR
virtual QString GetUIName(void)
void Serialise(const QVariant &Data, const QString &Type)
static void HandleOptions(TorcHTTPRequest &Request, int Allowed)
QString GetMethod(void) const
virtual QString GetPresentationURL(void)
~MethodParameters()=default
void SetAllowGZip(bool Allowed)
Allow gzip compression for the contents of this request.
void Disable(void)
Disable this method.
void HandleSubscriberDeleted(QObject *Subscriber)
#define TORC_SERVICE_VERSION
static bool ToBool(const QString &Value)
QVariantMap ProcessRequest(const QString &Method, const QVariant &Parameters, QObject *Connection, bool Authenticated) override
static int StringToAllowed(const QString &Allowed)
void SetStatus(HTTPStatus Status)
#define LOG(_MASK_, _LEVEL_, _STRING_)
QVariant GetProperty(int Index)
Get the value of a given property.
static bool MethodIsAuthorised(TorcHTTPRequest &Request, int Allowed)
Check the current request is authorised and set the authentication header if not. ...
QString GetMethod(int Index)
int m_allowedRequestTypes
void SetResponseType(HTTPResponseType Type)
QVariantMap GetServiceDetails(void)
Return a QVariantMap describing the services methods and properties.
static QString QMetaTypetoJavascriptType(int MetaType)
QVariant Invoke(QObject *Object, const QMap< QString, QString > &Queries, QString &ReturnType, bool &VoidResult)
Call the stored method with the arguments passed in via Queries.
TorcHTTPService(QObject *Parent, const QString &Signature, const QString &Name, const QMetaObject &MetaObject, const QString &Blacklist=QStringLiteral(""))
const QMap< QString, QString > & Queries(void) const