1 /*
2  * pres_ent.cxx
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 
32 #include <ptlib.h>
33 #include <opal/buildopts.h>
34 
35 #include <opal/pres_ent.h>
36 
37 #include <opal/manager.h>
38 #include <opal/endpoint.h>
39 #include <ptclib/url.h>
40 #include <sip/sipep.h>
41 
42 ///////////////////////////////////////////////////////////////////////
43 
44 PURL_LEGACY_SCHEME(pres, true, false, true, true, false, true, true, false, false, false, 0)
45 
AuthNameKey()46 const PCaselessString & OpalPresentity::AuthNameKey()     { static const PConstCaselessString s("Auth ID");       return s; }
AuthPasswordKey()47 const PCaselessString & OpalPresentity::AuthPasswordKey() { static const PConstCaselessString s("Auth Password"); return s; }
TimeToLiveKey()48 const PCaselessString & OpalPresentity::TimeToLiveKey()   { static const PConstCaselessString s("Time to Live");  return s; }
49 
50 
AsString() const51 PString OpalPresenceInfo::AsString() const
52 {
53   return AsString(m_state);
54 }
55 
operator <<(ostream & strm,OpalPresenceInfo::State state)56 ostream & operator<<(ostream & strm, OpalPresenceInfo::State state)
57 {
58   return strm << OpalPresenceInfo::AsString(state);
59 }
60 
61 //
62 //  defined in RFC 3856
63 //
64 static const char * const BasicNames[] = {
65   "Internal Error",
66   "Forbidden",
67   "No Presence",
68   "Unchanged",
69   "Available",
70   "Unavailable"
71 };
72 
73 //
74 // defined in RFC 4480 as "activities"
75 //
76 static const char * const ExtendedNames[] = {
77   "UnknownExtended",
78   "Appointment",
79   "Away",
80   "Breakfast",
81   "Busy",
82   "Dinner",
83   "Holiday",
84   "InTransit",
85   "LookingForWork",
86   "Lunch",
87   "Meal",
88   "Meeting",
89   "OnThePhone",
90   "Other",
91   "Performance",
92   "PermanentAbsence",
93   "Playing",
94   "Presentation",
95   "Shopping",
96   "Sleeping",
97   "Spectator",
98   "Steering",
99   "Travel",
100   "TV",
101   "Vacation",
102   "Working",
103   "Worship"
104 };
105 
AsString(State state)106 PString OpalPresenceInfo::AsString(State state)
107 {
108   if (state >= OpalPresenceInfo::InternalError) {
109     PINDEX index = state - OpalPresenceInfo::InternalError;
110     if (index < PARRAYSIZE(BasicNames))
111       return BasicNames[index];
112   }
113 
114   if (state >= OpalPresenceInfo::ExtendedBase) {
115     PINDEX index = state - OpalPresenceInfo::ExtendedBase;
116     if (index < PARRAYSIZE(ExtendedNames))
117       return ExtendedNames[index];
118   }
119 
120   PStringStream strm;
121   strm << "Presence<" << (unsigned)state << '>';
122   return strm;
123 }
124 
125 
FromString(const PString & stateString)126 OpalPresenceInfo::State OpalPresenceInfo::FromString(const PString & stateString)
127 {
128   if (stateString.IsEmpty() || (stateString *= "Unchanged"))
129     return Unchanged;
130 
131   if (stateString *= "Available")
132     return Available;
133 
134   if (stateString *= "Unavailable")
135     return Unavailable;
136 
137   if ((stateString *= "Invisible") ||
138       (stateString *= "Offline") ||
139       (stateString *= "NoPresence"))
140     return NoPresence;
141 
142   for (size_t k = 0; k < sizeof(ExtendedNames)/sizeof(ExtendedNames[0]); ++k) {
143     if (stateString *= ExtendedNames[k])
144       return (State)(ExtendedBase + k);
145   }
146 
147   return InternalError;
148 }
149 
150 
Compare(const PObject & obj) const151 PObject::Comparison OpalPresenceInfo::Compare(const PObject & obj) const
152 {
153   const OpalPresenceInfo & other = dynamic_cast<const OpalPresenceInfo &>(obj);
154   if (m_entity < other.m_entity)
155     return LessThan;
156   if (m_entity > other.m_entity)
157     return GreaterThan;
158 
159   if (m_target < other.m_target)
160     return LessThan;
161   if (m_target > other.m_target)
162     return GreaterThan;
163 
164   if (m_when < other.m_when)
165     return LessThan;
166   if (m_when > other.m_when)
167     return GreaterThan;
168 
169   return EqualTo;
170 }
171 
172 
173 ///////////////////////////////////////////////////////////////////////
174 
OpalPresentity()175 OpalPresentity::OpalPresentity()
176   : m_manager(NULL)
177   , m_temporarilyUnavailable(false)
178   , m_localState(OpalPresenceInfo::NoPresence)
179 {
180 }
181 
182 
OpalPresentity(const OpalPresentity & other)183 OpalPresentity::OpalPresentity(const OpalPresentity & other)
184   : PSafeObject(other)
185   , m_manager(other.m_manager)
186   , m_attributes(other.m_attributes)
187   , m_temporarilyUnavailable(false)
188   , m_localState(OpalPresenceInfo::NoPresence)
189 {
190 }
191 
192 
~OpalPresentity()193 OpalPresentity::~OpalPresentity()
194 {
195 }
196 
197 
SetAOR(const PURL & aor)198 void OpalPresentity::SetAOR(const PURL & aor)
199 {
200   m_aor = aor;
201 }
202 
203 
Create(OpalManager & manager,const PURL & url,const PString & scheme)204 OpalPresentity * OpalPresentity::Create(OpalManager & manager, const PURL & url, const PString & scheme)
205 {
206   OpalPresentity * presEntity = PFactory<OpalPresentity>::CreateInstance(scheme.IsEmpty() ? url.GetScheme() : scheme);
207   if (presEntity == NULL)
208     return NULL;
209 
210   presEntity->m_manager = &manager;
211   presEntity->SetAOR(url);
212 
213   return presEntity;
214 }
215 
216 
Open()217 bool OpalPresentity::Open()
218 {
219   if (m_open.TestAndSet(true))
220     return false; // Already open
221 
222   PTRACE(3, "OpalPres\t'" << m_aor << "' opening.");
223   return true;
224 }
225 
226 
Close()227 bool OpalPresentity::Close()
228 {
229   if (!m_open.TestAndSet(false))
230     return false; // Aleady closed
231 
232   PTRACE(3, "OpalPres\t'" << m_aor << "' closing.");
233   return true;
234 }
235 
236 
SubscribeToPresence(const PURL & presentity,bool subscribe,const PString & note)237 bool OpalPresentity::SubscribeToPresence(const PURL & presentity, bool subscribe, const PString & note)
238 {
239   if (!IsOpen())
240     return false;
241 
242   OpalSubscribeToPresenceCommand * cmd = CreateCommand<OpalSubscribeToPresenceCommand>();
243   if (cmd == NULL)
244     return false;
245 
246   cmd->m_presentity = presentity;
247   cmd->m_subscribe  = subscribe;
248   cmd->m_note       = note;
249   SendCommand(cmd);
250   return true;
251 }
252 
253 
UnsubscribeFromPresence(const PURL & presentity)254 bool OpalPresentity::UnsubscribeFromPresence(const PURL & presentity)
255 {
256   return SubscribeToPresence(presentity, false);
257 }
258 
259 
SetPresenceAuthorisation(const PURL & presentity,Authorisation authorisation)260 bool OpalPresentity::SetPresenceAuthorisation(const PURL & presentity, Authorisation authorisation)
261 {
262   if (!IsOpen())
263     return false;
264 
265   OpalAuthorisationRequestCommand * cmd = CreateCommand<OpalAuthorisationRequestCommand>();
266   if (cmd == NULL)
267     return false;
268 
269   cmd->m_presentity = presentity;
270   cmd->m_authorisation = authorisation;
271   SendCommand(cmd);
272   return true;
273 }
274 
275 
SetLocalPresence(OpalPresenceInfo::State state,const PString & note)276 bool OpalPresentity::SetLocalPresence(OpalPresenceInfo::State state, const PString & note)
277 {
278   if (!IsOpen())
279     return false;
280 
281   m_localState     = state;
282   m_localStateNote = note;
283 
284   OpalSetLocalPresenceCommand * cmd = CreateCommand<OpalSetLocalPresenceCommand>();
285   if (cmd == NULL)
286     return false;
287 
288   cmd->m_state = state;
289   cmd->m_note = note;
290   SendCommand(cmd);
291   return true;
292 }
293 
GetLocalPresence(OpalPresenceInfo::State & state,PString & note)294 bool OpalPresentity::GetLocalPresence(OpalPresenceInfo::State & state, PString & note)
295 {
296   if (!IsOpen())
297     return false;
298 
299   state = m_localState;
300   note  = m_localStateNote;
301 
302   return true;
303 }
304 
SendMessageTo(const OpalIM & message)305 bool OpalPresentity::SendMessageTo(const OpalIM & message)
306 {
307   if (!IsOpen())
308     return false;
309 
310   OpalSendMessageToCommand * cmd = CreateCommand<OpalSendMessageToCommand>();
311   if (cmd == NULL)
312     return false;
313 
314   cmd->m_message = message;
315   SendCommand(cmd);
316   return true;
317 }
318 
319 
OnReceivedMessage(const OpalIM & message)320 void OpalPresentity::OnReceivedMessage(const OpalIM & message)
321 {
322   PWaitAndSignal mutex(m_notificationMutex);
323 
324   if (!m_onReceivedMessageNotifier.IsNULL())
325     m_onReceivedMessageNotifier(*this, message);
326 }
327 
328 
SetReceivedMessageNotifier(const ReceivedMessageNotifier & notifier)329 void OpalPresentity::SetReceivedMessageNotifier(const ReceivedMessageNotifier & notifier)
330 {
331   PWaitAndSignal mutex(m_notificationMutex);
332   m_onReceivedMessageNotifier = notifier;
333 }
334 
335 
OnAuthorisationRequest(const AuthorisationRequest & request)336 void OpalPresentity::OnAuthorisationRequest(const AuthorisationRequest & request)
337 {
338   PWaitAndSignal mutex(m_notificationMutex);
339 
340   if (m_onAuthorisationRequestNotifier.IsNULL())
341     SetPresenceAuthorisation(request.m_presentity, AuthorisationPermitted);
342   else
343     m_onAuthorisationRequestNotifier(*this, request);
344 }
345 
346 
SetAuthorisationRequestNotifier(const AuthorisationRequestNotifier & notifier)347 void OpalPresentity::SetAuthorisationRequestNotifier(const AuthorisationRequestNotifier & notifier)
348 {
349   PWaitAndSignal mutex(m_notificationMutex);
350   m_onAuthorisationRequestNotifier = notifier;
351 }
352 
353 
OnPresenceChange(const OpalPresenceInfo & info)354 void OpalPresentity::OnPresenceChange(const OpalPresenceInfo & info)
355 {
356   PWaitAndSignal mutex(m_notificationMutex);
357 
358   if (!m_onPresenceChangeNotifier.IsNULL())
359     m_onPresenceChangeNotifier(*this, info);
360 }
361 
362 
SetPresenceChangeNotifier(const PresenceChangeNotifier & notifier)363 void OpalPresentity::SetPresenceChangeNotifier(const PresenceChangeNotifier & notifier)
364 {
365   PWaitAndSignal mutex(m_notificationMutex);
366 
367   m_onPresenceChangeNotifier = notifier;
368 }
369 
370 
GetBuddyListEx(BuddyList &)371 OpalPresentity::BuddyStatus OpalPresentity::GetBuddyListEx(BuddyList &)
372 {
373   if (!IsOpen())
374     return BuddyStatus_AccountNotLoggedIn;
375 
376   if (m_temporarilyUnavailable)
377     return BuddyStatus_ListTemporarilyUnavailable;
378 
379   return BuddyStatus_ListFeatureNotImplemented;
380 }
381 
382 
SetBuddyListEx(const BuddyList &)383 OpalPresentity::BuddyStatus OpalPresentity::SetBuddyListEx(const BuddyList &)
384 {
385   if (!IsOpen())
386     return BuddyStatus_AccountNotLoggedIn;
387 
388   if (m_temporarilyUnavailable)
389     return BuddyStatus_ListTemporarilyUnavailable;
390 
391   return BuddyStatus_ListFeatureNotImplemented;
392 }
393 
394 
DeleteBuddyListEx()395 OpalPresentity::BuddyStatus OpalPresentity::DeleteBuddyListEx()
396 {
397   if (!IsOpen())
398     return BuddyStatus_AccountNotLoggedIn;
399 
400   if (m_temporarilyUnavailable)
401     return BuddyStatus_ListTemporarilyUnavailable;
402 
403   return BuddyStatus_ListFeatureNotImplemented;
404 }
405 
406 
GetBuddyEx(BuddyInfo & buddy)407 OpalPresentity::BuddyStatus OpalPresentity::GetBuddyEx(BuddyInfo & buddy)
408 {
409   if (!IsOpen())
410     return BuddyStatus_AccountNotLoggedIn;
411 
412   if (buddy.m_presentity.IsEmpty())
413     return BuddyStatus_BadBuddySpecification;
414 
415   if (m_temporarilyUnavailable)
416     return BuddyStatus_ListTemporarilyUnavailable;
417 
418   BuddyList buddies;
419   BuddyStatus status = GetBuddyListEx(buddies);
420   if (status != BuddyStatus_OK)
421     return status;
422 
423   for (BuddyList::iterator it = buddies.begin(); it != buddies.end(); ++it) {
424     if (it->m_presentity == buddy.m_presentity) {
425       buddy = *it;
426       return BuddyStatus_OK;
427     }
428   }
429 
430   return BuddyStatus_SpecifiedBuddyNotFound;
431 }
432 
433 
SetBuddyEx(const BuddyInfo & buddy)434 OpalPresentity::BuddyStatus OpalPresentity::SetBuddyEx(const BuddyInfo & buddy)
435 {
436   if (!IsOpen())
437     return BuddyStatus_AccountNotLoggedIn;
438 
439   if (m_temporarilyUnavailable)
440     return BuddyStatus_ListTemporarilyUnavailable;
441 
442   if (buddy.m_presentity.IsEmpty())
443     return BuddyStatus_BadBuddySpecification;
444 
445   BuddyList buddies;
446   BuddyStatus status = GetBuddyListEx(buddies);
447   if (status != BuddyStatus_OK)
448     return status;
449 
450   buddies.push_back(buddy);
451   return SetBuddyListEx(buddies);
452 }
453 
454 
DeleteBuddyEx(const PURL & presentity)455 OpalPresentity::BuddyStatus OpalPresentity::DeleteBuddyEx(const PURL & presentity)
456 {
457   if (!IsOpen())
458     return BuddyStatus_AccountNotLoggedIn;
459 
460   if (presentity.IsEmpty())
461     return BuddyStatus_BadBuddySpecification;
462 
463   if (m_temporarilyUnavailable)
464     return BuddyStatus_ListTemporarilyUnavailable;
465 
466   BuddyList buddies;
467   BuddyStatus status = GetBuddyListEx(buddies);
468   if (status != BuddyStatus_OK)
469     return status;
470 
471   for (BuddyList::iterator it = buddies.begin(); it != buddies.end(); ++it) {
472     if (it->m_presentity == presentity) {
473       buddies.erase(it);
474       return SetBuddyListEx(buddies);
475     }
476   }
477 
478   return BuddyStatus_SpecifiedBuddyNotFound;
479 }
480 
481 
SubscribeBuddyListEx(PINDEX & successfulCount,bool subscribe)482 OpalPresentity::BuddyStatus OpalPresentity::SubscribeBuddyListEx(PINDEX & successfulCount, bool subscribe)
483 {
484   if (!IsOpen())
485     return BuddyStatus_AccountNotLoggedIn;
486 
487   if (m_temporarilyUnavailable)
488     return BuddyStatus_ListTemporarilyUnavailable;
489 
490   BuddyList buddies;
491   BuddyStatus status = GetBuddyListEx(buddies);
492   if (status != BuddyStatus_OK)
493     return status;
494 
495   successfulCount = 0;
496   for (BuddyList::iterator it = buddies.begin(); it != buddies.end(); ++it) {
497     if (!SubscribeToPresence(it->m_presentity, subscribe))
498       return BuddyStatus_ListSubscribeFailed;
499     ++successfulCount;
500   }
501 
502   return BuddyStatus_OK;
503 }
504 
UnsubscribeBuddyListEx()505 OpalPresentity::BuddyStatus OpalPresentity::UnsubscribeBuddyListEx()
506 {
507   PINDEX successfulCount;
508   return SubscribeBuddyListEx(successfulCount, false);
509 }
510 
511 
SendCommand(OpalPresentityCommand * cmd)512 bool OpalPresentity::SendCommand(OpalPresentityCommand * cmd)
513 {
514   if (cmd == NULL)
515     return false;
516 
517   cmd->Process(*this);
518   delete cmd;
519   return true;
520 }
521 
522 
InternalCreateCommand(const char * cmdName)523 OpalPresentityCommand * OpalPresentity::InternalCreateCommand(const char * cmdName)
524 {
525   PDefaultPFactoryKey partialKey(cmdName);
526   const char * className;
527 
528   for (unsigned ancestor = 0; *(className = GetClass(ancestor)) != '\0'; ++ancestor) {
529     OpalPresentityCommand * cmd = PFactory<OpalPresentityCommand>::CreateInstance(className+partialKey);
530     if (cmd != NULL) {
531       PTRACE(3, "OpalPres\tCreating presentity command '" << className+partialKey << "'");
532       return cmd;
533     }
534   }
535 
536   PAssertAlways(PUnimplementedFunction);
537   return NULL;
538 }
539 
540 
Internal_SendMessageToCommand(const OpalSendMessageToCommand & cmd)541 void OpalPresentity::Internal_SendMessageToCommand(const OpalSendMessageToCommand & cmd)
542 {
543   OpalEndPoint * endpoint = m_manager->FindEndPoint(m_aor.GetScheme());
544   if (endpoint == NULL) {
545     PTRACE(1, "OpalPres\tCannot find endpoint for '" << m_aor.GetScheme() << "'");
546     return;
547   }
548 
549   // need writable message object
550   OpalIM msg(cmd.m_message);
551 
552   if (msg.m_from.IsEmpty())
553     msg.m_from = m_aor;
554 
555   endpoint->Message(msg);
556 }
557 
558 
559 OPAL_DEFINE_COMMAND(OpalSendMessageToCommand, OpalPresentity, Internal_SendMessageToCommand);
560 
561 
562 /////////////////////////////////////////////////////////////////////////////
563 
OpalPresentityWithCommandThread()564 OpalPresentityWithCommandThread::OpalPresentityWithCommandThread()
565   : m_threadRunning(false)
566   , m_queueRunning(false)
567   , m_thread(NULL)
568 {
569 }
570 
571 
OpalPresentityWithCommandThread(const OpalPresentityWithCommandThread & other)572 OpalPresentityWithCommandThread::OpalPresentityWithCommandThread(
573                            const OpalPresentityWithCommandThread & other)
574   : OpalPresentity(other)
575   , m_threadRunning(false)
576   , m_queueRunning(false)
577   , m_thread(NULL)
578 {
579 }
580 
581 
~OpalPresentityWithCommandThread()582 OpalPresentityWithCommandThread::~OpalPresentityWithCommandThread()
583 {
584   StopThread();
585 
586   while (!m_commandQueue.empty()) {
587     delete m_commandQueue.front();
588     m_commandQueue.pop();
589   }
590 }
591 
592 
StartThread(bool startQueue)593 void OpalPresentityWithCommandThread::StartThread(bool startQueue)
594 {
595   if (m_threadRunning)
596     return;
597 
598   // start handler thread
599   m_threadRunning = true;
600   m_queueRunning  = startQueue;
601   m_thread = new PThreadObj<OpalPresentityWithCommandThread>(*this, &OpalPresentityWithCommandThread::ThreadMain);
602 }
603 
StartQueue(bool startQueue)604 void OpalPresentityWithCommandThread::StartQueue(bool startQueue)
605 {
606   if (m_threadRunning) {
607     m_queueRunning = startQueue;
608     m_commandQueueSync.Signal();
609   }
610 }
611 
StopThread()612 void OpalPresentityWithCommandThread::StopThread()
613 {
614   if (m_threadRunning && m_thread != NULL) {
615     PTRACE(4, "OpalPres\tStopping command thread " << *m_thread);
616     m_threadRunning = false;
617     m_commandQueueSync.Signal();
618     PAssert(m_thread->WaitForTermination(5000), "Could not terminate presentity command thread");
619     delete m_thread;
620     m_thread = NULL;
621   }
622 }
623 
624 
SendCommand(OpalPresentityCommand * cmd)625 bool OpalPresentityWithCommandThread::SendCommand(OpalPresentityCommand * cmd)
626 {
627   if (!m_threadRunning) {
628     delete cmd;
629     return false;
630   }
631 
632   {
633     PWaitAndSignal m(m_commandQueueMutex);
634     cmd->m_sequence = ++m_commandSequence;
635     m_commandQueue.push(cmd);
636   }
637 
638   m_commandQueueSync.Signal();
639 
640   return true;
641 }
642 
643 
644 
ThreadMain()645 void OpalPresentityWithCommandThread::ThreadMain()
646 {
647   PTRACE(4, "OpalPres\tCommand thread started");
648 
649   while (m_threadRunning) {
650     if (m_queueRunning) {
651       OpalPresentityCommand * cmd = NULL;
652 
653       {
654         PWaitAndSignal mutex(m_commandQueueMutex);
655         if (!m_commandQueue.empty()) {
656           cmd = m_commandQueue.front();
657           m_commandQueue.pop();
658         }
659       }
660 
661       if (cmd != NULL) {
662         cmd->Process(*this);
663         delete cmd;
664       }
665     }
666 
667     m_commandQueueSync.Wait(1000);
668   }
669 
670   PTRACE(4, "OpalPres\tCommand thread ended");
671 }
672 
673 /////////////////////////////////////////////////////////////////////////////
674