1 /******************************************************************************
2  * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18 
19 #pragma once
20 
21 #include "eventcontent.h"
22 #include "roomevent.h"
23 
24 class QFileInfo;
25 
26 namespace Quotient {
27 namespace MessageEventContent = EventContent; // Back-compatibility
28 
29 /**
30  * The event class corresponding to m.room.message events
31  */
32 class RoomMessageEvent : public RoomEvent {
33     Q_GADGET
34     Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT)
35     Q_PROPERTY(QString plainBody READ plainBody CONSTANT)
36     Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT)
37     Q_PROPERTY(const EventContent::TypedBase* content READ content CONSTANT)
38 public:
39     DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent)
40 
41     enum class MsgType {
42         Text,
43         Emote,
44         Notice,
45         Image,
46         File,
47         Location,
48         Video,
49         Audio,
50         Unknown
51     };
52 
53     RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType,
54                      EventContent::TypedBase* content = nullptr);
55     explicit RoomMessageEvent(const QString& plainBody,
56                               MsgType msgType = MsgType::Text,
57                               EventContent::TypedBase* content = nullptr);
58     explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file,
59                               bool asGenericFile = false);
60     explicit RoomMessageEvent(const QJsonObject& obj);
61 
62     MsgType msgtype() const;
63     QString rawMsgtype() const;
64     QString plainBody() const;
content()65     const EventContent::TypedBase* content() const { return _content.data(); }
66     template <typename VisitorT>
editContent(VisitorT && visitor)67     void editContent(VisitorT&& visitor)
68     {
69         visitor(*_content);
70         editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(),
71                                                       _content.data());
72     }
73     QMimeType mimeType() const;
74     bool hasTextContent() const;
75     bool hasFileContent() const;
76     bool hasThumbnail() const;
77     QString replacedEvent() const;
78 
79     static QString rawMsgTypeForUrl(const QUrl& url);
80     static QString rawMsgTypeForFile(const QFileInfo& fi);
81 
82 private:
83     QScopedPointer<EventContent::TypedBase> _content;
84 
85     // FIXME: should it really be static?
86     static QJsonObject assembleContentJson(const QString& plainBody,
87                                            const QString& jsonMsgType,
88                                            EventContent::TypedBase* content);
89 
90     Q_ENUM(MsgType)
91 };
92 REGISTER_EVENT_TYPE(RoomMessageEvent)
93 using MessageEventType = RoomMessageEvent::MsgType;
94 
95 namespace EventContent {
96     // Additional event content types
97 
98     struct RelatesTo {
ReplyTypeIdRelatesTo99         static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; }
ReplacementTypeIdRelatesTo100         static constexpr const char* ReplacementTypeId() { return "m.replace"; }
101         QString type; // The only supported relation so far
102         QString eventId;
103     };
replyTo(QString eventId)104     inline RelatesTo replyTo(QString eventId)
105     {
106         return { RelatesTo::ReplyTypeId(), std::move(eventId) };
107     }
replacementOf(QString eventId)108     inline RelatesTo replacementOf(QString eventId)
109     {
110         return { RelatesTo::ReplacementTypeId(), std::move(eventId) };
111     }
112 
113     /**
114      * Rich text content for m.text, m.emote, m.notice
115      *
116      * Available fields: mimeType, body. The body can be either rich text
117      * or plain text, depending on what mimeType specifies.
118      */
119     class TextContent : public TypedBase {
120     public:
121         TextContent(QString text, const QString& contentType,
122                     Omittable<RelatesTo> relatesTo = none);
123         explicit TextContent(const QJsonObject& json);
124 
type()125         QMimeType type() const override { return mimeType; }
126 
127         QMimeType mimeType;
128         QString body;
129         Omittable<RelatesTo> relatesTo;
130 
131     protected:
132         void fillJson(QJsonObject* json) const override;
133     };
134 
135     /**
136      * Content class for m.location
137      *
138      * Available fields:
139      * - corresponding to the top-level JSON:
140      *   - geoUri ("geo_uri" in JSON)
141      * - corresponding to the "info" subobject:
142      *   - thumbnail.url ("thumbnail_url" in JSON)
143      * - corresponding to the "info/thumbnail_info" subobject:
144      *   - thumbnail.payloadSize
145      *   - thumbnail.mimeType
146      *   - thumbnail.imageSize
147      */
148     class LocationContent : public TypedBase {
149     public:
150         LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {});
151         explicit LocationContent(const QJsonObject& json);
152 
153         QMimeType type() const override;
154 
155     public:
156         QString geoUri;
157         Thumbnail thumbnail;
158 
159     protected:
160         void fillJson(QJsonObject* o) const override;
161     };
162 
163     /**
164      * A base class for info types that include duration: audio and video
165      */
166     template <typename ContentT>
167     class PlayableContent : public ContentT {
168     public:
169         using ContentT::ContentT;
PlayableContent(const QJsonObject & json)170         PlayableContent(const QJsonObject& json)
171             : ContentT(json)
172             , duration(ContentT::originalInfoJson["duration"_ls].toInt())
173         {}
174 
175     protected:
fillJson(QJsonObject * json)176         void fillJson(QJsonObject* json) const override
177         {
178             ContentT::fillJson(json);
179             auto infoJson = json->take("info"_ls).toObject();
180             infoJson.insert(QStringLiteral("duration"), duration);
181             json->insert(QStringLiteral("info"), infoJson);
182         }
183 
184     public:
185         int duration;
186     };
187 
188     /**
189      * Content class for m.video
190      *
191      * Available fields:
192      * - corresponding to the top-level JSON:
193      *   - url
194      *   - filename (extension to the CS API spec)
195      * - corresponding to the "info" subobject:
196      *   - payloadSize ("size" in JSON)
197      *   - mimeType ("mimetype" in JSON)
198      *   - duration
199      *   - imageSize (QSize for a combination of "h" and "w" in JSON)
200      *   - thumbnail.url ("thumbnail_url" in JSON)
201      * - corresponding to the "info/thumbnail_info" subobject: contents of
202      *   thumbnail field, in the same vein as for "info":
203      *   - payloadSize
204      *   - mimeType
205      *   - imageSize
206      */
207     using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>;
208 
209     /**
210      * Content class for m.audio
211      *
212      * Available fields:
213      * - corresponding to the top-level JSON:
214      *   - url
215      *   - filename (extension to the CS API spec)
216      * - corresponding to the "info" subobject:
217      *   - payloadSize ("size" in JSON)
218      *   - mimeType ("mimetype" in JSON)
219      *   - duration
220      */
221     using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>;
222 } // namespace EventContent
223 } // namespace Quotient
224