/[resiprocate]/main/resip/recon/test/testUA.cxx
ViewVC logotype

Contents of /main/resip/recon/test/testUA.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11042 - (show annotations) (download)
Wed Mar 19 12:43:36 2014 UTC (5 years, 3 months ago) by Dpocock
File MIME type: text/plain
File size: 59826 byte(s)
resip/recon: testUA: remove duplicate definition of sleepSeconds()
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <signal.h>
6 #ifdef WIN32
7 #include <conio.h>
8 #else
9 /**
10 Linux (POSIX) implementation of _kbhit().
11 Morgan McGuire, morgan@cs.brown.edu
12 */
13 #include <stdio.h>
14 #include <sys/select.h>
15 #include <termios.h>
16 #ifndef __GNUC__
17 #include <stropts.h>
18 #endif
19 #include <sys/ioctl.h>
20
21 int _kbhit() {
22 static const int STDIN = 0;
23 static bool initialized = false;
24
25 if (! initialized) {
26 // Use termios to turn off line buffering
27 termios term;
28 tcgetattr(STDIN, &term);
29 term.c_lflag &= ~ICANON;
30 tcsetattr(STDIN, TCSANOW, &term);
31 setbuf(stdin, NULL);
32 initialized = true;
33 }
34
35 int bytesWaiting;
36 ioctl(STDIN, FIONREAD, &bytesWaiting);
37 return bytesWaiting;
38 }
39 #endif
40
41 #include "../UserAgent.hxx"
42 #include "../ReconSubsystem.hxx"
43
44 #include <os/OsSysLog.h>
45
46 // Test Prompts for cache testing
47 #include "playback_prompt.h"
48 #include "record_prompt.h"
49
50 #include <rutil/Log.hxx>
51 #include <rutil/Logger.hxx>
52 #include <rutil/DnsUtil.hxx>
53 #include <rutil/BaseException.hxx>
54 #include <rutil/Time.hxx>
55 #include <rutil/WinLeakCheck.hxx>
56
57 using namespace recon;
58 using namespace resip;
59 using namespace std;
60
61 #define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
62
63 static bool finished = false;
64 NameAddr uri("sip:noreg@127.0.0.1");
65 bool autoAnswerEnabled = false; // If enabled then testUA will automatically answer incoming calls by adding to lowest numbered conversation
66 SharedPtr<ConversationProfile> conversationProfile;
67
68 static void
69 signalHandler(int signo)
70 {
71 std::cerr << "Shutting down" << endl;
72 finished = true;
73 }
74
75 class MyUserAgent : public UserAgent
76 {
77 public:
78 MyUserAgent(ConversationManager* conversationManager, SharedPtr<UserAgentMasterProfile> profile) :
79 UserAgent(conversationManager, profile) {}
80
81 virtual void onApplicationTimer(unsigned int id, unsigned int durationMs, unsigned int seq)
82 {
83 InfoLog(<< "onApplicationTimeout: id=" << id << " dur=" << durationMs << " seq=" << seq);
84 }
85
86 virtual void onSubscriptionTerminated(SubscriptionHandle handle, unsigned int statusCode)
87 {
88 InfoLog(<< "onSubscriptionTerminated: handle=" << handle << " statusCode=" << statusCode);
89 }
90
91 virtual void onSubscriptionNotify(SubscriptionHandle handle, Data& notifyData)
92 {
93 InfoLog(<< "onSubscriptionNotify: handle=" << handle << " data=" << endl << notifyData);
94 }
95 };
96
97 class MyConversationManager : public ConversationManager
98 {
99 public:
100
101 MyConversationManager(bool localAudioEnabled)
102 : ConversationManager(localAudioEnabled),
103 mLocalAudioEnabled(localAudioEnabled)
104 {
105 };
106
107 virtual void startup()
108 {
109 if(mLocalAudioEnabled)
110 {
111 // Create initial local participant and conversation
112 addParticipant(createConversation(), createLocalParticipant());
113 resip::Uri uri("tone:dialtone;duration=1000");
114 createMediaResourceParticipant(mConversationHandles.front(), uri);
115 }
116 else
117 {
118 // If no local audio - just create a starter conversation
119 createConversation();
120 }
121
122 // Load 2 items into cache for testing
123 {
124 resip::Data buffer(Data::Share, (const char*)playback_prompt, sizeof(playback_prompt));
125 resip::Data name("playback");
126 addBufferToMediaResourceCache(name, buffer, 0);
127 }
128 {
129 resip::Data buffer(Data::Share, (const char *)record_prompt, sizeof(record_prompt));
130 resip::Data name("record");
131 addBufferToMediaResourceCache(name, buffer, 0);
132 }
133 }
134
135
136 virtual ConversationHandle createConversation()
137 {
138 ConversationHandle convHandle = ConversationManager::createConversation();
139 mConversationHandles.push_back(convHandle);
140 return convHandle;
141 }
142
143 virtual ParticipantHandle createRemoteParticipant(ConversationHandle convHandle, NameAddr& destination, ParticipantForkSelectMode forkSelectMode = ForkSelectAutomatic)
144 {
145 ParticipantHandle partHandle = ConversationManager::createRemoteParticipant(convHandle, destination, forkSelectMode);
146 mRemoteParticipantHandles.push_back(partHandle);
147 return partHandle;
148 }
149
150 virtual ParticipantHandle createMediaResourceParticipant(ConversationHandle convHandle, const Uri& mediaUrl)
151 {
152 ParticipantHandle partHandle = ConversationManager::createMediaResourceParticipant(convHandle, mediaUrl);
153 mMediaParticipantHandles.push_back(partHandle);
154 return partHandle;
155 }
156
157 virtual ParticipantHandle createLocalParticipant()
158 {
159 ParticipantHandle partHandle = ConversationManager::createLocalParticipant();
160 mLocalParticipantHandles.push_back(partHandle);
161 return partHandle;
162 }
163
164 virtual void onConversationDestroyed(ConversationHandle convHandle)
165 {
166 InfoLog(<< "onConversationDestroyed: handle=" << convHandle);
167 mConversationHandles.remove(convHandle);
168 }
169
170 virtual void onParticipantDestroyed(ParticipantHandle partHandle)
171 {
172 InfoLog(<< "onParticipantDestroyed: handle=" << partHandle);
173 // Remove from whatever list it is in
174 mRemoteParticipantHandles.remove(partHandle);
175 mLocalParticipantHandles.remove(partHandle);
176 mMediaParticipantHandles.remove(partHandle);
177 }
178
179 virtual void onDtmfEvent(ParticipantHandle partHandle, int dtmf, int duration, bool up)
180 {
181 InfoLog(<< "onDtmfEvent: handle=" << partHandle << " tone=" << dtmf << " dur=" << duration << " up=" << up);
182 }
183
184 virtual void onIncomingParticipant(ParticipantHandle partHandle, const SipMessage& msg, bool autoAnswer, ConversationProfile& conversationProfile)
185 {
186 InfoLog(<< "onIncomingParticipant: handle=" << partHandle << "auto=" << autoAnswer << " msg=" << msg.brief());
187 mRemoteParticipantHandles.push_back(partHandle);
188 if(autoAnswerEnabled)
189 {
190 // If there are no conversations, then create one
191 if(mConversationHandles.empty())
192 {
193 ConversationHandle convHandle = createConversation();
194 // ensure a local participant is in the conversation - create one if one doesn't exist
195 if(mLocalParticipantHandles.empty())
196 {
197 createLocalParticipant();
198 }
199 addParticipant(convHandle, mLocalParticipantHandles.front());
200 }
201 addParticipant(mConversationHandles.front(), partHandle);
202 answerParticipant(partHandle);
203 }
204 }
205
206 virtual void onRequestOutgoingParticipant(ParticipantHandle partHandle, const SipMessage& msg, ConversationProfile& conversationProfile)
207 {
208 InfoLog(<< "onRequestOutgoingParticipant: handle=" << partHandle << " msg=" << msg.brief());
209 /*
210 if(mConvHandles.empty())
211 {
212 ConversationHandle convHandle = createConversation();
213 addParticipant(convHandle, partHandle);
214 }*/
215 }
216
217 virtual void onParticipantTerminated(ParticipantHandle partHandle, unsigned int statusCode)
218 {
219 InfoLog(<< "onParticipantTerminated: handle=" << partHandle);
220 }
221
222 virtual void onParticipantProceeding(ParticipantHandle partHandle, const SipMessage& msg)
223 {
224 InfoLog(<< "onParticipantProceeding: handle=" << partHandle << " msg=" << msg.brief());
225 }
226
227 virtual void onRelatedConversation(ConversationHandle relatedConvHandle, ParticipantHandle relatedPartHandle,
228 ConversationHandle origConvHandle, ParticipantHandle origPartHandle)
229 {
230 InfoLog(<< "onRelatedConversation: relatedConvHandle=" << relatedConvHandle << " relatedPartHandle=" << relatedPartHandle
231 << " origConvHandle=" << origConvHandle << " origPartHandle=" << origPartHandle);
232 mConversationHandles.push_back(relatedConvHandle);
233 mRemoteParticipantHandles.push_back(relatedPartHandle);
234 }
235
236 virtual void onParticipantAlerting(ParticipantHandle partHandle, const SipMessage& msg)
237 {
238 InfoLog(<< "onParticipantAlerting: handle=" << partHandle << " msg=" << msg.brief());
239 }
240
241 virtual void onParticipantConnected(ParticipantHandle partHandle, const SipMessage& msg)
242 {
243 InfoLog(<< "onParticipantConnected: handle=" << partHandle << " msg=" << msg.brief());
244 }
245
246 virtual void onParticipantRedirectSuccess(ParticipantHandle partHandle)
247 {
248 InfoLog(<< "onParticipantRedirectSuccess: handle=" << partHandle);
249 }
250
251 virtual void onParticipantRedirectFailure(ParticipantHandle partHandle, unsigned int statusCode)
252 {
253 InfoLog(<< "onParticipantRedirectFailure: handle=" << partHandle << " statusCode=" << statusCode);
254 }
255
256 void displayInfo()
257 {
258 Data output;
259
260 if(!mConversationHandles.empty())
261 {
262 output = "Active conversation handles: ";
263 std::list<ConversationHandle>::iterator it;
264 for(it = mConversationHandles.begin(); it != mConversationHandles.end(); it++)
265 {
266 output += Data(*it) + " ";
267 }
268 InfoLog(<< output);
269 }
270 if(!mLocalParticipantHandles.empty())
271 {
272 output = "Local Participant handles: ";
273 std::list<ParticipantHandle>::iterator it;
274 for(it = mLocalParticipantHandles.begin(); it != mLocalParticipantHandles.end(); it++)
275 {
276 output += Data(*it) + " ";
277 }
278 InfoLog(<< output);
279 }
280 if(!mRemoteParticipantHandles.empty())
281 {
282 output = "Remote Participant handles: ";
283 std::list<ParticipantHandle>::iterator it;
284 for(it = mRemoteParticipantHandles.begin(); it != mRemoteParticipantHandles.end(); it++)
285 {
286 output += Data(*it) + " ";
287 }
288 InfoLog(<< output);
289 }
290 if(!mMediaParticipantHandles.empty())
291 {
292 output = "Media Participant handles: ";
293 std::list<ParticipantHandle>::iterator it;
294 for(it = mMediaParticipantHandles.begin(); it != mMediaParticipantHandles.end(); it++)
295 {
296 output += Data(*it) + " ";
297 }
298 InfoLog(<< output);
299 }
300 }
301
302 std::list<ConversationHandle> mConversationHandles;
303 std::list<ParticipantHandle> mLocalParticipantHandles;
304 std::list<ParticipantHandle> mRemoteParticipantHandles;
305 std::list<ParticipantHandle> mMediaParticipantHandles;
306 bool mLocalAudioEnabled;
307 };
308
309 void processCommandLine(Data& commandline, MyConversationManager& myConversationManager, MyUserAgent& myUserAgent)
310 {
311 Data command;
312 #define MAX_ARGS 5
313 Data arg[MAX_ARGS];
314 ParseBuffer pb(commandline);
315 pb.skipWhitespace();
316 if(pb.eof()) return;
317 const char *start = pb.position();
318 pb.skipToOneOf(ParseBuffer::Whitespace);
319 pb.data(command, start);
320
321 // Get arguments (up to MAX_ARGS)
322 int currentArg = 0;
323 while(!pb.eof() && currentArg < MAX_ARGS)
324 {
325 pb.skipWhitespace();
326 if(!pb.eof())
327 {
328 const char *start = pb.position();
329 pb.skipToOneOf(ParseBuffer::Whitespace);
330 pb.data(arg[currentArg++], start);
331 }
332 }
333
334 // Process commands
335 if(isEqualNoCase(command, "quit") || isEqualNoCase(command, "q") || isEqualNoCase(command, "exit"))
336 {
337 finished=true;
338 return;
339 }
340 if(isEqualNoCase(command, "createconv") || isEqualNoCase(command, "cc"))
341 {
342 myConversationManager.createConversation();
343 return;
344 }
345 if(isEqualNoCase(command, "destroyconv") || isEqualNoCase(command, "dc"))
346 {
347 unsigned long handle = arg[0].convertUnsignedLong();
348 if(handle != 0)
349 {
350 myConversationManager.destroyConversation(handle);
351 }
352 else
353 {
354 InfoLog( << "Invalid command format: <'destroyconv'|'dc'> <convHandle>");
355 }
356 return;
357 }
358 if(isEqualNoCase(command, "joinconv") || isEqualNoCase(command, "jc"))
359 {
360 unsigned long handleSrc = arg[0].convertUnsignedLong();
361 unsigned long handleDest = arg[1].convertUnsignedLong();
362 if(handleSrc != 0 && handleDest != 0)
363 {
364 myConversationManager.joinConversation(handleSrc, handleDest);
365 }
366 else
367 {
368 InfoLog( << "Invalid command format: <'joinconv'|'jc'> <sourceConvHandle> <destConvHandle>");
369 }
370 return;
371 }
372 if(isEqualNoCase(command, "createlocal") || isEqualNoCase(command, "clp"))
373 {
374 myConversationManager.createLocalParticipant();
375 return;
376 }
377 if(isEqualNoCase(command, "createremote") || isEqualNoCase(command, "crp"))
378 {
379 unsigned long handle = arg[0].convertUnsignedLong();
380 ConversationManager::ParticipantForkSelectMode mode = ConversationManager::ForkSelectAutomatic;
381 if(handle != 0 && !arg[1].empty())
382 {
383 if(!arg[2].empty() && isEqualNoCase(arg[2], "manual"))
384 {
385 mode = ConversationManager::ForkSelectManual;
386 }
387 try
388 {
389 NameAddr dest(arg[1]);
390 myConversationManager.createRemoteParticipant(handle, dest, mode);
391 }
392 catch(...)
393 {
394 NameAddr dest(uri);
395 dest.uri().user() = arg[1];
396 myConversationManager.createRemoteParticipant(handle, dest, mode);
397 }
398 }
399 else
400 {
401 InfoLog( << "Invalid command format: <'createremote'|'crp'> <convHandle> <destURI> [<'manual'>] (last arg is fork select mode, 'auto' is default).");
402 }
403 return;
404 }
405 if(isEqualNoCase(command, "createmedia") || isEqualNoCase(command, "cmp"))
406 {
407 unsigned long handle = arg[0].convertUnsignedLong();
408 unsigned long duration = arg[2].convertUnsignedLong();
409 if(handle != 0 && !arg[1].empty())
410 {
411 try
412 {
413 Uri url(arg[1]);
414 if(duration != 0)
415 {
416 url.param(p_duration) = duration;
417 }
418 myConversationManager.createMediaResourceParticipant(handle, url);
419 }
420 catch(resip::BaseException& e)
421 {
422 InfoLog( << "Invalid url format: <'createmedia'|'cmp'> <convHandle> <mediaURL> [<durationMs>]: " << e);
423 }
424 catch(...)
425 {
426 InfoLog( << "Invalid url format: <'createmedia'|'cmp'> <convHandle> <mediaURL> [<durationMs>]");
427 }
428 }
429 else
430 {
431 //myConversationManager.createMediaResourceParticipant(1, Uri("http://www.sillyhumor.com/answer/helloo.wav"));
432 InfoLog( << "Invalid command format: <'createmedia'|'cmp'> <convHandle> <mediaURL> [<durationMs>]");
433 }
434 return;
435 }
436 if(isEqualNoCase(command, "destroypart") || isEqualNoCase(command, "dp"))
437 {
438 unsigned long handle = arg[0].convertUnsignedLong();
439 if(handle != 0)
440 {
441 myConversationManager.destroyParticipant(handle);
442 }
443 else
444 {
445 InfoLog( << "Invalid command format: <'destroypart'|'dp'> <parthandle>");
446 }
447 return;
448 }
449 if(isEqualNoCase(command, "addpart") || isEqualNoCase(command, "ap"))
450 {
451 unsigned long convHandle = arg[0].convertUnsignedLong();
452 unsigned long partHandle = arg[1].convertUnsignedLong();
453 if(convHandle != 0 && partHandle != 0)
454 {
455 myConversationManager.addParticipant(convHandle, partHandle);
456 }
457 else
458 {
459 InfoLog( << "Invalid command format: <'addpart'|'ap'> <convHandle> <partHandle>");
460 }
461 return;
462 }
463 if(isEqualNoCase(command, "remotepart") || isEqualNoCase(command, "rp"))
464 {
465 unsigned long convHandle = arg[0].convertUnsignedLong();
466 unsigned long partHandle = arg[1].convertUnsignedLong();
467 if(convHandle != 0 && partHandle != 0)
468 {
469 myConversationManager.removeParticipant(convHandle, partHandle);
470 }
471 else
472 {
473 InfoLog( << "Invalid command format: <'removepart'|'rp'> <convHandle> <partHandle>");
474 }
475 return;
476 }
477 if(isEqualNoCase(command, "movepart") || isEqualNoCase(command, "mp"))
478 {
479 unsigned long partHandle = arg[0].convertUnsignedLong();
480 unsigned long srcConvHandle = arg[1].convertUnsignedLong();
481 unsigned long dstConvHandle = arg[2].convertUnsignedLong();
482 if(partHandle != 0 && srcConvHandle != 0 && dstConvHandle != 0)
483 {
484 myConversationManager.moveParticipant(partHandle, srcConvHandle, dstConvHandle);
485 }
486 else
487 {
488 InfoLog( << "Invalid command format: <'movepart'|'mp'> <partHandle> <srcConvHandle> <dstConvHandle>");
489 }
490 return;
491 }
492 if(isEqualNoCase(command, "partcontrib") || isEqualNoCase(command, "pc"))
493 {
494 unsigned long convHandle = arg[0].convertUnsignedLong();
495 unsigned long partHandle = arg[1].convertUnsignedLong();
496 if(partHandle != 0 && convHandle != 0)
497 {
498 myConversationManager.modifyParticipantContribution(convHandle, partHandle, arg[2].convertUnsignedLong(), arg[3].convertUnsignedLong());
499 }
500 else
501 {
502 InfoLog( << "Invalid command format: <'partcontrib'|'pc'> <convHandle> <partHandle> <inputGain> <outputGain> (gain in percentage)");
503 }
504 return;
505 }
506 if(isEqualNoCase(command, "bridgematrix") || isEqualNoCase(command, "bm"))
507 {
508 myConversationManager.outputBridgeMatrix();
509 return;
510 }
511 if(isEqualNoCase(command, "alert") || isEqualNoCase(command, "al"))
512 {
513 unsigned long partHandle = arg[0].convertUnsignedLong();
514 bool early = true;
515 if(partHandle != 0)
516 {
517 if(!arg[1].empty() && isEqualNoCase(arg[1], "noearly"))
518 {
519 early = false;
520 }
521 myConversationManager.alertParticipant(partHandle, early);
522 }
523 else
524 {
525 InfoLog( << "Invalid command format: <'alert'|'al'> <partHandle> [<'noearly'>] (last arg is early flag, enabled by default)");
526 }
527 return;
528 }
529 if(isEqualNoCase(command, "answer") || isEqualNoCase(command, "an"))
530 {
531 unsigned long partHandle = arg[0].convertUnsignedLong();
532 if(partHandle != 0)
533 {
534 myConversationManager.answerParticipant(partHandle);
535 }
536 else
537 {
538 InfoLog( << "Invalid command format: <'answer'|'an'> <partHandle>");
539 }
540 return;
541 }
542 if(isEqualNoCase(command, "reject") || isEqualNoCase(command, "rj"))
543 {
544 unsigned long partHandle = arg[0].convertUnsignedLong();
545 unsigned long status = arg[1].convertUnsignedLong();
546 if(partHandle != 0)
547 {
548 if(status == 0) status = 486;
549 myConversationManager.rejectParticipant(partHandle, status);
550 }
551 else
552 {
553 InfoLog( << "Invalid command format: <'reject'|'rj'> <partHandle> [<statusCode>] (default status code is 486)");
554 }
555 return;
556 }
557 if(isEqualNoCase(command, "redirect") || isEqualNoCase(command, "rd"))
558 {
559 unsigned long partHandle = arg[0].convertUnsignedLong();
560 if(partHandle != 0 && !arg[1].empty())
561 {
562 try
563 {
564 NameAddr dest(arg[1]);
565 myConversationManager.redirectParticipant(partHandle, dest);
566 }
567 catch(...)
568 {
569 NameAddr dest(uri);
570 dest.uri().user() = arg[1];
571 myConversationManager.redirectParticipant(partHandle, dest);
572 }
573 }
574 else
575 {
576 InfoLog( << "Invalid command format: <'redirect'|'rd'> <partHandle> <destURI>");
577 }
578 return;
579 }
580 if(isEqualNoCase(command, "redirectTo") || isEqualNoCase(command, "rt"))
581 {
582 unsigned long partHandle = arg[0].convertUnsignedLong();
583 unsigned long destPartHandle = arg[1].convertUnsignedLong();
584 if(partHandle != 0 && destPartHandle != 0)
585 {
586 myConversationManager.redirectToParticipant(partHandle, destPartHandle);
587 }
588 else
589 {
590 InfoLog( << "Invalid command format: <'redirectTo'|'rt'> <partHandle> <destPartHandle>");
591 }
592 return;
593 }
594 if(isEqualNoCase(command, "volume") || isEqualNoCase(command, "sv"))
595 {
596 unsigned long volume = arg[0].convertUnsignedLong();
597 myConversationManager.setSpeakerVolume(volume);
598 InfoLog( << "Speaker volume set to " << volume);
599 return;
600 }
601 if(isEqualNoCase(command, "gain") || isEqualNoCase(command, "sg"))
602 {
603 unsigned long gain = arg[0].convertUnsignedLong();
604 myConversationManager.setMicrophoneGain(gain);
605 InfoLog( << "Microphone gain set to " << gain);
606 return;
607 }
608 if(isEqualNoCase(command, "mute") || isEqualNoCase(command, "mm"))
609 {
610 bool enable = arg[0].convertUnsignedLong() != 0;
611 myConversationManager.muteMicrophone(enable);
612 InfoLog( << "Microphone mute " << (enable ? "enabled" : "disabled"));
613 return;
614 }
615 if(isEqualNoCase(command, "echocanel") || isEqualNoCase(command, "aec"))
616 {
617 bool enable = arg[0].convertUnsignedLong() != 0;
618 myConversationManager.enableEchoCancel(enable);
619 InfoLog( << "Echo cancellation " << (enable ? "enabled" : "disabled"));
620 return;
621 }
622 if(isEqualNoCase(command, "autogain") || isEqualNoCase(command, "agc"))
623 {
624 bool enable = arg[0].convertUnsignedLong() != 0;
625 myConversationManager.enableAutoGainControl(enable);
626 InfoLog( << "Automatic gain control " << (enable ? "enabled" : "disabled"));
627 return;
628 }
629 if(isEqualNoCase(command, "noisereduction") || isEqualNoCase(command, "nr"))
630 {
631 bool enable = arg[0].convertUnsignedLong() != 0;
632 myConversationManager.enableNoiseReduction(enable);
633 return;
634 }
635 if(isEqualNoCase(command, "subscribe") || isEqualNoCase(command, "cs"))
636 {
637 unsigned int subTime = arg[2].convertUnsignedLong();
638 if(!arg[0].empty() && !arg[1].empty() && subTime != 0 && !arg[3].empty() && !arg[4].empty())
639 {
640 try
641 {
642 NameAddr dest(arg[1]);
643 Mime mime(arg[3], arg[4]);
644 myUserAgent.createSubscription(arg[0], dest, subTime, mime);
645 }
646 catch(...)
647 {
648 NameAddr dest(uri);
649 Mime mime(arg[3], arg[4]);
650 dest.uri().user() = arg[1];
651 myUserAgent.createSubscription(arg[0], dest, subTime, mime);
652 }
653 }
654 else
655 {
656 InfoLog( << "Invalid command format: <'subscribe'|'cs'> <eventType> <targetUri> <subTime> <mimeType> <mimeSubType>");
657 }
658 return;
659 }
660 if(isEqualNoCase(command, "destsub") || isEqualNoCase(command, "ds"))
661 {
662 unsigned int subHandle = arg[0].convertUnsignedLong();
663
664 if(subHandle > 0)
665 {
666 myUserAgent.destroySubscription(subHandle);
667 }
668 else
669 {
670 InfoLog( << "Invalid command format: <'destsub'|'ds'> <subHandle>");
671 }
672 return;
673 }
674 if(isEqualNoCase(command, "autoans") || isEqualNoCase(command, "aa"))
675 {
676 bool enable = arg[0].convertUnsignedLong() != 0;
677 autoAnswerEnabled = enable;
678 InfoLog( << "Autoanswer " << (enable ? "enabled" : "disabled"));
679 return;
680 }
681 if(isEqualNoCase(command, "setcodecs") || isEqualNoCase(command, "sc"))
682 {
683 Data codecId;
684 std::list<unsigned int> idList;
685 ParseBuffer pb(arg[0]);
686 pb.skipWhitespace();
687 while(!pb.eof())
688 {
689 const char *start = pb.position();
690 pb.skipToOneOf(ParseBuffer::Whitespace, ","); // white space or ","
691 pb.data(codecId, start);
692 idList.push_back(codecId.convertUnsignedLong());
693 if(!pb.eof())
694 {
695 pb.skipChar(',');
696 }
697 }
698 unsigned int numCodecIds = idList.size();
699 if(numCodecIds > 0)
700 {
701 unsigned int* codecIdArray = new unsigned int[numCodecIds];
702 unsigned int index = 0;
703 std::list<unsigned int>::iterator it = idList.begin();
704 for(;it != idList.end(); it++)
705 {
706 codecIdArray[index++] = (*it);
707 }
708 Data ipAddress(conversationProfile->sessionCaps().session().connection().getAddress());
709 // Note: Technically modifying the conversation profile at runtime like this is not
710 // thread safe. But it should be fine for this test consoles purposes.
711 myConversationManager.buildSessionCapabilities(ipAddress, numCodecIds, codecIdArray, conversationProfile->sessionCaps());
712 delete [] codecIdArray;
713 }
714 return;
715 }
716 if(isEqualNoCase(command, "securemedia") || isEqualNoCase(command, "sm"))
717 {
718 ConversationProfile::SecureMediaMode secureMediaMode = ConversationProfile::NoSecureMedia;
719 bool secureMediaRequired = false;
720 if(isEqualNoCase(arg[0], "Srtp"))
721 {
722 secureMediaMode = ConversationProfile::Srtp;
723 }
724 else if(isEqualNoCase(arg[0], "SrtpReq"))
725 {
726 secureMediaMode = ConversationProfile::Srtp;
727 secureMediaRequired = true;
728 }
729 #ifdef USE_SSL
730 else if(isEqualNoCase(arg[0], "SrtpDtls"))
731 {
732 secureMediaMode = ConversationProfile::SrtpDtls;
733 }
734 else if(isEqualNoCase(arg[0], "SrtpDtlsReq"))
735 {
736 secureMediaMode = ConversationProfile::SrtpDtls;
737 secureMediaRequired = true;
738 }
739 #endif
740 else
741 {
742 arg[0] = "None"; // for display output only
743 }
744 // Note: Technically modifying the conversation profile at runtime like this is not
745 // thread safe. But it should be fine for this test consoles purposes.
746 conversationProfile->secureMediaMode() = secureMediaMode;
747 conversationProfile->secureMediaRequired() = secureMediaRequired;
748 InfoLog( << "Secure media mode set to: " << arg[0]);
749 return;
750 }
751 if(isEqualNoCase(command, "natmode") || isEqualNoCase(command, "nm"))
752 {
753 ConversationProfile::NatTraversalMode natTraversalMode = ConversationProfile::NoNatTraversal;
754 if(isEqualNoCase(arg[0], "Bind"))
755 {
756 natTraversalMode = ConversationProfile::StunBindDiscovery;
757 }
758 else if(isEqualNoCase(arg[0], "UdpAlloc"))
759 {
760 natTraversalMode = ConversationProfile::TurnUdpAllocation;
761 }
762 else if(isEqualNoCase(arg[0], "TcpAlloc"))
763 {
764 natTraversalMode = ConversationProfile::TurnTcpAllocation;
765 }
766 #ifdef USE_SSL
767 else if(isEqualNoCase(arg[0], "TlsAlloc"))
768 {
769 natTraversalMode = ConversationProfile::TurnTlsAllocation;
770 }
771 #endif
772 else
773 {
774 arg[0] = "None"; // for display output only
775 }
776 // Note: Technically modifying the conversation profile at runtime like this is not
777 // thread safe. But it should be fine for this test consoles purposes.
778 conversationProfile->natTraversalMode() = natTraversalMode;
779 InfoLog( << "NAT traversal mode set to: " << arg[0]);
780 return;
781 }
782 if(isEqualNoCase(command, "natserver") || isEqualNoCase(command, "ns"))
783 {
784 Data natTraversalServerHostname;
785 unsigned short natTraversalServerPort = 8777;
786 // Read server and port
787 ParseBuffer pb(arg[0]);
788 pb.skipWhitespace();
789 const char *start = pb.position();
790 pb.skipToOneOf(ParseBuffer::Whitespace, ":"); // white space or ":"
791 pb.data(natTraversalServerHostname, start);
792 if(!pb.eof())
793 {
794 pb.skipChar(':');
795 start = pb.position();
796 pb.skipToOneOf(ParseBuffer::Whitespace); // white space
797 Data port;
798 pb.data(port, start);
799 natTraversalServerPort = port.convertUnsignedLong();
800 }
801 // Note: Technically modifying the conversation profile at runtime like this is not
802 // thread safe. But it should be fine for this test consoles purposes.
803 conversationProfile->natTraversalServerHostname() = natTraversalServerHostname;
804 conversationProfile->natTraversalServerPort() = natTraversalServerPort;
805 InfoLog( << "NAT traversal STUN/TURN server set to: " << natTraversalServerHostname << ":" << natTraversalServerPort);
806 return;
807 }
808 if(isEqualNoCase(command, "natuser") || isEqualNoCase(command, "nu"))
809 {
810 // Note: Technically modifying the conversation profile at runtime like this is not
811 // thread safe. But it should be fine for this test consoles purposes.
812 conversationProfile->stunUsername() = arg[0];
813 InfoLog( << "STUN/TURN user set to: " << arg[0]);
814 return;
815 }
816 if(isEqualNoCase(command, "natpwd") || isEqualNoCase(command, "np"))
817 {
818 // Note: Technically modifying the conversation profile at runtime like this is not
819 // thread safe. But it should be fine for this test consoles purposes.
820 conversationProfile->stunPassword() = arg[0];
821 InfoLog( << "STUN/TURN password set to: " << arg[0]);
822 return;
823 }
824 if(isEqualNoCase(command, "starttimer") || isEqualNoCase(command, "st"))
825 {
826 unsigned int timerId = arg[0].convertUnsignedLong();
827 unsigned int durationMs = arg[1].convertUnsignedLong();
828 unsigned int seqNumber = arg[2].convertUnsignedLong();
829
830 if(durationMs > 0)
831 {
832 myUserAgent.startApplicationTimer(timerId, durationMs, seqNumber);
833 InfoLog( << "Application Timer started for " << durationMs << "ms");
834 }
835 else
836 {
837 InfoLog( << "Invalid command format: <'starttimer'|'st'> <timerId> <durationMs> <seqNo>");
838 }
839 return;
840 }
841 if(isEqualNoCase(command, "info") || isEqualNoCase(command, "i"))
842 {
843 myConversationManager.displayInfo();
844 return;
845 }
846 if(isEqualNoCase(command, "dns") || isEqualNoCase(command, "ld"))
847 {
848 InfoLog( << "DNS cache (at WARNING log level):");
849 myUserAgent.logDnsCache();
850 return;
851 }
852 if(isEqualNoCase(command, "cleardns") || isEqualNoCase(command, "cd"))
853 {
854 myUserAgent.clearDnsCache();
855 InfoLog( << "DNS cache has been cleared.");
856 return;
857 }
858
859 #ifdef USE_SSL
860 Data setSecureMediaMode(" setSecureMediaMode <'securemedia'|'sm'> <'None'|'Srtp'|'SrtpReq'|'SrtpDtls'|'SrtpDtlsReq'>");
861 Data setNATTraversalMode(" setNATTraversalMode <'natmode'|'nm'> <'None'|'Bind'|'UdpAlloc'|'TcpAlloc'|'TlsAlloc'>" );
862 #else
863 Data setSecureMediaMode(" setSecureMediaMode <'securemedia'|'sm'> <'None'|'Srtp'|'SrtpReq'>");
864 Data setNATTraversalMode(" setNATTraversalMode <'natmode'|'nm'> <'None'|'Bind'|'UdpAlloc'|'TcpAlloc'>" );
865 #endif
866
867 InfoLog( << "Possible commands are: " << endl
868 << " createConversation: <'createconv'|'cc'>" << endl
869 << " destroyConversation: <'destroyconv'|'dc'> <convHandle>" << endl
870 << " joinConversation: <'joinconv'|'jc'> <sourceConvHandle> <destConvHandle>" << endl
871 << endl
872 << " createLocalParticipant: <'createlocal'|'clp'>" << endl
873 << " createRemoteParticipant: <'createremote'|'crp'> <convHandle> <destURI> [<'manual'>] (last arg is fork select mode, 'auto' is default)" << endl
874 << " createMediaResourceParticipant: <'createmedia'|'cmp'> <convHandle> <mediaURL> [<durationMs>]" << endl
875 << " destroyParticipant: <'destroypart'|'dp'> <parthandle>" << endl
876 << endl
877 << " addPartcipant: <'addpart'|'ap'> <convHandle> <partHandle>" << endl
878 << " removePartcipant: <'removepart'|'rp'> <convHandle> <partHandle>" << endl
879 << " moveParticipant: <'movepart'|'mp'> <partHandle> <srcConvHandle> <dstConvHandle>" << endl
880 << " modifyParticipantContribution: <'partcontrib'|'pc'> <convHandle> <partHandle> <inputGain> <outputGain> (gain in percentage)" << endl
881 << " outputBridgeMatrix: <'bridgematrix'|'bm'>" << endl
882 << " alertPartcipant: <'alert'|'al'> <partHandle> [<'noearly'>] (last arg is early flag, enabled by default)" << endl
883 << " answerParticipant: <'answer'|'an'> <partHandle>" << endl
884 << " rejectParticipant: <'reject'|'rj'> <partHandle> [<statusCode>] (default status code is 486)" << endl
885 << " redirectPartcipant: <'redirect'|'rd'> <partHandle> <destURI>" << endl
886 << " redirectToPartcipant: <'redirectTo'|'rt'> <partHandle> <destPartHandle>" << endl
887 << endl
888 << " setSpeakerVolume: <'volume'|'sv'> <volume>" << endl
889 << " setMicrophoneGain: <'gain'|'sg'> <gain>" << endl
890 << " muteMicrophone: <'mute'|'mm'> <'0'|'1'> (1 to enable/mute)" << endl
891 << " enableEchoCancel: <'echocancel'|'aec'> <'0'|'1'> (1 to enable)" << endl
892 << " enableAutoGainControl: <'autogain'|'agc'> <'0'|'1'> (1 to enable)" << endl
893 << " enableNoiseReduction: <'noisereduction'|'nr'> <'0'|'1'> (1 to enable)" << endl
894 << endl
895 << " createSubscription: <'subscribe'|'cs'> <eventType> <targetUri> <subTime> <mimeType> <mimeSubType>" << endl
896 << " destroySubscription: <'destsub'|'ds'> <subHandle>" << endl
897 << endl
898 << " setAutoAnswer <'autoans'|'aa'> <'0'|'1'> (1 to enable (default))" << endl
899 << " setCodecs <'setcodecs'|'sc'> <codecId>[,<codecId>]+ (comma separated list)" << endl
900 << setSecureMediaMode << endl
901 << setNATTraversalMode << endl
902 << " setNATTraversalServer <'natserver'|'ns'> <server:port>" << endl
903 << " setNATUsername <'natuser'|'nu'> <username>" << endl
904 << " setNATPassword <'natpwd'|'np'> <password>" << endl
905 << " startApplicationTimer: <'starttimer'|'st'> <timerId> <durationMs> <seqNo>" << endl
906 << " displayInfo: <'info'|'i'>" << endl
907 << " logDnsCache: <'dns'|'ld'>" << endl
908 << " clearDnsCache: <'cleardns'|'cd'>" << endl
909 << " exitProgram: <'exit'|'quit'|'q'>");
910 }
911
912 #define KBD_BUFFER_SIZE 256
913 void processKeyboard(char input, MyConversationManager& myConversationManager, MyUserAgent& myUserAgent)
914 {
915 static char buffer[KBD_BUFFER_SIZE];
916 static int bufferpos = 0;
917
918 if(input == 13 || input == 10) // enter
919 {
920 Data db(buffer,bufferpos);
921 #ifdef WIN32
922 cout << endl;
923 #endif
924 processCommandLine(db, myConversationManager, myUserAgent);
925 bufferpos = 0;
926 }
927 else if(input == 8 || input == 127) // backspace
928 {
929 if(bufferpos > 0)
930 {
931 #ifdef WIN32
932 cout << input << ' ' << input;
933 #else
934 // note: This is bit of a hack and may not be portable to all linux terminal types
935 cout << "\b\b\b \b\b\b";
936 fflush(stdout);
937 #endif
938 bufferpos--;
939 }
940 }
941 else
942 {
943 if(bufferpos == KBD_BUFFER_SIZE)
944 {
945 cout << endl;
946 bufferpos = 0;
947 }
948 else
949 {
950 #ifdef WIN32
951 cout << input;
952 #endif
953 buffer[bufferpos++] = (char)input;
954 }
955 }
956 }
957
958 int
959 main (int argc, char** argv)
960 {
961 #ifndef _WIN32
962 if ( signal( SIGPIPE, SIG_IGN) == SIG_ERR)
963 {
964 cerr << "Couldn't install signal handler for SIGPIPE" << endl;
965 exit(-1);
966 }
967 #endif
968
969 #if defined(WIN32) && defined(_DEBUG) && defined(LEAK_CHECK)
970 resip::FindMemoryLeaks fml;
971 {
972 #endif
973
974 if ( signal( SIGINT, signalHandler ) == SIG_ERR )
975 {
976 cerr << "Couldn't install signal handler for SIGINT" << endl;
977 exit( -1 );
978 }
979
980 if ( signal( SIGTERM, signalHandler ) == SIG_ERR )
981 {
982 cerr << "Couldn't install signal handler for SIGTERM" << endl;
983 exit( -1 );
984 }
985
986 // Defaults
987 bool registrationDisabled = false;
988 bool keepAlivesDisabled = false;
989 Data password;
990 Data dnsServers;
991 Data address = DnsUtil::getLocalIpAddress();
992 ConversationProfile::SecureMediaMode secureMediaMode = ConversationProfile::NoSecureMedia;
993 bool secureMediaRequired = false;
994 ConversationProfile::NatTraversalMode natTraversalMode = ConversationProfile::NoNatTraversal;
995 Data natTraversalServerHostname;
996 unsigned short natTraversalServerPort = 8777;
997 Data stunUsername;
998 Data stunPassword;
999 bool localAudioEnabled = true;
1000 unsigned short sipPort = 5062;
1001 unsigned short tlsPort = 5063;
1002 unsigned short mediaPortStart = 17384;
1003 Data tlsDomain = DnsUtil::getLocalHostName();
1004 NameAddr outboundProxy;
1005 Data logLevel("INFO");
1006 unsigned int codecIds[] = { SdpCodec::SDP_CODEC_PCMU /* 0 - pcmu */,
1007 SdpCodec::SDP_CODEC_PCMA /* 8 - pcma */,
1008 SdpCodec::SDP_CODEC_SPEEX /* 96 - speex NB 8,000bps */,
1009 SdpCodec::SDP_CODEC_SPEEX_15 /* 98 - speex NB 15,000bps */,
1010 SdpCodec::SDP_CODEC_SPEEX_24 /* 99 - speex NB 24,600bps */,
1011 SdpCodec::SDP_CODEC_L16_44100_MONO /* PCM 16 bit/sample 44100 samples/sec. */,
1012 SdpCodec::SDP_CODEC_ILBC /* 108 - iLBC */,
1013 SdpCodec::SDP_CODEC_ILBC_20MS /* 109 - Internet Low Bit Rate Codec, 20ms (RFC3951) */,
1014 SdpCodec::SDP_CODEC_SPEEX_5 /* 97 - speex NB 5,950bps */,
1015 SdpCodec::SDP_CODEC_GSM /* 3 - GSM */,
1016 //SdpCodec::SDP_CODEC_G722 /* 9 - G.722 */,
1017 SdpCodec::SDP_CODEC_TONES /* 110 - telephone-event */};
1018 unsigned int numCodecIds = sizeof(codecIds) / sizeof(codecIds[0]);
1019
1020 // Loop through command line arguments and process them
1021 for(int i = 1; i < argc; i++)
1022 {
1023 Data commandName(argv[i]);
1024
1025 // Process all commandNames that don't take values
1026 if(isEqualNoCase(commandName, "-?") ||
1027 isEqualNoCase(commandName, "--?") ||
1028 isEqualNoCase(commandName, "--help") ||
1029 isEqualNoCase(commandName, "/?"))
1030 {
1031 cout << "Command line options are:" << endl;
1032 cout << " -aa - enable autoanswer" << endl;
1033 cout << " -a <IP Address> - bind SIP transports to this IP address" << endl;
1034 cout << " -u <SIP URI> - URI of this SIP user" << endl;
1035 cout << " -p <password> - SIP password of this this SIP user" << endl;
1036 cout << " -nr - no registration, set this to disable registration with SIP Proxy" << endl;
1037 cout << " -d <DNS servers> - comma seperated list of DNS servers, overrides OS detected list" << endl;
1038 cout << " -sp <port num> - local port number to use for SIP messaging (UDP/TCP)" << endl;
1039 cout << " -mp <port num> - local port number to start allocating from for RTP media" << endl;
1040 #ifdef USE_SSL
1041 cout << " -tp <port num> - local port number to use for TLS SIP messaging" << endl;
1042 cout << " -td <domain name> - domain name to use for TLS server connections" << endl;
1043 #endif
1044 cout << " -nk - no keepalives, set this to disable sending of keepalives" << endl;
1045 cout << " -op <SIP URI> - URI of a proxy server to use a SIP outbound proxy" << endl;
1046 #ifdef USE_SSL
1047 cout << " -sm <Srtp|SrtpReq|SrtpDtls|SrtpDtlsReq> - sets the secure media mode" << endl;
1048 cout << " -nm <Bind|UdpAlloc|TcpAlloc|TlsAlloc> - sets the NAT traversal mode" << endl;
1049 #else
1050 cout << " -sm <Srtp|SrtpReq> - sets the secure media mode" << endl;
1051 cout << " -nm <Bind|UdpAlloc|TcpAlloc> - sets the NAT traversal mode" << endl;
1052 #endif
1053 cout << " -ns <server:port> - set the hostname and port of the NAT STUN/TURN server" << endl;
1054 cout << " -nu <username> - sets the STUN/TURN username to use for NAT server" << endl;
1055 cout << " -np <password> - sets the STUN/TURN password to use for NAT server" << endl;
1056 cout << " -nl - no local audio support - removed local sound hardware requirement" << endl;
1057 cout << " -l <NONE|CRIT|ERR|WARNING|INFO|DEBUG|STACK> - logging level" << endl;
1058 cout << endl;
1059 cout << "Sample Command line:" << endl;
1060 cout << "testUA -a 192.168.1.100 -u sip:1000@myproxy.com -p 123 -aa" << endl;
1061 return 0;
1062 }
1063 else if(isEqualNoCase(commandName, "-nr"))
1064 {
1065 registrationDisabled = true;
1066 }
1067 else if(isEqualNoCase(commandName, "-aa"))
1068 {
1069 autoAnswerEnabled = true;
1070 }
1071 else if(isEqualNoCase(commandName, "-nk"))
1072 {
1073 keepAlivesDisabled = true;
1074 }
1075 else if(isEqualNoCase(commandName, "-nl"))
1076 {
1077 localAudioEnabled = false;
1078 }
1079 else
1080 {
1081 // Process commands that have values
1082 Data commandValue(i+1 < argc ? argv[i+1] : Data::Empty);
1083 if(commandValue.empty() || commandValue.at(0) == '-')
1084 {
1085 cerr << "Invalid command line parameters!" << endl;
1086 exit(-1);
1087 }
1088 i++; // increment argument
1089
1090 if(isEqualNoCase(commandName, "-a"))
1091 {
1092 address = commandValue;
1093 }
1094 else if(isEqualNoCase(commandName, "-u"))
1095 {
1096 try
1097 {
1098 NameAddr tempuri(commandValue);
1099 uri = tempuri;
1100 }
1101 catch(resip::BaseException& e)
1102 {
1103 cerr << "Invalid uri format: " << e << endl;
1104 exit(-1);
1105 }
1106 }
1107 else if(isEqualNoCase(commandName, "-p"))
1108 {
1109 password = commandValue;
1110 }
1111 else if(isEqualNoCase(commandName, "-d"))
1112 {
1113 dnsServers = commandValue;
1114 }
1115 else if(isEqualNoCase(commandName, "-sm"))
1116 {
1117 if(isEqualNoCase(commandValue, "Srtp"))
1118 {
1119 secureMediaMode = ConversationProfile::Srtp;
1120 }
1121 else if(isEqualNoCase(commandValue, "SrtpReq"))
1122 {
1123 secureMediaMode = ConversationProfile::Srtp;
1124 secureMediaRequired = true;
1125 }
1126 #ifdef USE_SSL
1127 else if(isEqualNoCase(commandValue, "SrtpDtls"))
1128 {
1129 secureMediaMode = ConversationProfile::SrtpDtls;
1130 }
1131 else if(isEqualNoCase(commandValue, "SrtpDtlsReq"))
1132 {
1133 secureMediaMode = ConversationProfile::SrtpDtls;
1134 secureMediaRequired = true;
1135 }
1136 #endif
1137 else
1138 {
1139 cerr << "Invalid Secure Media Mode: " << commandValue << endl;
1140 exit(-1);
1141 }
1142 }
1143 else if(isEqualNoCase(commandName, "-nm"))
1144 {
1145 if(isEqualNoCase(commandValue, "Bind"))
1146 {
1147 natTraversalMode = ConversationProfile::StunBindDiscovery;
1148 }
1149 else if(isEqualNoCase(commandValue, "UdpAlloc"))
1150 {
1151 natTraversalMode = ConversationProfile::TurnUdpAllocation;
1152 }
1153 else if(isEqualNoCase(commandValue, "TcpAlloc"))
1154 {
1155 natTraversalMode = ConversationProfile::TurnTcpAllocation;
1156 }
1157 #ifdef USE_SSL
1158 else if(isEqualNoCase(commandValue, "TlsAlloc"))
1159 {
1160 natTraversalMode = ConversationProfile::TurnTlsAllocation;
1161 }
1162 #endif
1163 else
1164 {
1165 cerr << "Invalid NAT Traversal Mode: " << commandValue << endl;
1166 exit(-1);
1167 }
1168 }
1169 else if(isEqualNoCase(commandName, "-ns"))
1170 {
1171 // Read server and port
1172 Data natServerAndPort = commandValue;
1173 ParseBuffer pb(natServerAndPort);
1174 pb.skipWhitespace();
1175 const char *start = pb.position();
1176 pb.skipToOneOf(ParseBuffer::Whitespace, ":"); // white space or ":"
1177 Data hostname;
1178 pb.data(hostname, start);
1179 natTraversalServerHostname = hostname;
1180 if(!pb.eof())
1181 {
1182 pb.skipChar(':');
1183 start = pb.position();
1184 pb.skipToOneOf(ParseBuffer::Whitespace); // white space
1185 Data port;
1186 pb.data(port, start);
1187 natTraversalServerPort = port.convertUnsignedLong();
1188 }
1189 }
1190 else if(isEqualNoCase(commandName, "-nu"))
1191 {
1192 stunUsername = commandValue;
1193 }
1194 else if(isEqualNoCase(commandName, "-np"))
1195 {
1196 stunPassword = commandValue;
1197 }
1198 else if(isEqualNoCase(commandName, "-sp"))
1199 {
1200 sipPort = (unsigned short)commandValue.convertUnsignedLong();
1201 }
1202 else if(isEqualNoCase(commandName, "-mp"))
1203 {
1204 mediaPortStart = (unsigned short)commandValue.convertUnsignedLong();
1205 }
1206 else if(isEqualNoCase(commandName, "-tp"))
1207 {
1208 tlsPort = (unsigned short)commandValue.convertUnsignedLong();
1209 }
1210 else if(isEqualNoCase(commandName, "-td"))
1211 {
1212 tlsDomain = commandValue;
1213 }
1214 else if(isEqualNoCase(commandName, "-op"))
1215 {
1216 try
1217 {
1218 NameAddr tempuri(commandValue);
1219 outboundProxy = tempuri;
1220 }
1221 catch(resip::BaseException& e)
1222 {
1223 cerr << "Invalid outbound proxy uri format: " << e << endl;
1224 exit(-1);
1225 }
1226 }
1227 else if(isEqualNoCase(commandName, "-l"))
1228 {
1229 logLevel = commandValue;
1230 }
1231 else
1232 {
1233 cerr << "Invalid command line parameters!" << endl;
1234 exit(-1);
1235 }
1236 }
1237 }
1238
1239 //enableConsoleOutput(TRUE); // Allow sipX console output
1240 OsSysLog::initialize(0, "testUA");
1241 OsSysLog::setOutputFile(0, "sipXtapilog.txt") ;
1242 //OsSysLog::enableConsoleOutput(true);
1243 //OsSysLog::setLoggingPriority(PRI_DEBUG);
1244 Log::initialize("Cout", logLevel, "testUA");
1245 //UserAgent::setLogLevel(Log::Warning, UserAgent::SubsystemAll);
1246 //UserAgent::setLogLevel(Log::Info, UserAgent::SubsystemRecon);
1247
1248 initNetwork();
1249
1250 InfoLog( << "testUA settings:");
1251 InfoLog( << " No Keepalives = " << (keepAlivesDisabled ? "true" : "false"));
1252 InfoLog( << " Autoanswer = " << (autoAnswerEnabled ? "true" : "false"));
1253 InfoLog( << " Do not register = " << (registrationDisabled ? "true" : "false"));
1254 InfoLog( << " Local IP Address = " << address);
1255 InfoLog( << " SIP URI = " << uri);
1256 InfoLog( << " SIP Password = " << password);
1257 InfoLog( << " Override DNS Servers = " << dnsServers);
1258 InfoLog( << " Secure Media Mode = " << secureMediaMode);
1259 InfoLog( << " NAT Traversal Mode = " << natTraversalMode);
1260 InfoLog( << " NAT Server = " << natTraversalServerHostname << ":" << natTraversalServerPort);
1261 InfoLog( << " STUN/TURN user = " << stunUsername);
1262 InfoLog( << " STUN/TURN password = " << stunPassword);
1263 InfoLog( << " SIP Port = " << sipPort);
1264 InfoLog( << " Media Port Range Start = " << mediaPortStart);
1265 #ifdef USE_SSL
1266 InfoLog( << " TLS Port = " << tlsPort);
1267 InfoLog( << " TLS Domain = " << tlsDomain);
1268 #endif
1269 InfoLog( << " Outbound Proxy = " << outboundProxy);
1270 InfoLog( << " Local Audio Enabled = " << (localAudioEnabled ? "true" : "false"));
1271 InfoLog( << " Log Level = " << logLevel);
1272
1273 InfoLog( << "type help or '?' for list of accepted commands." << endl);
1274
1275 //////////////////////////////////////////////////////////////////////////////
1276 // Setup UserAgentMasterProfile
1277 //////////////////////////////////////////////////////////////////////////////
1278
1279 SharedPtr<UserAgentMasterProfile> profile(new UserAgentMasterProfile);
1280
1281 // Add transports
1282 profile->addTransport(UDP, sipPort, V4, address);
1283 profile->addTransport(TCP, sipPort, V4, address);
1284 #ifdef USE_SSL
1285 profile->addTransport(TLS, tlsPort, V4, address, tlsDomain);
1286 #endif
1287
1288 // The following settings are used to avoid a kernel panic seen on an ARM embedded platform.
1289 // The kernel panic happens when either binding a udp socket to port 0 (OS selected),
1290 // or calling connect without first binding to a specific port. There is code in the
1291 // resip transport selector that uses a utility UDP socket in order to determine
1292 // which interface should be used to route to a particular destination. This code calls
1293 // connect with no bind. By setting a fixed transport interface here that
1294 // code will not be used.
1295 // The following line can be safely removed for other platforms
1296 //profile->setFixedTransportInterface(address);
1297
1298 // Settings
1299 profile->setDefaultRegistrationTime(3600);
1300 profile->setDefaultFrom(uri);
1301 profile->setDigestCredential(uri.uri().host(), uri.uri().user(), password);
1302
1303 // DNS Servers
1304 ParseBuffer pb(dnsServers);
1305 Data dnsServer;
1306 while(!dnsServers.empty() && !pb.eof())
1307 {
1308 pb.skipWhitespace();
1309 const char *start = pb.position();
1310 pb.skipToOneOf(ParseBuffer::Whitespace, ";,"); // allow white space
1311 pb.data(dnsServer, start);
1312 if(DnsUtil::isIpV4Address(dnsServer))
1313 {
1314 InfoLog( << "Adding DNS Server: " << dnsServer);
1315 profile->addAdditionalDnsServer(dnsServer);
1316 }
1317 else
1318 {
1319 ErrLog( << "Tried to add dns server, but invalid format: " << dnsServer);
1320 }
1321 if(!pb.eof())
1322 {
1323 pb.skipChar();
1324 }
1325 }
1326
1327 // Disable Statisitics Manager
1328 profile->statisticsManagerEnabled() = false;
1329
1330 // Add ENUM Suffixes from setting string - use code similar to dns server
1331 //profile->addEnumSuffix(enumSuffix);
1332
1333 if(!keepAlivesDisabled)
1334 {
1335 profile->setKeepAliveTimeForDatagram(30);
1336 profile->setKeepAliveTimeForStream(180);
1337 }
1338
1339 // Support Methods, etc.
1340 profile->validateContentEnabled() = false;
1341 profile->validateContentLanguageEnabled() = false;
1342 profile->validateAcceptEnabled() = false;
1343
1344 profile->clearSupportedLanguages();
1345 profile->addSupportedLanguage(Token("en"));
1346
1347 profile->clearSupportedMimeTypes();
1348 profile->addSupportedMimeType(INVITE, Mime("application", "sdp"));
1349 profile->addSupportedMimeType(INVITE, Mime("multipart", "mixed"));
1350 profile->addSupportedMimeType(INVITE, Mime("multipart", "signed"));
1351 profile->addSupportedMimeType(INVITE, Mime("multipart", "alternative"));
1352 profile->addSupportedMimeType(OPTIONS,Mime("application", "sdp"));
1353 profile->addSupportedMimeType(OPTIONS,Mime("multipart", "mixed"));
1354 profile->addSupportedMimeType(OPTIONS, Mime("multipart", "signed"));
1355 profile->addSupportedMimeType(OPTIONS, Mime("multipart", "alternative"));
1356 profile->addSupportedMimeType(PRACK, Mime("application", "sdp"));
1357 profile->addSupportedMimeType(PRACK, Mime("multipart", "mixed"));
1358 profile->addSupportedMimeType(PRACK, Mime("multipart", "signed"));
1359 profile->addSupportedMimeType(PRACK, Mime("multipart", "alternative"));
1360 profile->addSupportedMimeType(UPDATE, Mime("application", "sdp"));
1361 profile->addSupportedMimeType(UPDATE, Mime("multipart", "mixed"));
1362 profile->addSupportedMimeType(UPDATE, Mime("multipart", "signed"));
1363 profile->addSupportedMimeType(UPDATE, Mime("multipart", "alternative"));
1364 profile->addSupportedMimeType(NOTIFY, Mime("message", "sipfrag"));
1365
1366 profile->clearSupportedMethods();
1367 profile->addSupportedMethod(INVITE);
1368 profile->addSupportedMethod(ACK);
1369 profile->addSupportedMethod(CANCEL);
1370 profile->addSupportedMethod(OPTIONS);
1371 profile->addSupportedMethod(BYE);
1372 profile->addSupportedMethod(REFER);
1373 profile->addSupportedMethod(NOTIFY);
1374 profile->addSupportedMethod(SUBSCRIBE);
1375 profile->addSupportedMethod(UPDATE);
1376 profile->addSupportedMethod(PRACK);
1377 //profile->addSupportedMethod(INFO);
1378 //profile->addSupportedMethod(MESSAGE);
1379
1380 profile->clearSupportedOptionTags();
1381 profile->addSupportedOptionTag(Token(Symbols::Replaces));
1382 profile->addSupportedOptionTag(Token(Symbols::Timer));
1383 profile->addSupportedOptionTag(Token(Symbols::NoReferSub));
1384 profile->addSupportedOptionTag(Token(Symbols::AnswerMode));
1385 profile->addSupportedOptionTag(Token(Symbols::TargetDialog));
1386 //profile->addSupportedOptionTag(Token(Symbols::C100rel)); // Automatically added by calling setUacReliableProvisionalMode
1387
1388 profile->setUacReliableProvisionalMode(MasterProfile::Supported);
1389
1390 profile->clearSupportedSchemes();
1391 profile->addSupportedScheme("sip");
1392 #ifdef USE_SSL
1393 profile->addSupportedScheme("sips");
1394 #endif
1395
1396 // Have stack add Allow/Supported/Accept headers to INVITE dialog establishment messages
1397 profile->clearAdvertisedCapabilities(); // Remove Profile Defaults, then add our preferences
1398 profile->addAdvertisedCapability(Headers::Allow);
1399 //profile->addAdvertisedCapability(Headers::AcceptEncoding); // This can be misleading - it might specify what is expected in response
1400 profile->addAdvertisedCapability(Headers::AcceptLanguage);
1401 profile->addAdvertisedCapability(Headers::Supported);
1402 profile->setMethodsParamEnabled(true);
1403
1404 //profile->setOverrideHostAndPort(mContact);
1405 if(!outboundProxy.uri().host().empty())
1406 {
1407 profile->setOutboundProxy(outboundProxy.uri());
1408 }
1409
1410 profile->setUserAgent("ConversationManager/TestUA");
1411 profile->rtpPortRangeMin() = mediaPortStart;
1412 profile->rtpPortRangeMax() = mediaPortStart + 101; // Allows 100 media streams
1413
1414 //////////////////////////////////////////////////////////////////////////////
1415 // Setup ConversationProfile
1416 //////////////////////////////////////////////////////////////////////////////
1417
1418 conversationProfile = SharedPtr<ConversationProfile>(new ConversationProfile(profile));
1419 if(uri.uri().user() != "noreg" && !registrationDisabled)
1420 {
1421 conversationProfile->setDefaultRegistrationTime(3600);
1422 }
1423 else
1424 {
1425 conversationProfile->setDefaultRegistrationTime(0);
1426 }
1427 conversationProfile->setDefaultRegistrationRetryTime(120); // 2 mins
1428 conversationProfile->setDefaultFrom(uri);
1429 conversationProfile->setDigestCredential(uri.uri().host(), uri.uri().user(), password);
1430
1431 #if 0 // Now auto-built
1432
1433 // Create Session Capabilities and assign to coversation Profile
1434 // Note: port, sessionId and version will be replaced in actual offer/answer int port = 16384;
1435 // Build s=, o=, t=, and c= lines
1436 SdpContents::Session::Origin origin("-", 0 /* sessionId */, 0 /* version */, SdpContents::IP4, address); // o=
1437 SdpContents::Session session(0, origin, "-" /* s= */);
1438 session.connection() = SdpContents::Session::Connection(SdpContents::IP4, address); // c=
1439 session.addTime(SdpContents::Session::Time(0, 0));
1440
1441 // Build Codecs and media offering
1442 SdpContents::Session::Medium medium("audio", port, 1, "RTP/AVP");
1443 // For G.722, it is necessary to patch sipXmediaLib/src/mp/codecs/plgg722/plgg722.c
1444 // #define USE_8K_SAMPLES G722_SAMPLE_RATE_8000
1445 // and change sample rate from 16000 to 8000
1446 // (tested against a Polycom device configured for G.722 8000)
1447 // http://www.mail-archive.com/sipxtapi-dev@list.sipfoundry.org/msg02522.html
1448 // A more generic solution is needed long term, as G.722 is peculiar and
1449 // implementations are not consistent:
1450 // https://lists.cs.columbia.edu/pipermail/sip-implementors/2007-August/017292.html
1451 //SdpContents::Session::Codec g722codec("G722", 8000);
1452 //g722codec.payloadType() = 9; /* RFC3551 */ ;
1453 //medium.addCodec(g722codec);
1454 SdpContents::Session::Codec g711ucodec("PCMU", 8000);
1455 g711ucodec.payloadType() = 0; /* RFC3551 */ ;
1456 medium.addCodec(g711ucodec);
1457 SdpContents::Session::Codec g711acodec("PCMA", 8000);
1458 g711acodec.payloadType() = 8; /* RFC3551 */ ;
1459 medium.addCodec(g711acodec);
1460 SdpContents::Session::Codec speexCodec("SPEEX", 8000);
1461 speexCodec.payloadType() = 110;
1462 speexCodec.parameters() = Data("mode=3");
1463 medium.addCodec(speexCodec);
1464 SdpContents::Session::Codec gsmCodec("GSM", 8000);
1465 gsmCodec.payloadType() = 3; /* RFC3551 */ ;
1466 medium.addCodec(gsmCodec);
1467 medium.addAttribute("ptime", Data(20)); // 20 ms of speech per frame (note G711 has 10ms samples, so this is 2 samples per frame)
1468 medium.addAttribute("sendrecv");
1469
1470 SdpContents::Session::Codec toneCodec("telephone-event", 8000);
1471 toneCodec.payloadType() = 102;
1472 toneCodec.parameters() = Data("0-15");
1473 medium.addCodec(toneCodec);
1474 session.addMedium(medium);
1475
1476 conversationProfile->sessionCaps().session() = session;
1477 #endif
1478
1479 // Setup NatTraversal Settings
1480 conversationProfile->natTraversalMode() = natTraversalMode;
1481 conversationProfile->natTraversalServerHostname() = natTraversalServerHostname;
1482 conversationProfile->natTraversalServerPort() = natTraversalServerPort;
1483 conversationProfile->stunUsername() = stunUsername;
1484 conversationProfile->stunPassword() = stunPassword;
1485
1486 // Secure Media Settings
1487 conversationProfile->secureMediaMode() = secureMediaMode;
1488 conversationProfile->secureMediaRequired() = secureMediaRequired;
1489 conversationProfile->secureMediaDefaultCryptoSuite() = ConversationProfile::SRTP_AES_CM_128_HMAC_SHA1_80;
1490
1491 //////////////////////////////////////////////////////////////////////////////
1492 // Create ConverationManager and UserAgent
1493 //////////////////////////////////////////////////////////////////////////////
1494 {
1495 MyConversationManager myConversationManager(localAudioEnabled);
1496 MyUserAgent ua(&myConversationManager, profile);
1497 myConversationManager.buildSessionCapabilities(address, numCodecIds, codecIds, conversationProfile->sessionCaps());
1498 ua.addConversationProfile(conversationProfile);
1499
1500 //////////////////////////////////////////////////////////////////////////////
1501 // Startup and run...
1502 //////////////////////////////////////////////////////////////////////////////
1503
1504 ua.startup();
1505 myConversationManager.startup();
1506
1507 //ua.createSubscription("message-summary", uri, 120, Mime("application", "simple-message-summary")); // thread safe
1508
1509 int input;
1510 while(true)
1511 {
1512 ua.process(50);
1513 while(_kbhit() != 0)
1514 {
1515 #ifdef WIN32
1516 input = _getch();
1517 processKeyboard(input, myConversationManager, ua);
1518 #else
1519 input = fgetc(stdin);
1520 fflush(stdin);
1521 //cout << "input: " << input << endl;
1522 processKeyboard(input, myConversationManager, ua);
1523 #endif
1524 }
1525 if(finished) break;
1526 }
1527
1528 ua.shutdown();
1529 }
1530 InfoLog(<< "testUA is shutdown.");
1531 OsSysLog::shutdown();
1532 sleepSeconds(2);
1533
1534 #if defined(WIN32) && defined(_DEBUG) && defined(LEAK_CHECK)
1535 } // end FML scope
1536 #endif
1537 }
1538
1539
1540 /* ====================================================================
1541
1542 Copyright (c) 2007-2008, Plantronics, Inc.
1543 All rights reserved.
1544
1545 Redistribution and use in source and binary forms, with or without
1546 modification, are permitted provided that the following conditions are
1547 met:
1548
1549 1. Redistributions of source code must retain the above copyright
1550 notice, this list of conditions and the following disclaimer.
1551
1552 2. Redistributions in binary form must reproduce the above copyright
1553 notice, this list of conditions and the following disclaimer in the
1554 documentation and/or other materials provided with the distribution.
1555
1556 3. Neither the name of Plantronics nor the names of its contributors
1557 may be used to endorse or promote products derived from this
1558 software without specific prior written permission.
1559
1560 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1561 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1562 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1563 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1564 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1565 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1566 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1567 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1568 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1569 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1570 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1571
1572 ==================================================================== */

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.26