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
85 {
86 // Ensure that a valid login has been done if "authentication required" is enabled
87 // Myth/LoginUser is exempt from this requirement
88 QString authReqOption = gCoreContext->GetSetting("APIAuthReqd","NONE");
89 bool authReq = false;
90 if (authReqOption == "REMOTE")
91 {
92 if (!gCoreContext->IsLocalSubnet(Request->m_peerAddress, false))
93 authReq = true;
94 }
95 else if (authReqOption == "ALL")
96 {
97 authReq = true;
98 }
99 QString authorization = MythHTTP::GetHeader(Request->m_headers, "authorization").trimmed();
100 if (authorization.isEmpty())
101 authorization = Request->m_queries.value("authorization",{});
103 // methods /Myth/LoginUser and /Myth/GetConnectionInfo do not require authentication
104 if ( ! (Request->m_path == "/Myth/"
105 && (method == "LoginUser" || method == "GetConnectionInfo")) )
106 {
107 if (!authorization.isEmpty() || authReq)
108 {
109 if (!sessionManager->IsValidSession(authorization))
110 {
111 QString error(" Invalid authorization token");
112 LOG(VB_HTTP, LOG_ERR, LOC + error);
113 Request->m_status = HTTPUnauthorized;
115 }
116 }
117 }
118 }
119 // Sanity check type count (handler should have the return type at least)
120 if (handler->m_types.empty())
121 return nullptr;
122
123 // Handle options
124 Request->m_allowed = handler->m_requestTypes;
126 return options;
127
128 // Parse the parameters and match against those expected by the method.
129 // As for the old code, this allows parameters to be missing and they will
130 // thus be allocated a default/null/value.
131 size_t typecount = std::min(handler->m_types.size(), static_cast<size_t>(100));
132
133 // Build parameters list
134 // Note: We allow up to 100 args but anything above Q_METAMETHOD_INVOKE_MAX_ARGS
135 // will be ignored
136 std::array<void*, 100> param { nullptr};
137 std::array<int, 100> types { 0 };
138
139 // Return type
140#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
141 param[0] = handler->m_types[0] == 0 ? nullptr : QMetaType::create(handler->m_types[0]);
142#else
143 param[0] = handler->m_types[0] == 0 ? nullptr : QMetaType(handler->m_types[0]).create();
144#endif
145 types[0] = handler->m_types[0];
146
147 // Parameters
148 // Iterate over the method's parameters and search for the incoming values...
149 size_t count = 1;
150 QString error;
151 while (count < typecount)
152 {
153 auto name = handler->m_names[count];
154 auto value = Request->m_queries.value(name.toLower(), "");
155 auto type = handler->m_types[count];
156 types[count] = type;
157 // These should be filtered out in MythHTTPMetaMethod
158 if (type == 0)
159 {
160 error = QString("Unknown parameter type '%1'").arg(name);
161 break;
162 }
163
164#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
165 auto * newparam = QMetaType::create(type);
166#else
167 auto * newparam = QMetaType(type).create();
168#endif
169 param[count] = MythHTTPMetaMethod::CreateParameter(newparam, type, value);
170 ++count;
171 }
172
173 HTTPResponse result = nullptr;
174 if (count == typecount)
175 {
176 // Invoke
177 QVariant returnvalue;
178 try {
179 if (qt_metacall(QMetaObject::InvokeMetaMethod, handler->m_index, param.data()) >= 0)
180 LOG(VB_GENERAL, LOG_ERR, "qt_metacall error");
181 else
182 {
183 // Retrieve result
184 returnvalue = MythHTTPMetaMethod::CreateReturnValue(types[0], param[0]);
185 }
186 }
187 catch( QString &msg ) {
188 LOG(VB_GENERAL, LOG_ERR, "Service Exception: " + msg);
189 if (msg.startsWith("Forbidden:"))
190 Request->m_status = HTTPForbidden;
191 else
192 Request->m_status = HTTPBadRequest;
194 }
195 catch (V2HttpRedirectException &ex) {
197 }
198
199 if (!returnvalue.isValid())
200 {
201 if (!result)
202 result = MythHTTPResponse::ErrorResponse(Request, "Unknown Failure");
203 }
204 else if (returnvalue.canConvert<QFileInfo>())
205 {
206 if (!result)
207 {
208 auto info = returnvalue.value<QFileInfo>();
209 QString file = info.absoluteFilePath();
210 if (file.size() == 0)
211 {
212 LOG(VB_HTTP, LOG_WARNING, LOC + QString("Invalid request for unknown file"));
213 Request->m_status = HTTPNotFound;
215 }
216 else
217 {
218 HTTPFile httpfile = MythHTTPFile::Create(info.fileName(),file);
219 if (!httpfile->open(QIODevice::ReadOnly))
220 {
221 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Failed to open '%1'").arg(file));
222 Request->m_status = HTTPNotFound;
224 }
225 else
226 {
227 httpfile->m_lastModified = info.lastModified();
228 httpfile->m_cacheType = HTTPLastModified | HTTPLongLife;
229 LOG(VB_HTTP, LOG_DEBUG, LOC + QString("Last modified: %2")
230 .arg(MythDate::toString(httpfile->m_lastModified, MythDate::kOverrideUTC | MythDate::kRFC822)));
231 // Create our response
232 result = MythHTTPResponse::FileResponse(Request, httpfile);
233 }
234 }
235 }
236 }
237 else
238 {
239 auto accept = MythHTTPEncoding::GetMimeTypes(MythHTTP::GetHeader(Request->m_headers, "accept"));
240 HTTPData content = MythSerialiser::Serialise(handler->m_returnTypeName, returnvalue, accept);
241 content->m_cacheType = HTTPETag | HTTPShortLife;
243
244 // If the return type is QObject* we need to cleanup
245 if (returnvalue.canConvert<QObject*>())
246 {
247 LOG(VB_HTTP, LOG_DEBUG, LOC + "Deleting object");
248 auto * object = returnvalue.value<QObject*>();
249 delete object;
250 }
251 }
252 }
253
254 // Cleanup
255 for (size_t i = 0; i < typecount; ++i)
256 {
257 if ((param[i] != nullptr) && (types[i] != 0))
258 {
259#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
260 QMetaType::destroy(types[i], param[i]);
261#else
262 QMetaType(types[i]).destroy(param[i]);
263#endif
264 }
265 }
266
267 // Return the previous error
268 if (count != typecount)
269 {
270 LOG(VB_HTTP, LOG_ERR, LOC + error);
271 Request->m_status = HTTPBadRequest;
273 }
274
275 // Valid result...
276 return result;
277}
278
279
281{
282 return m_name;
283}
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="")
bool IsBackend(void) const
is this process a backend process
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:214
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