1 /*
2  * im_mf.h
3  *
4  * Media formats for Instant Messaging
5  *
6  * Open Phone Abstraction Library (OPAL)
7  *
8  * Copyright (c) 2008 Post Increment
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open Phone Abstraction Library.
21  *
22  * The Initial Developer of the Original Code is Post Increment
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 27149 $
27  * $Author: rjongbloed $
28  * $Date: 2012-03-07 18:32:36 -0600 (Wed, 07 Mar 2012) $
29  */
30 
31 #ifndef OPAL_IM_IM_H
32 #define OPAL_IM_IM_H
33 
34 #include <ptlib.h>
35 #include <opal/buildopts.h>
36 
37 #include <ptclib/url.h>
38 #include <ptclib/threadpool.h>
39 
40 #include <opal/transports.h>
41 
42 class OpalIM : public PObject
43 {
44   public:
45     OpalIM();
46 
47     enum Type {
48       Text,
49       CompositionIndication_Idle,   // aka RFC 3994
50       CompositionIndication_Active, // aka RFC 3994
51       Disposition                   // aka RFC 5438
52     } m_type;
53 
54     PURL    m_to;
55     PURL    m_from;
56     PString m_fromName;
57     PString m_mimeType;
58     PString m_body;
59     PString m_conversationId;
60 
61     OpalTransportAddress m_fromAddr;
62     OpalTransportAddress m_toAddr;
63 
64     PAtomicInteger::IntegerType m_messageId;
65 
66     static PAtomicInteger::IntegerType GetNextMessageId();
67 };
68 
69 #if OPAL_HAS_IM
70 
71 #include <opal/mediastrm.h>
72 #include <im/rfc4103.h>
73 
74 class OpalIMMediaType : public OpalMediaTypeDefinition
75 {
76   public:
OpalIMMediaType(const char * mediaType,const char * sdpType)77     OpalIMMediaType(
78       const char * mediaType, ///< name of the media type (audio, video etc)
79       const char * sdpType    ///< name of the SDP type
80     )
81       : OpalMediaTypeDefinition(mediaType, sdpType, 0, OpalMediaType::DontOffer)
82     { }
83 
GetRTPEncoding()84     PString GetRTPEncoding() const { return PString::Empty(); }
CreateRTPSession(OpalRTPConnection &,unsigned,bool)85     RTP_UDP * CreateRTPSession(OpalRTPConnection & , unsigned , bool ) { return NULL; }
UsesRTP()86     virtual bool UsesRTP() const { return false; }
87 };
88 
89 /////////////////////////////////////////////////////////////////////
90 
91 class OpalIMManager;
92 class OpalPresentity;
93 
94 class OpalIMContext : public PSafeObject
95 {
96   PCLASSINFO(OpalIMContext, PSafeObject);
97 
98   public:
99     friend class OpalIMManager;
100 
101     OpalIMContext();
102     ~OpalIMContext();
103 
104     static PSafePtr<OpalIMContext> Create(
105       OpalManager & manager,
106       const PURL & localURL,
107       const PURL & remoteURL
108     );
109 
110     static PSafePtr<OpalIMContext> Create(
111       OpalManager & manager,
112       PSafePtr<OpalConnection> conn
113     );
114 
115     static PSafePtr<OpalIMContext> Create(
116       OpalManager & manager,
117       PSafePtr<OpalPresentity> presentity,
118       const PURL & remoteURL
119     );
120 
121     enum SentStatus {
122       SentOK,
123       SentPending,
124       SentAccepted,
125       SentUnacceptableContent,
126       SentInvalidContent,
127       SentConnectionClosed,
128       SentNoTransport,
129       SentNoAnswer,
130       SentDestinationUnknown,
131       SentFailedGeneric,
132     };
133 
134     // send text message in this conversation
135     virtual SentStatus Send(OpalIM * message);
136     virtual SentStatus SendCompositionIndication(bool active = true);
137 
138     /// Called when an outgoing message has been delivered for this context
139     /// Default implementation calls MessageSentNotifier, if set
140     struct MessageSentInfo {
141       PAtomicInteger::IntegerType messageId;
142       OpalIMContext::SentStatus status;
143     };
144     virtual void OnMessageSent(const MessageSentInfo & info);
145 
146     typedef PNotifierTemplate<const MessageSentInfo &> MessageSentNotifier;
147     #define PDECLARE_MessageSentNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalIMContext, cls, fn, const MessageSentInfo &)
148     #define PCREATE_MessageSentNotifier(fn) PCREATE_NOTIFIER2(fn, const MessageSentInfo &)
149 
150     /// Set the notifier for the OnMessageSent() function.
151     void SetMessageSentNotifier(
152       const MessageSentNotifier & notifier   ///< Notifier to be called by OnIncomingIM()
153     );
154 
155     /// Called when an incoming message arrives for this context
156     /// Default implementation calls IncomingIMNotifier, if set, else returns true
157     virtual SentStatus OnIncomingIM(OpalIM & message);
158 
159     typedef PNotifierTemplate<const OpalIM &> IncomingIMNotifier;
160     #define PDECLARE_IncomingIMNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalIMContext, cls, fn, const OpalIM &)
161     #define PCREATE_IncomingIMNotifier(fn) PCREATE_NOTIFIER2(fn, const OpalIM &)
162 
163     /// Set the notifier for the OnIncomingMessage() function.
164     void SetIncomingIMNotifier(
165       const IncomingIMNotifier & notifier   ///< Notifier to be called by OnIncomingIM()
166     );
167 
168     /// Called when the remote composition indication changes state for this context
169     /// Default implementation calls IncomingIMNotifier, if set, else returns true
170     virtual void OnCompositionIndicationChanged(const PString & state);
171 
172     typedef PNotifierTemplate<const PString &> CompositionIndicationChangedNotifier;
173     #define PDECLARE_CompositionIndicationChangedNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalIMContext, cls, fn, const PString &)
174     #define PCREATE_CompositionIndicationChangedNotifier(fn) PCREATE_NOTIFIER2(fn, const PString &)
175 
176     /// Set the notifier for the OnIncomingMessage() function.
177     void SetCompositionIndicationChangedNotifier(
178       const CompositionIndicationChangedNotifier & notifier   ///< Notifier to be called by OnIncomingIM()
179     );
180 
181     virtual bool CheckContentType(const PString & contentType) const;
182     virtual PStringArray GetContentTypes() const;
183 
184     // start of internal functions
185 
GetID()186     PString GetID() const          { return m_id; }
SetID(const PString & id)187     void SetID(const PString & id) { m_id = id; }
GetKey()188     PString GetKey() const         { return m_key; }
GetLocalURL()189     PString GetLocalURL() const    { return m_localURL; }
GetRemoteURL()190     PString GetRemoteURL() const   { return m_remoteURL; }
191 
192   /**@name Attributes */
193   //@{
194     ///< Get the attributes for this presentity.
GetAttributes()195     PStringOptions & GetAttributes() { return m_attributes; }
GetAttributes()196     const PStringOptions & GetAttributes() const { return m_attributes; }
197 
198     virtual bool OnNewIncomingIM();
199 
200     virtual bool AddIncomingIM(OpalIM * message);
201 
202     virtual void OnCompositionIndicationTimeout();
203 
204     OpalIM * GetIncomingMessage();
205 
206     virtual void InternalOnMessageSent(const MessageSentInfo & info);
207 
208     static PString CreateKey(const PString & from, const PString & to);
209 
210     void ResetLastUsed();
211 
212   protected:
213     virtual SentStatus InternalSend();
214     virtual SentStatus InternalSendOutsideCall(OpalIM * message);
215     virtual SentStatus InternalSendInsideCall(OpalIM * message);
216 
217     PMutex m_notificationMutex;
218     IncomingIMNotifier                   m_incomingMessageNotifier;
219     MessageSentNotifier                  m_messageSentNotifier;
220     CompositionIndicationChangedNotifier m_compositionIndicationChangedNotifier;
221 
222     OpalManager  * m_manager;
223     PStringOptions m_attributes;
224 
225     PSafePtr<OpalConnection> m_connection;
226     PSafePtr<OpalPresentity> m_presentity;
227 
228     PMutex m_incomingMessagesMutex;
229     PQueue<OpalIM> m_incomingMessages;
230 
231     PMutex m_outgoingMessagesMutex;
232     OpalIM * m_currentOutgoingMessage;
233     PQueue<OpalIM> m_outgoingMessages;
234 
235     PMutex m_lastUsedMutex;
236     PTime m_lastUsed;
237 
238   private:
239     PString m_id, m_localURL, m_remoteURL, m_key;
240 
241 };
242 
243 class OpalConnectionIMContext : public OpalIMContext
244 {
245   public:
246     OpalConnectionIMContext();
247 };
248 
249 class OpalPresentityIMContext : public OpalIMContext
250 {
251   public:
252     OpalPresentityIMContext();
253 };
254 
255 /////////////////////////////////////////////////////////////////////
256 
257 class OpalIMManager : public PObject
258 {
259   public:
260     OpalIMManager(OpalManager & manager);
261     ~OpalIMManager();
262 
263     class IM_Work;
264 
265     OpalIMContext::SentStatus OnIncomingMessage(OpalIM * im, PString & conversationId, PSafePtr<OpalConnection> conn = NULL);
266     void OnCompositionIndicationTimeout(const PString & conversationId);
267 
268     void AddContext(PSafePtr<OpalIMContext> context);
269     void RemoveContext(OpalIMContext * context);
270 
271     void GarbageCollection();
272 
273     PSafePtr<OpalIMContext> FindContextByIdWithLock(
274       const PString & key,
275       PSafetyMode mode = PSafeReadWrite
276     );
277 
278     PSafePtr<OpalIMContext> FindContextByNamesWithLock(
279       const PString & local,
280       const PString & remote,
281       PSafetyMode mode = PSafeReadWrite
282     );
283 
284     PSafePtr<OpalIMContext> FindContextForMessageWithLock(OpalIM & im, OpalConnection * conn = NULL);
285 
286     typedef PNotifierTemplate<OpalIMContext &> NewConversationNotifier;
287     #define PDECLARE_NewConversationNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalIMManager, cls, fn, OpalIMContext &)
288     #define PCREATE_NewConversationNotifier(fn) PCREATE_NOTIFIER2(fn, OpalIMContext &)
289 
290     class NewConversationCallBack : public PObject {
291       public:
292         NewConversationNotifier m_notifier;
293         PString m_scheme;
294     };
295 
296     void AddNotifier(const NewConversationNotifier & notifier, const PString & scheme);
297     bool RemoveNotifier(const NewConversationNotifier & notifier, const PString & scheme);
298 
299 
300     // thread pool declarations
301     class IM_Work
302     {
303       public:
304         IM_Work(OpalIMManager & mgr, const PString & conversationId);
305         virtual ~IM_Work();
306 
307         virtual void Work() = 0;
308 
309         OpalIMManager & m_mgr;
310         PString m_conversationId;
311     };
312 
313     class NewIncomingIM_Work : public IM_Work
314     {
315       public:
NewIncomingIM_Work(OpalIMManager & mgr,const PString & conversationId)316         NewIncomingIM_Work(OpalIMManager & mgr, const PString & conversationId)
317           : IM_Work(mgr, conversationId)
318         { }
Work()319         virtual void Work()
320         { m_mgr.InternalOnNewIncomingIM(m_conversationId); }
321     };
322 
323     class NewConversation_Work : public IM_Work
324     {
325       public:
NewConversation_Work(OpalIMManager & mgr,const PString & conversationId)326         NewConversation_Work(OpalIMManager & mgr, const PString & conversationId)
327           : IM_Work(mgr, conversationId)
328         { }
Work()329         virtual void Work()
330         { m_mgr.InternalOnNewConversation(m_conversationId); }
331     };
332 
333     class MessageSent_Work : public IM_Work
334     {
335       public:
MessageSent_Work(OpalIMManager & mgr,const PString & conversationId,const OpalIMContext::MessageSentInfo & info)336         MessageSent_Work(OpalIMManager & mgr, const PString & conversationId, const OpalIMContext::MessageSentInfo & info)
337           : IM_Work(mgr, conversationId)
338           , m_info(info)
339         { }
Work()340         virtual void Work()
341         { m_mgr.InternalOnMessageSent(m_conversationId, m_info); }
342 
343         OpalIMContext::MessageSentInfo m_info;
344     };
345 
346     class CompositionIndicationTimeout_Work : public IM_Work
347     {
348       public:
CompositionIndicationTimeout_Work(OpalIMManager & mgr,const PString & conversationId)349         CompositionIndicationTimeout_Work(OpalIMManager & mgr, const PString & conversationId)
350           : IM_Work(mgr, conversationId)
351         { }
Work()352         virtual void Work()
353         { m_mgr.InternalOnCompositionIndicationTimeout(m_conversationId); }
354     };
355 
356 
357     void AddWork(IM_Work * work);
358     virtual void InternalOnNewConversation(const PString & conversation);
359     virtual void InternalOnNewIncomingIM(const PString & conversation);
360     virtual void InternalOnMessageSent(const PString & conversation, const OpalIMContext::MessageSentInfo & info);
361     virtual void InternalOnCompositionIndicationTimeout(const PString & conversationId);
362 
363   protected:
364     PQueuedThreadPool<IM_Work> m_imThreadPool;
365 
366     PTime m_lastGarbageCollection;
367     OpalManager & m_manager;
368     bool m_deleting;
369     typedef PSafeDictionary<PString, OpalIMContext> ContextsByConversationId;
370     ContextsByConversationId m_contextsByConversationId;
371 
372     PMutex m_contextsByNamesMutex;
373     typedef std::multimap<std::string, PString> ContextsByNames;
374     ContextsByNames m_contextsByNames;
375 
376     PMutex m_notifierMutex;
377     PList<NewConversationCallBack> m_callbacks;
378 };
379 
380 /////////////////////////////////////////////////////////////////////
381 
382 class RTP_IMFrame : public RTP_DataFrame
383 {
384   public:
385     RTP_IMFrame();
386     RTP_IMFrame(const PString & contentType);
387     RTP_IMFrame(const PString & contentType, const T140String & content);
388     RTP_IMFrame(const BYTE * data, PINDEX len, PBoolean dynamic = true);
389 
390     void SetContentType(const PString & contentType);
391     PString GetContentType() const;
392 
393     void SetContent(const T140String & text);
394     bool GetContent(T140String & text) const;
395 
AsString()396     PString AsString() const { return PString((const char *)GetPayloadPtr(), GetPayloadSize()); }
397 };
398 
399 class OpalIMMediaStream : public OpalMediaStream
400 {
401   public:
402     OpalIMMediaStream(
403       OpalConnection & conn,
404       const OpalMediaFormat & mediaFormat, ///<  Media format for stream
405       unsigned sessionID,                  ///<  Session number for stream
406       bool isSource                        ///<  Is a source stream
407     );
408 
IsSynchronous()409     virtual PBoolean IsSynchronous() const         { return false; }
RequiresPatchThread()410     virtual PBoolean RequiresPatchThread() const   { return false; }
411 
412     bool ReadPacket(RTP_DataFrame & packet);
413     bool WritePacket(RTP_DataFrame & packet);
414 
415   protected:
InternalClose()416     virtual void InternalClose() { }
417 };
418 
419 #endif // OPAL_HAS_IM
420 
421 #endif // OPAL_IM_IM_H
422