1 /*
2  * main.cxx
3  *
4  * OPAL application source file for testing Opal presentities
5  *
6  * Copyright (c) 2009 Post Increment
7  *
8  * The contents of this file are subject to the Mozilla Public License
9  * Version 1.0 (the "License"); you may not use this file except in
10  * compliance with the License. You may obtain a copy of the License at
11  * http://www.mozilla.org/MPL/
12  *
13  * Software distributed under the License is distributed on an "AS IS"
14  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15  * the License for the specific language governing rights and limitations
16  * under the License.
17  *
18  * The Original Code is Open Phone Abstraction Library.
19  *
20  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
21  *
22  * Contributor(s): ______________________________________.
23  *
24  * $Revision: 25999 $
25  * $Author: rjongbloed $
26  * $Date: 2011-06-13 21:45:30 -0500 (Mon, 13 Jun 2011) $
27  */
28 
29 #include <ptlib.h>
30 
31 #include <ptclib/cli.h>
32 #include <sip/sippres.h>
33 
34 #if P_EXPAT
35 #else
36 #error Cannot compile Presentity test program without XML support!
37 #endif
38 
39 #if OPAL_SIP
40 #else
41 #error Cannot compile Presentity test program without SIP support!
42 #endif
43 
44 
45 //////////////////////////////////////////////////////////////
46 
47 class MyManager : public OpalManager
48 {
49   PCLASSINFO(MyManager, OpalManager)
50   public:
51 };
52 
53 
54 class TestPresEnt : public PProcess
55 {
56   PCLASSINFO(TestPresEnt, PProcess)
57 
58   public:
59     TestPresEnt();
60     ~TestPresEnt();
61 
62     PDECLARE_AuthorisationRequestNotifier(TestPresEnt, AuthorisationRequest);
63     PDECLARE_PresenceChangeNotifier(TestPresEnt, PresenceChange);
64 
65     virtual void Main();
66 
67     void AddPresentity(PArgList & args);
68 
69   private:
70     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdCreate);
71     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdList);
72     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdSubscribeToPresence);
73     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdUnsubscribeToPresence);
74     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdPresenceAuthorisation);
75     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdSetLocalPresence);
76     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdBuddyList);
77     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdBuddyAdd);
78     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdBuddyRemove);
79     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdBuddySusbcribe);
80     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdDelay);
81     PDECLARE_NOTIFIER(PCLI::Arguments, TestPresEnt, CmdQuit);
82 
83     MyManager * m_manager;
84     PString     m_presenceAgent;
85     PString     m_xcapRoot;
86     PString     m_xcapAuthID;
87     PString     m_xcapPassword;
88     PDictionary<PString, OpalPresentity> m_presentities;
89 };
90 
91 
92 PCREATE_PROCESS(TestPresEnt);
93 
TestPresEnt()94 TestPresEnt::TestPresEnt()
95   : PProcess("OPAL Test Presentity", "TestPresEnt", OPAL_MAJOR, OPAL_MINOR, ReleaseCode, OPAL_BUILD)
96   , m_manager(NULL)
97 {
98   m_presentities.DisallowDeleteObjects();
99 }
100 
101 
~TestPresEnt()102 TestPresEnt::~TestPresEnt()
103 {
104   m_presentities.RemoveAll();
105   delete m_manager;
106 }
107 
108 
AddPresentity(PArgList & args)109 void TestPresEnt::AddPresentity(PArgList & args)
110 {
111   PSafePtr<OpalPresentity> presentity = m_manager->AddPresentity(args[0]);
112   if (presentity == NULL) {
113     cerr << "error: cannot create presentity for \"" << args[0] << '"' << endl;
114     return;
115   }
116 
117   presentity->GetAttributes().Set(SIP_Presentity::SubProtocolKey,
118             args.GetOptionString('s', !m_xcapRoot || !m_xcapAuthID || !m_xcapPassword ? "OMA" : "Agent"));
119 
120   presentity->SetAuthorisationRequestNotifier(PCREATE_AuthorisationRequestNotifier(AuthorisationRequest));
121   presentity->SetPresenceChangeNotifier(PCREATE_PresenceChangeNotifier(PresenceChange));
122 
123   presentity->GetAttributes().Set(OpalPresentity::TimeToLiveKey,      "1200");
124   if (args.HasOption('a'))
125     presentity->GetAttributes().Set(SIP_Presentity::AuthNameKey,      args.GetOptionString('a'));
126   if (args.HasOption('p'))
127     presentity->GetAttributes().Set(SIP_Presentity::AuthPasswordKey,  args.GetOptionString('p'));
128   if (!m_presenceAgent.IsEmpty())
129     presentity->GetAttributes().Set(SIP_Presentity::PresenceAgentKey, m_presenceAgent);
130   presentity->GetAttributes().Set(SIP_Presentity::XcapRootKey,        m_xcapRoot);
131   if (!m_xcapAuthID)
132     presentity->GetAttributes().Set(SIP_Presentity::XcapAuthIdKey,    m_xcapAuthID);
133   if (!m_xcapPassword)
134     presentity->GetAttributes().Set(SIP_Presentity::XcapPasswordKey,  m_xcapPassword);
135 
136   if (presentity->Open()) {
137     m_presentities.SetAt(psprintf("#%u", m_presentities.GetSize()/2+1),    presentity);
138     m_presentities.SetAt(PCaselessString(presentity->GetAOR().AsString()), presentity);
139   }
140   else {
141     cerr << "error: cannot open presentity \"" << args[0] << '"' << endl;
142     m_manager->RemovePresentity(args[0]);
143   }
144 }
145 
146 
Main()147 void TestPresEnt::Main()
148 {
149   PArgList & args = GetArguments();
150 #define URL_OPTIONS "a-auth-id:" \
151                     "p-password:" \
152                     "s-sub-protocol:"
153   args.Parse("h-help."
154              "f-file:"
155              "L-listener:"
156              "P-presence-agent:"
157 #if PTRACING
158              "o-output:"
159 #endif
160              "S-stun:"
161              "T-translation:"
162 #if PTRACING
163              "t-trace."
164 #endif
165              "X-xcap-server:"
166              "-xcap-auth-id:"
167              "-xcap-password:"
168              "-proxy:"
169              URL_OPTIONS);
170 
171 #if PTRACING
172   PTrace::Initialise(args.GetOptionCount('t'),
173                      args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL,
174          PTrace::Blocks | PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine);
175 #endif
176 
177   if (args.HasOption('h')) {
178     cerr << "usage: " << GetFile().GetTitle() << " [ global-options ] { [ url-options ] url } ...\n"
179             "\n"
180             "Available global options are:\n"
181             "  -proxy URL                   : set outbound proxy.\n"
182             "  -f or --file filename        : name of script file to execute.\n"
183             "  -L or --listener addr        : set listener address:port.\n"
184             "  -T or --translation addr     : set NAT translation address.\n"
185             "  -S or --stun addr            : set STUN server address.\n"
186             "  -P or --presence-agent addr  : set presence agent default address.\n"
187             "  -X or --xcap-root url        : set XCAP server root URL.\n"
188             "        --xcap-auth-id name    : set XCAP server authorisation ID.\n"
189             "        --xcap-password pwd    : set XCAP server authorisation password.\n"
190 #if PTRACING
191             "  -o or --output file          : file name for output of log messages\n"
192             "  -t or --trace                : degree of verbosity in error log (more times for more detail)\n"
193 #endif
194             "  -h or --help                 : print this help message.\n"
195             "\n"
196             "Available URL options are:\n"
197 #define URL_HELP \
198             "  -a or --auth-id              : Authorisation ID, default to URL username.\n" \
199             "  -p or --password             : Authorisation password.\n" \
200             "  -s or --sub-protocol proto   : set sub-protocol, one of PeerToPeer, Agent, XCAP or OMA\n"
201             URL_HELP
202             "\n"
203             "e.g. " << GetFile().GetTitle() << " -X http://xcap.bloggs.com -p passone sip:fred1@bloggs.com -p passtwo sip:fred2@bloggs.com\n"
204             ;
205     return;
206   }
207 
208   m_manager = new MyManager();
209 
210   if (args.HasOption('T'))
211     m_manager->SetTranslationHost(args.GetOptionString('T'));
212 
213   PString listeners = args.GetOptionString('L');
214 
215   if (args.HasOption('S')) {
216     cout << "Executing STUN lookup..." << flush;
217     m_manager->SetSTUNServer(args.GetOptionString('S'));
218     PSTUNClient * stun = m_manager->GetSTUNClient();
219     if (stun != NULL)
220       cout << stun->GetNatTypeName();
221     else
222       cout << "(unknown)";
223     cout << "...done" << endl;
224     if ((stun != NULL) && (stun->GetNatType() != PSTUNClient::OpenNat))
225       listeners = "udp$*:*";
226   }
227 
228   m_presenceAgent = args.GetOptionString('P');
229   m_xcapRoot = args.GetOptionString('X');
230   m_xcapAuthID = args.GetOptionString("xcap-auth-id");
231   m_xcapPassword = args.GetOptionString("xcap-password");
232 
233   SIPEndPoint * sip  = new SIPEndPoint(*m_manager);
234   if (!sip->StartListeners(listeners)) {
235     cerr << "Could not start SIP listeners." << endl;
236     return;
237   }
238 
239   if (args.HasOption("proxy"))
240     sip->SetProxy(args.GetOptionString("proxy"));
241 
242   PTextFile scriptFile;
243   if (args.HasOption('f') && !scriptFile.Open(args.GetOptionString('f'))) {
244     cerr << "error: cannot open script file '" << args.GetOptionString('f') << "'" << endl;
245     return;
246   }
247 
248   if (args.GetCount() != 0) {
249     do {
250       AddPresentity(args);
251     } while (args.Parse(URL_OPTIONS));
252 
253     if (m_presentities.GetSize() == 0) {
254       cerr << "error: no presentities available" << endl;
255       return;
256     }
257   }
258 
259   PCLIStandard cli;
260   cli.SetPrompt("PRES> ");
261   cli.SetCommand("create", PCREATE_NOTIFIER(CmdCreate),
262                  "Create presentity.",
263                  "[ -a -p -s ] <url>\n" URL_HELP);
264   cli.SetCommand("list", PCREATE_NOTIFIER(CmdList),
265                  "List presentities.");
266   cli.SetCommand("subscribe", PCREATE_NOTIFIER(CmdSubscribeToPresence),
267                  "Subscribe to presence state for presentity.",
268                  "<url-watcher> <url-watched>");
269   cli.SetCommand("unsubscribe", PCREATE_NOTIFIER(CmdUnsubscribeToPresence),
270                  "Subscribe to presence state for presentity.",
271                  "<url-watcher> <url-watched>");
272   cli.SetCommand("authorise", PCREATE_NOTIFIER(CmdPresenceAuthorisation),
273                  "Authorise a presentity to see local presence.",
274                  "<url-watched> <url-watcher> [ deny | deny-politely | remove ]");
275   cli.SetCommand("publish", PCREATE_NOTIFIER(CmdSetLocalPresence),
276                  "Publish local presence state for presentity.",
277                  "<url> { available | unavailable | busy } [ <note> ]");
278   cli.SetCommand("buddy list\nshow buddies", PCREATE_NOTIFIER(CmdBuddyList),
279                  "Show buddy list for presentity.",
280                  "<presentity>");
281   cli.SetCommand("buddy add\nadd buddy", PCREATE_NOTIFIER(CmdBuddyAdd),
282                  "Add buddy to list for presentity.",
283                  "<presentity> <url-buddy> <display-name>");
284   cli.SetCommand("buddy remove\ndel buddy", PCREATE_NOTIFIER(CmdBuddyRemove),
285                  "Delete buddy from list for presentity.",
286                  "<presentity> <url-buddy>");
287   cli.SetCommand("buddy subscribe", PCREATE_NOTIFIER(CmdBuddySusbcribe),
288                  "Susbcribe to all URIs in the buddy list for presentity.",
289                  "<presentity>");
290   cli.SetCommand("delay", PCREATE_NOTIFIER(CmdDelay),
291                   "Delay for n seconds",
292                   "secs");
293   cli.SetCommand("quit\nq\nexit", PCREATE_NOTIFIER(CmdQuit),
294                   "Quit command line interpreter, note quitting from console also shuts down application.");
295 
296   // create the foreground context
297   PCLI::Context * cliContext = cli.StartForeground();
298   if (cliContext == NULL)
299     return;
300 
301   // if there is a script file, process commands
302   if (scriptFile.IsOpen()) {
303     cout << "Running script '" << scriptFile.GetFilePath() << "'" << endl;
304     for (;;) {
305       PString line;
306       if (!scriptFile.ReadLine(line))
307         break;
308       line = line.Trim();
309       if (line.GetLength() > 0) {
310         if ((line[0] != '#') && (line[0] != ';') && ((line.GetLength() < 2) || (line[0] != '/') || (line[1] != '/'))) {
311           cout << line << endl;
312           if (!cliContext->ProcessInput(line)) {
313             cerr << "error: error occurred while processing script" << endl;
314             return;
315           }
316         }
317       }
318     }
319     cout << "Script complete" << endl;
320   }
321 
322   scriptFile.Close();
323 
324   cli.RunContext(cliContext); // Do not spawn thread, wait till end of input
325 
326   cout << "\nExiting ..." << endl;
327 }
328 
329 
CmdCreate(PCLI::Arguments & args,INT)330 void TestPresEnt::CmdCreate(PCLI::Arguments & args, INT)
331 {
332   if (!args.Parse(URL_OPTIONS))
333     args.WriteUsage();
334   else if (m_presentities.Contains(args[0]))
335     args.WriteError() << "Presentity \"" << args[0] << "\" already exists." << endl;
336   else
337     AddPresentity(args);
338 }
339 
340 
CmdList(PCLI::Arguments & args,INT)341 void TestPresEnt::CmdList(PCLI::Arguments & args, INT)
342 {
343   PINDEX i;
344   for (i = 0; i < m_presentities.GetSize(); ++i) {
345     PString key = m_presentities.GetKeyAt(i);
346     OpalPresentity & presentity = m_presentities[key];
347     OpalPresenceInfo::State state;
348     PString note;
349     presentity.GetLocalPresence(state, note);
350     cout << key << " "
351          << presentity.GetAOR() << " "
352          << OpalPresenceInfo::AsString(state) << " "
353          << note
354          << endl;
355   }
356 }
357 
CmdSubscribeToPresence(PCLI::Arguments & args,INT)358 void TestPresEnt::CmdSubscribeToPresence(PCLI::Arguments & args, INT)
359 {
360   if (args.GetCount() < 2)
361     args.WriteUsage();
362   else if (!m_presentities.Contains(args[0]))
363     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
364   else
365     m_presentities[args[0]].SubscribeToPresence(args[1], true, "This is test presentity");
366 }
367 
368 
CmdUnsubscribeToPresence(PCLI::Arguments & args,INT)369 void TestPresEnt::CmdUnsubscribeToPresence(PCLI::Arguments & args, INT)
370 {
371   if (args.GetCount() < 2)
372     args.WriteUsage();
373   else if (!m_presentities.Contains(args[0]))
374     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
375   else
376     m_presentities[args[0]].UnsubscribeFromPresence(args[1]);
377 }
378 
379 
CmdPresenceAuthorisation(PCLI::Arguments & args,INT)380 void TestPresEnt::CmdPresenceAuthorisation(PCLI::Arguments & args, INT)
381 {
382   OpalPresentity::Authorisation auth = OpalPresentity::AuthorisationPermitted;
383   if (args.GetCount() > 2) {
384     if (args[2] *= "deny")
385       auth = OpalPresentity::AuthorisationDenied;
386     else if (args[2] *= "deny-politely")
387       auth = OpalPresentity::AuthorisationDeniedPolitely;
388     else if (args[2] *= "remove")
389       auth = OpalPresentity::AuthorisationRemove;
390     else {
391       args.WriteUsage();
392       return;
393     }
394   }
395 
396   if (args.GetCount() < 2)
397     args.WriteUsage();
398   else if (!m_presentities.Contains(args[0]))
399     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
400   else
401     m_presentities[args[0]].SetPresenceAuthorisation(args[1], auth);
402 }
403 
404 
CmdSetLocalPresence(PCLI::Arguments & args,INT)405 void TestPresEnt::CmdSetLocalPresence(PCLI::Arguments & args, INT)
406 {
407   PString note;
408   if (args.GetCount() > 2)
409     note = args[2];
410 
411   if (args.GetCount() < 2)
412     args.WriteUsage();
413   else if (!m_presentities.Contains(args[0]))
414     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
415   else if (args[1] *= "available")
416     m_presentities[args[0]].SetLocalPresence(OpalPresenceInfo::Available, note);
417   else if (args[1] *= "unavailable")
418     m_presentities[args[0]].SetLocalPresence(OpalPresenceInfo::NoPresence, note);
419   else if (args[1] *= "busy")
420     m_presentities[args[0]].SetLocalPresence(OpalPresenceInfo::Busy, note);
421   else
422     args.WriteUsage();
423 }
424 
425 
CmdBuddyList(PCLI::Arguments & args,INT)426 void TestPresEnt::CmdBuddyList(PCLI::Arguments & args, INT)
427 {
428   if (args.GetCount() < 1)
429     args.WriteUsage();
430   else if (!m_presentities.Contains(args[0]))
431     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
432   else {
433     OpalPresentity::BuddyList buddies;
434     if (!m_presentities[args[0]].GetBuddyList(buddies))
435       args.WriteError() << "Presentity \"" << args[0] << "\" does not support buddy lists." << endl;
436     else if (buddies.empty())
437       args.GetContext() << "Presentity \"" << args[0] << "\" has no buddies." << endl;
438     else {
439       for (OpalPresentity::BuddyList::iterator it = buddies.begin(); it != buddies.end(); ++it)
440         args.GetContext() << it->m_presentity << "\t\"" << it->m_displayName << '"' << endl;
441     }
442   }
443 }
444 
445 
CmdBuddyAdd(PCLI::Arguments & args,INT)446 void TestPresEnt::CmdBuddyAdd(PCLI::Arguments & args, INT)
447 {
448   if (args.GetCount() < 2)
449     args.WriteUsage();
450   else if (!m_presentities.Contains(args[0]))
451     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
452   else {
453     OpalPresentity::BuddyInfo buddy(args[1]);
454     for (PINDEX arg = 2; arg < args.GetCount(); ++arg)
455       buddy.m_displayName &= args[arg];
456     if (!m_presentities[args[0]].SetBuddy(buddy))
457       args.WriteError() << "Presentity \"" << args[0] << "\" does not have a buddy list." << endl;
458   }
459 }
460 
461 
CmdBuddyRemove(PCLI::Arguments & args,INT)462 void TestPresEnt::CmdBuddyRemove(PCLI::Arguments & args, INT)
463 {
464   if (args.GetCount() < 2)
465     args.WriteUsage();
466   else if (!m_presentities.Contains(args[0]))
467     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
468   else if (!m_presentities[args[0]].DeleteBuddy(args[1]))
469     args.WriteError() << "Could not delete \"" << args[1] << "\" for presentity \"" << args[0] << '"' << endl;
470 }
471 
472 
CmdBuddySusbcribe(PCLI::Arguments & args,INT)473 void TestPresEnt::CmdBuddySusbcribe(PCLI::Arguments & args, INT)
474 {
475   if (args.GetCount() < 1)
476     args.WriteUsage();
477   else if (!m_presentities.Contains(args[0]))
478     args.WriteError() << "Presentity \"" << args[0] << "\" does not exist." << endl;
479   else if (!m_presentities[args[0]].SubscribeBuddyList())
480     args.WriteError() << "Could not subscribe all buddies for presentity \"" << args[0] << '"' << endl;
481 }
482 
483 
CmdDelay(PCLI::Arguments & args,INT)484 void TestPresEnt::CmdDelay(PCLI::Arguments & args, INT)
485 {
486   if (args.GetCount() < 1)
487     args.WriteUsage();
488   int delay = args[0].AsInteger();
489   PThread::Sleep(delay * 1000);
490 }
491 
492 
CmdQuit(PCLI::Arguments & args,INT)493 void TestPresEnt::CmdQuit(PCLI::Arguments & args, INT)
494 {
495   args.GetContext().Stop();
496 }
497 
498 
AuthorisationRequest(OpalPresentity & presentity,const OpalPresentity::AuthorisationRequest & request)499 void TestPresEnt::AuthorisationRequest(OpalPresentity & presentity, const OpalPresentity::AuthorisationRequest & request)
500 {
501   cout << request.m_presentity << " requesting access to presence for " << presentity.GetAOR() << endl;
502   if (!request.m_note.IsEmpty())
503     cout << "  \"" << request.m_note << '"' << endl;
504 }
505 
506 
PresenceChange(OpalPresentity & presentity,const OpalPresenceInfo & info)507 void TestPresEnt::PresenceChange(OpalPresentity & presentity, const OpalPresenceInfo & info)
508 {
509   cout << "Presentity " << presentity.GetAOR();
510   if (info.m_entity != info.m_target)
511     cout << " received presence change from " << info.m_entity;
512   else
513     cout << " changed locally";
514   cout << " to " << info.AsString() << endl;
515 }
516 
517 
518 
519 // End of File ///////////////////////////////////////////////////////////////
520