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