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