MythTV master
mythhttpservice.cpp
Go to the documentation of this file.
1#include <QFileInfo>
2
3// MythTV
4#include "mythlogging.h"
5#include "mythdate.h"
6#include "http/mythwsdl.h"
15
16#define LOC QString("HTTPService: ")
17
19 : m_name(MetaService->m_name),
20 m_staticMetaService(MetaService)
21{
22}
23
30{
31 QString& method = Request->m_fileName;
32 if (method.isEmpty())
33 return nullptr;
35 // WSDL
36 if (method == "wsdl") {
38 return wsdl.GetWSDL( Request );
39 }
40 if ( method == "xsd" )
41 {
42 MythXSD xsd;
43 if (Request->m_queries.contains( "type" ))
44 return xsd.GetXSD( Request, Request->m_queries.value("type"));
45 // The xsd for enums does not work, so it is commented for now.
46 // else
47 // return xsd.GetEnumXSD( Request, Request->m_queries.value("enum"));
48 }
49 if ( method == "version" )
50 {
51 int nClassIdx = m_staticMetaService->m_meta.indexOfClassInfo( "Version" );
52 if (nClassIdx >=0)
53 {
54 QString sVersion =
55 m_staticMetaService->m_meta.classInfo(nClassIdx).value();
56 auto accept = MythHTTPEncoding::GetMimeTypes(MythHTTP::GetHeader(Request->m_headers, "accept"));
57 HTTPData content = MythSerialiser::Serialise("String", sVersion, accept);
58 content->m_cacheType = HTTPETag | HTTPShortLife;
60 return result;
61 }
62 }
63 // Find the method
64 LOG(VB_HTTP, LOG_DEBUG, LOC + QString("Looking for method '%1'").arg(method));
65 HTTPMethodPtr handler = nullptr;
66 // cppcheck-suppress unassignedVariable
67 for (auto & [path, handle] : m_staticMetaService->m_slots)
68 if (path == method) { handler = handle; break; }
69
70 if (handler == nullptr)
71 {
72 // Should we just return not found here rather than falling through
73 // to all of the other handlers? Do we need other handlers?
74 LOG(VB_HTTP, LOG_DEBUG, LOC + "Failed to find method");
75 return nullptr;
76 }
77
78 // Authentication required per method is not implemented
79 if (handler->m_protected)
80 {
81 LOG(VB_HTTP, LOG_INFO, LOC + "Authentication required for this call");
82 }
83
84 // Ensure that a valid login has been done if "authentication required" is enabled
85 // Myth/LoginUser is exempt from this requirement
86 QString authReqOption = gCoreContext->GetSetting("APIAuthReqd","NONE");
87 bool authReq = false;
88 if (authReqOption == "REMOTE") {
89 if (!gCoreContext->IsLocalSubnet(Request->m_peerAddress, false))
90 authReq = true;
91 }
92 else if (authReqOption == "ALL") {
93 authReq = true;
94 }
95 QString authorization = MythHTTP::GetHeader(Request->m_headers, "authorization").trimmed();
96 if (authorization.isEmpty())
97 authorization = Request->m_queries.value("authorization",{});
99 // methods /Myth/LoginUser and /Myth/GetConnectionInfo do not require authentication
100 if ( ! (Request->m_path == "/Myth/"
101 && (method == "LoginUser" || method == "GetConnectionInfo")) )
102 {
103 if (!authorization.isEmpty() || authReq)
104 {
105 if (!sessionManager->IsValidSession(authorization))
106 {
107 QString error(" Invalid authorization token");
108 LOG(VB_HTTP, LOG_ERR, LOC + error);
109 Request->m_status = HTTPUnauthorized;
111 }
112 }
113 }
114
115 // Sanity check type count (handler should have the return type at least)
116 if (handler->m_types.empty())
117 return nullptr;
118
119 // Handle options
120 Request->m_allowed = handler->m_requestTypes;
122 return options;
123
124 // Parse the parameters and match against those expected by the method.
125 // As for the old code, this allows parameters to be missing and they will
126 // thus be allocated a default/null/value.
127 size_t typecount = std::min(handler->m_types.size(), static_cast<size_t>(100));
128
129 // Build parameters list
130 // Note: We allow up to 100 args but anything above Q_METAMETHOD_INVOKE_MAX_ARGS
131 // will be ignored
132 std::array<void*, 100> param { nullptr};
133 std::array<int, 100> types { 0 };
134
135 // Return type
136#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
137 param[0] = handler->m_types[0] == 0 ? nullptr : QMetaType::create(handler->m_types[0]);
138#else
139 param[0] = handler->m_types[0] == 0 ? nullptr : QMetaType(handler->m_types[0]).create();
140#endif
141 types[0] = handler->m_types[0];
142
143 // Parameters
144 // Iterate over the method's parameters and search for the incoming values...
145 size_t count = 1;
146 QString error;
147 while (count < typecount)
148 {
149 auto name = handler->m_names[count];
150 auto value = Request->m_queries.value(name.toLower(), "");
151 auto type = handler->m_types[count];
152 types[count] = type;
153 // These should be filtered out in MythHTTPMetaMethod
154 if (type == 0)
155 {
156 error = QString("Unknown parameter type '%1'").arg(name);
157 break;
158 }
159
160#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
161 auto * newparam = QMetaType::create(type);
162#else
163 auto * newparam = QMetaType(type).create();
164#endif
165 param[count] = MythHTTPMetaMethod::CreateParameter(newparam, type, value);
166 ++count;
167 }
168
169 HTTPResponse result = nullptr;
170 if (count == typecount)
171 {
172 // Invoke
173 QVariant returnvalue;
174 try {
175 if (qt_metacall(QMetaObject::InvokeMetaMethod, handler->m_index, param.data()) >= 0)
176 LOG(VB_GENERAL, LOG_ERR, "qt_metacall error");
177 else
178 {
179 // Retrieve result
180 returnvalue = MythHTTPMetaMethod::CreateReturnValue(types[0], param[0]);
181 }
182 }
183 catch( QString &msg ) {
184 LOG(VB_GENERAL, LOG_ERR, "Service Exception: " + msg);
185 if (msg.startsWith("Forbidden:"))
186 Request->m_status = HTTPForbidden;
187 else
188 Request->m_status = HTTPBadRequest;
190 }
191 catch (V2HttpRedirectException &ex) {
193 }
194
195 if (!returnvalue.isValid())
196 {
197 if (!result)
198 result = MythHTTPResponse::ErrorResponse(Request, "Unknown Failure");
199 }
200 else if (returnvalue.canConvert<QFileInfo>())
201 {
202 if (!result)
203 {
204 auto info = returnvalue.value<QFileInfo>();
205 QString file = info.absoluteFilePath();
206 if (file.size() == 0)
207 {
208 LOG(VB_HTTP, LOG_WARNING, LOC + QString("Invalid request for unknown file"));
209 Request->m_status = HTTPNotFound;
211 }
212 else
213 {
214 HTTPFile httpfile = MythHTTPFile::Create(info.fileName(),file);
215 if (!httpfile->open(QIODevice::ReadOnly))
216 {
217 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Failed to open '%1'").arg(file));
218 Request->m_status = HTTPNotFound;
220 }
221 else
222 {
223 httpfile->m_lastModified = info.lastModified();
224 httpfile->m_cacheType = HTTPLastModified | HTTPLongLife;
225 LOG(VB_HTTP, LOG_DEBUG, LOC + QString("Last modified: %2")
226 .arg(MythDate::toString(httpfile->m_lastModified, MythDate::kOverrideUTC | MythDate::kRFC822)));
227 // Create our response
228 result = MythHTTPResponse::FileResponse(Request, httpfile);
229 }
230 }
231 }
232 }
233 else
234 {
235 auto accept = MythHTTPEncoding::GetMimeTypes(MythHTTP::GetHeader(Request->m_headers, "accept"));
236 HTTPData content = MythSerialiser::Serialise(handler->m_returnTypeName, returnvalue, accept);
237 content->m_cacheType = HTTPETag | HTTPShortLife;
239
240 // If the return type is QObject* we need to cleanup
241 if (returnvalue.canConvert<QObject*>())
242 {
243 LOG(VB_HTTP, LOG_DEBUG, LOC + "Deleting object");
244 auto * object = returnvalue.value<QObject*>();
245 delete object;
246 }
247 }
248 }
249
250 // Cleanup
251 for (size_t i = 0; i < typecount; ++i)
252 {
253 if ((param[i] != nullptr) && (types[i] != 0))
254 {
255#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
256 QMetaType::destroy(types[i], param[i]);
257#else
258 QMetaType(types[i]).destroy(param[i]);
259#endif
260 }
261 }
262
263 // Return the previous error
264 if (count != typecount)
265 {
266 LOG(VB_HTTP, LOG_ERR, LOC + error);
267 Request->m_status = HTTPBadRequest;
269 }
270
271 // Valid result...
272 return result;
273}
274
275
277{
278 return m_name;
279}
bool IsLocalSubnet(const QHostAddress &peer, bool log)
Check if peer is on local subnet.
MythSessionManager * GetSessionManager(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static QStringList GetMimeTypes(const QString &Accept)
static HTTPFile Create(const QString &ShortName, const QString &FullName)
static void * CreateParameter(void *Parameter, int Type, const QString &Value)
Populate the QMetaType object referenced by Parameter with Value.
static QVariant CreateReturnValue(int Type, void *Value)
const QMetaObject & m_meta
static HTTPResponse RedirectionResponse(const HTTPRequest2 &Request, const QString &Redirect)
static HTTPResponse FileResponse(const HTTPRequest2 &Request, const HTTPFile &File)
static HTTPResponse HandleOptions(const HTTPRequest2 &Request)
static HTTPResponse ErrorResponse(MythHTTPStatus Status, const QString &ServerName)
static HTTPResponse DataResponse(const HTTPRequest2 &Request, const HTTPData &Data)
virtual HTTPResponse HTTPRequest(const HTTPRequest2 &Request)
Respond to a valid HTTPRequest.
MythHTTPMetaService * m_staticMetaService
MythHTTPService(MythHTTPMetaService *MetaService)
HTTPRequest2 m_request
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
static HTTPData Serialise(const QString &Name, const QVariant &Value, const QStringList &Accept)
Serialise the given data with an encoding suggested by Accept.
We use digest authentication because it protects the password over unprotected networks.
Definition: mythsession.h:106
bool IsValidSession(const QString &sessionToken)
Check if the session token is valid.
HTTPResponse GetWSDL(const HTTPRequest2 &Request)
Definition: mythwsdl.cpp:28
HTTPResponse GetXSD(const HTTPRequest2 &pRequest, QString sTypeName)
Definition: mythxsd.cpp:209
static const struct wl_interface * types[]
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
std::shared_ptr< MythHTTPMetaMethod > HTTPMethodPtr
#define LOC
@ HTTPBadRequest
@ HTTPUnauthorized
@ HTTPForbidden
@ HTTPNotFound
std::shared_ptr< MythHTTPFile > HTTPFile
Definition: mythhttptypes.h:41
std::shared_ptr< MythHTTPRequest > HTTPRequest2
Definition: mythhttptypes.h:39
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:40
std::shared_ptr< MythHTTPData > HTTPData
Definition: mythhttptypes.h:37
@ HTTPETag
@ HTTPLastModified
@ HTTPLongLife
@ HTTPShortLife
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kOverrideUTC
Present date/time in UTC.
Definition: mythdate.h:31
@ kRFC822
HTTP Date format.
Definition: mythdate.h:30
dictionary info
Definition: azlyrics.py:7
def error(message)
Definition: smolt.py:409