1 /*
2  * prese_ent.h
3  *
4  * Presence Entity classes for Opal
5  *
6  * Open Phone Abstraction Library (OPAL)
7  *
8  * Copyright (c) 2009 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: 28081 $
27  * $Author: rjongbloed $
28  * $Date: 2012-07-22 07:24:14 -0500 (Sun, 22 Jul 2012) $
29  */
30 
31 #ifndef OPAL_IM_PRES_ENT_H
32 #define OPAL_IM_PRES_ENT_H
33 
34 #include <ptlib.h>
35 #include <opal/buildopts.h>
36 
37 #include <ptlib/pfactory.h>
38 #include <ptlib/safecoll.h>
39 #include <ptclib/url.h>
40 #include <ptclib/guid.h>
41 #include <ptclib/vcard.h>
42 
43 #include <im/im.h>
44 
45 #include <list>
46 #include <queue>
47 
48 class OpalManager;
49 class OpalPresentityCommand;
50 
51 
52 ////////////////////////////////////////////////////////////////////////////////////////////////////
53 
54 /**Presencu state information
55   */
56 class OpalPresenceInfo : public PObject
57 {
58   public:
59     /// Presence states.
60     enum State {
61       InternalError = -3,    // something bad happened
62       Forbidden     = -2,    // access to presence information was specifically forbidden
63       NoPresence    = -1,    // remove presence status - not the same as Unavailable or Away
64 
65       // basic states (from RFC 3863)
66       Unchanged,
67       Available,
68       Unavailable,
69 
70       // extended states (from RFC 4480)
71       // if this is changed, also change the tables in sippres.cxx and handlers.cxx - look for RFC 4480
72       ExtendedBase    = 100,
73       UnknownExtended = ExtendedBase,
74       Appointment,
75       Away,
76       Breakfast,
77       Busy,
78       Dinner,
79       Holiday,
80       InTransit,
81       LookingForWork,
82       Lunch,
83       Meal,
84       Meeting,
85       OnThePhone,
86       Other,
87       Performance,
88       PermanentAbsence,
89       Playing,
90       Presentation,
91       Shopping,
92       Sleeping,
93       Spectator,
94       Steering,
95       Travel,
96       TV,
97       Vacation,
98       Working,
99       Worship
100     };
101 
102     State   m_state;    ///< New state for presentity
103     PString m_note;     ///< Additional information about state change
104     PURL    m_entity;   ///< The presentity whose state had changed
105     PURL    m_target;   ///< The presentity that is being informed about the state change
106     PTime   m_when;     ///< Time/date of state change
107 
m_state(state)108     OpalPresenceInfo(State state = Unchanged) : m_state(state) { }
109 
110     static PString AsString(State state);
111     static State FromString(const PString & str);
112     PString AsString() const;
113 
114     Comparison Compare(const PObject & other) const;
115 };
116 
117 ostream & operator<<(ostream & strm, OpalPresenceInfo::State state);
118 
119 ////////////////////////////////////////////////////////////////////////////////////////////////////
120 
121 class OpalSetLocalPresenceCommand;
122 class OpalSubscribeToPresenceCommand;
123 class OpalAuthorisationRequestCommand;
124 class OpalSendMessageToCommand;
125 
126 /**Representation of a presence identity.
127    This class contains an abstraction of the functionality for "presence"
128    using a URL string as the "identity". The concrete class depends on the
129    scheme of the identity URL.
130 
131    The general architecture is that commands will be sent to the preentity
132    concrete class via concrete versions of OpalPresentityCommand class. These
133    may be protocol specific or one of the abstracted versions.
134   */
135 class OpalPresentity : public PSafeObject
136 {
137     PCLASSINFO(OpalPresentity, PSafeObject);
138 
139   /**@name Construction */
140   //@{
141   protected:
142     /// Construct the presentity class
143     OpalPresentity();
144     OpalPresentity(const OpalPresentity & other);
145 
146   public:
147     ~OpalPresentity();
148 
149     /**Create a concrete class based on the scheme of the URL provided.
150       */
151     static OpalPresentity * Create(
152       OpalManager & manager, ///< Manager for presentity
153       const PURL & url,      ///< URL for presence identity
154       const PString & scheme = PString::Empty() ///< Overide the url scheme
155     );
156   //@}
157 
158   /**@name Initialisation */
159   //@{
160     /** Open the presentity handler.
161         This will perform whatever is required to allow this presentity
162         to access servers etc for the underlying protocol. It may start open
163         network channels, start threads etc.
164 
165         Note that a return value of true does not necessarily mean that the
166         presentity has successfully been indicated as "present" on the server
167         only that the underlying system can do so at some time.
168       */
169     virtual bool Open();
170 
171     /** Indicate if the presentity has been successfully opened.
172       */
IsOpen()173     virtual bool IsOpen() const { return m_open; }
174 
175     /**Close the presentity.
176       */
177     virtual bool Close();
178   //@}
179 
180   /**@name Attributes */
181   //@{
182     ///< Get the attributes for this presentity.
GetAttributes()183     PStringOptions & GetAttributes() { return m_attributes; }
184 
185     ///< Get all attribute names for this presentity class.
186     virtual PStringArray GetAttributeNames() const = 0;
187 
188     ///< Get all attribute types for this presentity class.
189     virtual PStringArray GetAttributeTypes() const = 0;
190 
191     static const PCaselessString & AuthNameKey();         ///< Key for authentication name attribute
192     static const PCaselessString & AuthPasswordKey();     ///< Key for authentication password attribute
193     static const PCaselessString & TimeToLiveKey();       ///< Key for Time-To-Live attribute, in seconds for underlying protocol
194 
195     /** Get the address-of-record for the presentity.
196         This is typically a URL which represents our local identity in the
197         presense system.
198       */
GetAOR()199     const PURL & GetAOR() const { return m_aor; }
200   //@}
201 
202   /**@name Commands */
203   //@{
204     /** Subscribe to presence state of another presentity.
205         This function is a wrapper and the OpalSubscribeToPresenceCommand
206         command.
207 
208         Returns true if the command was queued. Note that this does not
209         mean the command succeeded, only that the underlying protocol is
210         capabable of the action.
211       */
212     virtual bool SubscribeToPresence(
213       const PURL & presentity,      ///< Other presentity to monitor
214       bool subscribe = true,        ///< true if to subscribe, else unsubscribe
215       const PString & note = PString::Empty() ///< Optional extra note attached to subscription request
216     );
217 
218     /** Unsubscribe to presence state of another presentity.
219         This function is a wrapper and the OpalSubscribeToPresenceCommand
220         command.
221 
222         Returns true if the command was queued. Note that this does not
223         mean the command succeeded, only that the underlying protocol is
224         capabable of the action.
225       */
226     virtual bool UnsubscribeFromPresence(
227       const PURL & presentity    ///< Other presentity to monitor
228     );
229 
230     /// Authorisation modes for SetPresenceAuthorisation()
231     enum Authorisation {
232       AuthorisationPermitted,
233       AuthorisationDenied,
234       AuthorisationDeniedPolitely,
235       AuthorisationConfirming,
236       AuthorisationRemove,
237       NumAuthorisations
238     };
239 
240     /** Called to allow/deny another presentity access to our presence
241         information.
242 
243         This function is a wrapper and the OpalAuthorisationRequestCommand
244         command.
245 
246         Returns true if the command was queued. Note that this does not
247         mean the command succeeded, only that the underlying protocol is
248         capabable of the action.
249       */
250     virtual bool SetPresenceAuthorisation(
251       const PURL & presentity,        ///< Remote presentity to be authorised
252       Authorisation authorisation     ///< Authorisation mode
253     );
254 
255     /** Set our presence state.
256         This function is a wrapper and the OpalSetLocalPresenceCommand command.
257 
258         Returns true if the command was queued. Note that this does not
259         mean the command succeeded, only that the underlying protocol is
260         capabable of the action.
261       */
262     virtual bool SetLocalPresence(
263       OpalPresenceInfo::State state,            ///< New state for our presentity
264       const PString & note = PString::Empty()   ///< Additional note attached to the state change
265     );
266 
267     /** Get our presence state
268     */
269     virtual bool GetLocalPresence(
270       OpalPresenceInfo::State & state,
271       PString & note
272     );
273 
274 
275     /** Low level function to create a command.
276         As commands have protocol specific implementations, we use a factory
277         to create them.
278       */
279     template <class cls>
CreateCommand()280     __inline cls * CreateCommand()
281     {
282       return dynamic_cast<cls *>(InternalCreateCommand(typeid(cls).name()));
283     }
284 
285     /** Lowlevel function to send a command to the presentity handler.
286         All commands are asynchronous. They will usually initiate an action
287         for which an indication (callback) will give a result.
288 
289         Note that the command is typically created by the CreateCommand()
290         function and is subsequently deleted by this function.
291 
292         Returns true if the command was queued. Note that this does not
293         mean the command succeeded, only that the underlying protocol is
294         processing commands.
295       */
296     virtual bool SendCommand(
297       OpalPresentityCommand * cmd   ///< Command to be processed
298     );
299   //@}
300 
301   /**@name Indications (callbacks) */
302   //@{
303     struct AuthorisationRequest
304     {
305       PURL    m_presentity; ///< Other presentity requesting our presence
306       PString m_note;       ///< Optional extra note attached to request
307     };
308 
309     /** Callback when another presentity requests access to our presence.
310         It is expected that the handler will call SetPresenceAuthorisation
311         with whatever policy is appropriate.
312 
313         Default implementation calls m_onRequestPresenceNotifier if non-NULL
314         otherwise will authorise the request.
315       */
316     virtual void OnAuthorisationRequest(
317       const AuthorisationRequest & request  ///< Authorisation request information
318     );
319 
320     typedef PNotifierTemplate<const AuthorisationRequest &> AuthorisationRequestNotifier;
321 #define PDECLARE_AuthorisationRequestNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalPresentity, cls, fn, const OpalPresentity::AuthorisationRequest &)
322     #define PCREATE_AuthorisationRequestNotifier(fn) PCREATE_NOTIFIER2(fn, const OpalPresentity::AuthorisationRequest &)
323 
324     /// Set the notifier for the OnAuthorisationRequest() function.
325     void SetAuthorisationRequestNotifier(
326       const AuthorisationRequestNotifier & notifier   ///< Notifier to be called by OnAuthorisationRequest()
327     );
328 
329     /** Callback when presentity has changed its state.
330         Note if the m_entity and m_target fields of the OpalPresenceInfo
331         structure are the same, then this indicates that the local presentity
332         itself has successfully "registered" itself with the presence agent
333         server.
334 
335         Default implementation calls m_onPresenceChangeNotifier.
336       */
337     virtual void OnPresenceChange(
338       const OpalPresenceInfo & info ///< Info on other presentity that changed state
339     );
340 
341     typedef PNotifierTemplate<const OpalPresenceInfo &> PresenceChangeNotifier;
342     #define PDECLARE_PresenceChangeNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalPresentity, cls, fn, const OpalPresenceInfo &)
343     #define PCREATE_PresenceChangeNotifier(fn) PCREATE_NOTIFIER2(fn, const OpalPresenceInfo &)
344 
345     /// Set the notifier for the OnPresenceChange() function.
346     void SetPresenceChangeNotifier(
347       const PresenceChangeNotifier & notifier   ///< Notifier to be called by OnPresenceChange()
348     );
349   //@}
350 
351   /**@name Buddy Lists */
352   //@{
353     /**Buddy list entry.
354        The buddy list is a list of presentities that the application is
355        expecting to get presence status for.
356       */
357     struct BuddyInfo {
358       BuddyInfo(
359         const PURL & presentity = PString::Empty(),
360         const PString & displayName = PString::Empty()
m_presentityBuddyInfo361       ) : m_presentity(presentity)
362         , m_displayName(displayName)
363       { }
364 
365       PURL    m_presentity;   ///< Typicall URI address-of-record
366       PString m_displayName;  ///< Human readable name
367 
368       // RFC4482 contact fields, note most of these are duplicated
369       // in the vCard structure
370       PvCard  m_vCard;        /**< vCard for the buddy. This is
371                                    either a URI pointing to the image, or the image
372                                    itself in the form: "data:text/x-vcard,xxxxx
373                                    as per RFC2397 */
374       PURL    m_icon;         /**< The icon/avatar/photo for the buddy. This is
375                                    either a URI pointing to the image, or the image
376                                    itself in the form: "data:image/jpeg;base64,xxxxx
377                                    as per RFC2397 */
378       PURL    m_map;          /**< The map reference for the buddy. This is may be URL
379                                    to a GIS system or an image file similar to m_icon */
380       PURL    m_sound;        /**< The sound relating to the buddy. This is
381                                    either a URI pointing to the sound, or the sound
382                                    itself in the form: "data:audio/mpeg;base64,xxxxx
383                                    as per RFC2397 */
384       PURL    m_homePage;     ///< Home page for buddy
385 
386       // Extra field for protocol dependent "get out of gaol" card
387       PString m_contentType;  ///< MIME type code for XML
388       PString m_rawXML;       ///< Raw XML of buddy list entry
389     };
390 
391     typedef std::list<BuddyInfo> BuddyList;
392 
393     enum BuddyStatus {
394       BuddyStatus_GenericFailure             = -1,
395       BuddyStatus_OK                         = 0,
396       BuddyStatus_SpecifiedBuddyNotFound,
397       BuddyStatus_ListFeatureNotImplemented,
398       BuddyStatus_ListTemporarilyUnavailable,
399       BuddyStatus_ListMayBeIncomplete,
400       BuddyStatus_BadBuddySpecification,
401       BuddyStatus_ListSubscribeFailed,
402       BuddyStatus_AccountNotLoggedIn
403     };
404 
405     /**Get complete buddy list.
406       */
407     virtual BuddyStatus GetBuddyListEx(
408       BuddyList & buddies   ///< List of buddies
409     );
GetBuddyList(BuddyList & buddies)410     virtual bool GetBuddyList(
411       BuddyList & buddies   ///< List of buddies
412     )
413     { return GetBuddyListEx(buddies) == BuddyStatus_OK; }
414 
415     /**Set complete buddy list.
416       */
417     virtual BuddyStatus SetBuddyListEx(
418       const BuddyList & buddies   ///< List of buddies
419     );
SetBuddyList(const BuddyList & buddies)420     virtual bool SetBuddyList(
421       const BuddyList & buddies   ///< List of buddies
422     )
423     { return SetBuddyListEx(buddies) == BuddyStatus_OK; }
424 
425 
426     /**Delete the buddy list.
427       */
428     virtual BuddyStatus DeleteBuddyListEx();
DeleteBuddyList()429     virtual bool DeleteBuddyList() { return DeleteBuddyListEx() == BuddyStatus_OK; }
430 
431     /**Get a specific buddy from the buddy list.
432        Note the buddy.m_presentity field must be preset to the URI to search
433        the buddy list for.
434       */
435     virtual BuddyStatus GetBuddyEx(
436       BuddyInfo & buddy
437     );
GetBuddy(BuddyInfo & buddy)438     virtual bool GetBuddy(
439       BuddyInfo & buddy
440     )
441     { return GetBuddyEx(buddy) == BuddyStatus_OK; }
442 
443     /**Set/Add a buddy to the buddy list.
444       */
445     virtual BuddyStatus SetBuddyEx(
446       const BuddyInfo & buddy
447     );
SetBuddy(const BuddyInfo & buddy)448     virtual bool SetBuddy(
449       const BuddyInfo & buddy
450     )
451     { return SetBuddyEx(buddy) == BuddyStatus_OK; }
452 
453     /**Delete a buddy to the buddy list.
454       */
455     virtual BuddyStatus DeleteBuddyEx(
456       const PURL & presentity
457     );
DeleteBuddy(const PURL & presentity)458     virtual bool DeleteBuddy(
459       const PURL & presentity
460     )
461     { return DeleteBuddyEx(presentity) == BuddyStatus_OK; }
462 
463     /**Subscribe to buddy list.
464        Send a subscription for the presence of every presentity in the current
465        buddy list. This might cause multiple calls to SubscribeToPresence() or
466        if the underlying protocol allows a single call for all.
467       */
468     virtual BuddyStatus SubscribeBuddyListEx(
469       PINDEX & successfulCount,
470       bool subscribe = true
471     );
472     virtual bool SubscribeBuddyList(
473       bool subscribe = true
474     )
475     { PINDEX successfulCount; return SubscribeBuddyListEx(successfulCount, subscribe) == BuddyStatus_OK; }
476 
477     /**Unsubscribe to buddy list.
478        Send an unsubscription for the presence of every presentity in the current
479        buddy list. This might cause multiple calls to UnsubscribeFromPresence() or
480        if the underlying protocol allows a single call for all.
481       */
482     virtual BuddyStatus UnsubscribeBuddyListEx();
UnsubscribeBuddyList()483     virtual bool UnsubscribeBuddyList()
484     { return UnsubscribeBuddyListEx() == BuddyStatus_OK; }
485   //@}
486 
487 
488   /**@name Instant Messaging */
489   //@{
490     virtual bool SendMessageTo(
491       const OpalIM & message
492     );
493 
494     /** Callback when presentity receives a message
495 
496         Default implementation calls m_onReceivedMessageNotifier.
497       */
498     virtual void OnReceivedMessage(
499       const OpalIM & message ///< incoming message
500     );
501 
502     typedef PNotifierTemplate<const OpalIM &> ReceivedMessageNotifier;
503     #define PDECLARE_ReceivedMessageNotifier(cls, fn) PDECLARE_NOTIFIER2(OpalPresentity, cls, fn, const OpalIM &)
504     #define PCREATE_ReceivedMessageNotifier(fn) PCREATE_NOTIFIER2(fn, const OpalIM &)
505 
506     /// Set the notifier for the OnPresenceChange() function.
507     void SetReceivedMessageNotifier(
508       const ReceivedMessageNotifier & notifier   ///< Notifier to be called by OnReceivedMessage()
509     );
510   //@}
511 
512     /** Used to set the AOR after the presentity is created
513         This override allows the descendant class to convert the internal URL into a real AOR,
514         usually by changing the scheme.
515       */
516     virtual void SetAOR(
517       const PURL & aor
518     );
519 
520     void Internal_SendMessageToCommand(const OpalSendMessageToCommand & cmd);
521 
522   protected:
523     OpalPresentityCommand * InternalCreateCommand(const char * cmdName);
524 
525     OpalManager        * m_manager;
526     PGloballyUniqueID    m_guid;
527     PURL                 m_aor;
528     PStringOptions       m_attributes;
529 
530     AuthorisationRequestNotifier m_onAuthorisationRequestNotifier;
531     PresenceChangeNotifier       m_onPresenceChangeNotifier;
532     ReceivedMessageNotifier      m_onReceivedMessageNotifier;
533 
534     PAtomicBoolean m_open;
535     PMutex m_notificationMutex;
536     bool m_temporarilyUnavailable;
537     OpalPresenceInfo::State m_localState;      ///< our presentity state
538     PString m_localStateNote;                  ///< Additional note attached to the
539 };
540 
541 
542 ////////////////////////////////////////////////////////////////////////////////////////////////////
543 
544 /**Representation of a presence identity that uses a background thread to
545    process commands.
546   */
547 class OpalPresentityWithCommandThread : public OpalPresentity
548 {
549     PCLASSINFO(OpalPresentityWithCommandThread, OpalPresentity);
550 
551   /**@name Construction */
552   //@{
553   protected:
554     /// Construct the presentity class that uses a command thread.
555     OpalPresentityWithCommandThread();
556     OpalPresentityWithCommandThread(const OpalPresentityWithCommandThread & other);
557 
558   public:
559     /** Destory the presentity class that uses a command thread.
560         Note this will block until the background thread has stopped.
561       */
562     ~OpalPresentityWithCommandThread();
563   //@}
564 
565   /**@name Overrides from OpalPresentity */
566   //@{
567     /** Lowlevel function to send a command to the presentity handler.
568         All commands are asynchronous. They will usually initiate an action
569         for which an indication (callback) will give a result.
570 
571         The default implementation queues the command for the background
572         thread to handle.
573 
574         Returns true if the command was queued. Note that this does not
575         mean the command succeeded, only that the underlying protocol is
576         processing commands, that is the backgrund thread is running
577       */
578     virtual bool SendCommand(
579       OpalPresentityCommand * cmd   ///< Command to execute
580     );
581   //@}
582 
583   /**@name new functions */
584   //@{
585     /**Start the background thread to handle commands.
586        This is typically called from the concrete classes Open() function.
587 
588        If the argument is true (the default) then the thread starts processing
589        queue entries ASAP.
590 
591        If the argument is false, the thread is still created and still runs,
592        but it does not process queue entries. This allows for presenties that
593        may need to allow commands to be paused, for example during initialisation
594       */
595     void StartThread(
596       bool startQueue = true
597     );
598 
599     /**Stop the background thread to handle commands.
600        This is typically called from the concrete classes Close() function.
601        It is also called fro the destructor to be sure the thread has stopped
602        before the object is destroyed.
603       */
604     void StopThread();
605 
606     /**Start/resume processing of queue commands
607       */
608     void StartQueue(
609       bool startQueue = true
610     );
611 
612   //@}
613 
614   protected:
615     void ThreadMain();
616 
617     typedef std::queue<OpalPresentityCommand *> CommandQueue;
618     CommandQueue   m_commandQueue;
619     PMutex         m_commandQueueMutex;
620     PAtomicInteger m_commandSequence;
621     PSyncPoint     m_commandQueueSync;
622 
623     bool      m_threadRunning;
624     bool      m_queueRunning;
625     PThread * m_thread;
626 };
627 
628 ////////////////////////////////////////////////////////////////////////////////////////////////////
629 
630 /**Abstract class for all OpelPresentity commands.
631   */
632 class OpalPresentityCommand {
633   public:
634     OpalPresentityCommand(bool responseNeeded = false)
m_responseNeeded(responseNeeded)635       : m_responseNeeded(responseNeeded)
636     { }
~OpalPresentityCommand()637     virtual ~OpalPresentityCommand() { }
638 
639     /**Function to process the command.
640        This typically calls functions on the concrete OpalPresentity class.
641       */
642     virtual void Process(
643       OpalPresentity & presentity
644     ) = 0;
645 
646     typedef PAtomicInteger::IntegerType CmdSeqType;
647     CmdSeqType m_sequence;
648     bool       m_responseNeeded;
649     PURL       m_presentity;
650 };
651 
652 /** Macro to define the factory that creates a concrete command class.
653   */
654 #define OPAL_DEFINE_COMMAND(command, entity, func) \
655   class entity##_##command : public command \
656   { \
657     public: virtual void Process(OpalPresentity & presentity) { dynamic_cast<entity &>(presentity).func(*this); } \
658   }; \
659   static PFactory<OpalPresentityCommand>::Worker<entity##_##command> \
660   s_##entity##_##command(PDefaultPFactoryKey(entity::Class())+typeid(command).name())
661 
662 
663 /** Command for subscribing to the status of another presentity.
664   */
665 class OpalSubscribeToPresenceCommand : public OpalPresentityCommand {
666   public:
m_subscribe(subscribe)667     OpalSubscribeToPresenceCommand(bool subscribe = true) : m_subscribe(subscribe) { }
668 
669     bool    m_subscribe;  ///< Flag to indicate subscribing/unsubscribing
670     PString m_note;       ///< Optional extra note attached to subscription request
671 };
672 
673 
674 /** Command for authorising a request by some other presentity to see our status.
675     This action is typically due to some other presentity doing the equivalent
676     of a OpalSubscribeToPresenceCommand. The mechanism by which this happens
677     is dependent on the concrete OpalPresentity class and it's underlying
678     protocol.
679   */
680 class OpalAuthorisationRequestCommand : public OpalPresentityCommand {
681   public:
OpalAuthorisationRequestCommand()682     OpalAuthorisationRequestCommand() : m_authorisation(OpalPresentity::AuthorisationPermitted) { }
683 
684     OpalPresentity::Authorisation m_authorisation;  ///< Authorisation mode to indicate to remote
685     PString m_note;                                 ///< Optional extra note attached to subscription request
686 };
687 
688 
689 /** Command for adusting our current status.
690     This action typically causes all presentities to be notified of the
691     change. The mechanism by which this happens is dependent on the concrete
692     OpalPresentity class and it's underlying protocol.
693   */
694 class OpalSetLocalPresenceCommand : public OpalPresentityCommand, public OpalPresenceInfo {
695   public:
OpalPresenceInfo(state)696     OpalSetLocalPresenceCommand(State state = NoPresence) : OpalPresenceInfo(state) { }
697 };
698 
699 
700 /** Command for sending an IM
701   */
702 class OpalSendMessageToCommand : public OpalPresentityCommand
703 {
704   public:
OpalSendMessageToCommand()705     OpalSendMessageToCommand() { }
706 
707     OpalIM m_message;
708 };
709 
710 ///////////////////////////////////////////////////////////////////////////////
711 
712 // Include concrete classes here so the factories are initialised
713 #if OPAL_SIP && OPAL_PTLIB_EXPAT
714 PFACTORY_LOAD(SIP_Presentity);
715 #endif
716 
717 
718 #endif  // OPAL_IM_PRES_ENT_H
719 
720