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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11042 - (hide annotations) (download)
Wed Mar 19 12:43:36 2014 UTC (5 years, 8 months ago) by Dpocock
File MIME type: text/plain
File size: 59826 byte(s)
resip/recon: testUA: remove duplicate definition of sleepSeconds()
1 dpocock 9493 #ifdef HAVE_CONFIG_H
2     #include "config.h"
3     #endif
4    
5 sgodin 7686 #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 sgodin 9003 #ifndef __GNUC__
17     #include <stropts.h>
18     #endif
19 sgodin 7686 #include <sys/ioctl.h>
20 sgodin 7759
21 sgodin 7686 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 sgodin 8219 #include "../ReconSubsystem.hxx"
43 sgodin 7686
44 sgodin 8401 #include <os/OsSysLog.h>
45    
46 sgodin 7686 // 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 Dpocock 11042 #include <rutil/Time.hxx>
55 sgodin 7686 #include <rutil/WinLeakCheck.hxx>
56    
57 sgodin 8219 using namespace recon;
58 sgodin 7686 using namespace resip;
59     using namespace std;
60    
61 sgodin 8219 #define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
62 sgodin 7686
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 sgodin 8401 MyConversationManager(bool localAudioEnabled)
102     : ConversationManager(localAudioEnabled),
103     mLocalAudioEnabled(localAudioEnabled)
104 sgodin 7686 {
105     };
106    
107     virtual void startup()
108     {
109 sgodin 8401 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 sgodin 7686 // 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 sgodin 8991 virtual ParticipantHandle createMediaResourceParticipant(ConversationHandle convHandle, const Uri& mediaUrl)
151 sgodin 7686 {
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 sgodin 8294 virtual void onDtmfEvent(ParticipantHandle partHandle, int dtmf, int duration, bool up)
180 sgodin 7686 {
181 sgodin 8294 InfoLog(<< "onDtmfEvent: handle=" << partHandle << " tone=" << dtmf << " dur=" << duration << " up=" << up);
182 sgodin 7686 }
183    
184 sgodin 8935 virtual void onIncomingParticipant(ParticipantHandle partHandle, const SipMessage& msg, bool autoAnswer, ConversationProfile& conversationProfile)
185 sgodin 7686 {
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 sgodin 8935 virtual void onRequestOutgoingParticipant(ParticipantHandle partHandle, const SipMessage& msg, ConversationProfile& conversationProfile)
207 sgodin 7686 {
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 sgodin 8401 bool mLocalAudioEnabled;
307 sgodin 7686 };
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 sgodin 8736 #ifdef USE_SSL
730 sgodin 7686 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 sgodin 8736 #endif
740 sgodin 7686 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 sgodin 8736 #ifdef USE_SSL
767 sgodin 7686 else if(isEqualNoCase(arg[0], "TlsAlloc"))
768     {
769     natTraversalMode = ConversationProfile::TurnTlsAllocation;
770     }
771 sgodin 8736 #endif
772 sgodin 7686 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 sgodin 8174 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 sgodin 7686
859 sgodin 8915 #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 sgodin 7686 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 sgodin 8914 << " createLocalParticipant: <'createlocal'|'clp'>" << endl
873 sgodin 7686 << " 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 sgodin 8915 << setSecureMediaMode << endl
901     << setNATTraversalMode << endl
902 sgodin 7686 << " 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 sgodin 8174 << " logDnsCache: <'dns'|'ld'>" << endl
908     << " clearDnsCache: <'cleardns'|'cd'>" << endl
909 sgodin 7686 << " 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 sgodin 7759 #else
934     // note: This is bit of a hack and may not be portable to all linux terminal types
935 sgodin 7760 cout << "\b\b\b \b\b\b";
936 sgodin 7759 fflush(stdout);
937 sgodin 7686 #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 sgodin 8524 #endif
968    
969     #if defined(WIN32) && defined(_DEBUG) && defined(LEAK_CHECK)
970 sgodin 7686 resip::FindMemoryLeaks fml;
971 sgodin 8524 {
972 sgodin 7686 #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 sgodin 8401 bool localAudioEnabled = true;
1000 sgodin 7686 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 sgodin 8113 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 sgodin 7686 SdpCodec::SDP_CODEC_ILBC /* 108 - iLBC */,
1013 sgodin 8213 SdpCodec::SDP_CODEC_ILBC_20MS /* 109 - Internet Low Bit Rate Codec, 20ms (RFC3951) */,
1014 sgodin 8381 SdpCodec::SDP_CODEC_SPEEX_5 /* 97 - speex NB 5,950bps */,
1015 sgodin 7686 SdpCodec::SDP_CODEC_GSM /* 3 - GSM */,
1016 dpocock 10157 //SdpCodec::SDP_CODEC_G722 /* 9 - G.722 */,
1017 sgodin 8213 SdpCodec::SDP_CODEC_TONES /* 110 - telephone-event */};
1018 sgodin 7686 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 sgodin 8915 #ifdef USE_SSL
1041 sgodin 7686 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 sgodin 8915 #endif
1044 sgodin 7686 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 sgodin 8736 #ifdef USE_SSL
1047 sgodin 7686 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 sgodin 8736 #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 sgodin 7686 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 sgodin 8401 cout << " -nl - no local audio support - removed local sound hardware requirement" << endl;
1057 sgodin 7686 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 sgodin 8401 else if(isEqualNoCase(commandName, "-nl"))
1076     {
1077     localAudioEnabled = false;
1078     }
1079 sgodin 7686 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 sgodin 8736 #ifdef USE_SSL
1127 sgodin 7686 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 sgodin 8736 #endif
1137 sgodin 7686 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 sgodin 8736 #ifdef USE_SSL
1158 sgodin 7686 else if(isEqualNoCase(commandValue, "TlsAlloc"))
1159     {
1160     natTraversalMode = ConversationProfile::TurnTlsAllocation;
1161     }
1162 sgodin 8736 #endif
1163 sgodin 7686 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 sgodin 8401 OsSysLog::initialize(0, "testUA");
1241     OsSysLog::setOutputFile(0, "sipXtapilog.txt") ;
1242 dpocock 10154 //OsSysLog::enableConsoleOutput(true);
1243     //OsSysLog::setLoggingPriority(PRI_DEBUG);
1244 sgodin 7686 Log::initialize("Cout", logLevel, "testUA");
1245     //UserAgent::setLogLevel(Log::Warning, UserAgent::SubsystemAll);
1246 sgodin 8219 //UserAgent::setLogLevel(Log::Info, UserAgent::SubsystemRecon);
1247 sgodin 7686
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 sgodin 8915 #ifdef USE_SSL
1266 sgodin 7686 InfoLog( << " TLS Port = " << tlsPort);
1267     InfoLog( << " TLS Domain = " << tlsDomain);
1268 sgodin 8915 #endif
1269 sgodin 7686 InfoLog( << " Outbound Proxy = " << outboundProxy);
1270 sgodin 8401 InfoLog( << " Local Audio Enabled = " << (localAudioEnabled ? "true" : "false"));
1271 sgodin 7686 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 sgodin 8915 #ifdef USE_SSL
1285 sgodin 7686 profile->addTransport(TLS, tlsPort, V4, address, tlsDomain);
1286 sgodin 8915 #endif
1287 sgodin 7686
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 sgodin 8540 // connect with no bind. By setting a fixed transport interface here that
1294 sgodin 7686 // code will not be used.
1295     // The following line can be safely removed for other platforms
1296 sgodin 8540 //profile->setFixedTransportInterface(address);
1297 sgodin 7686
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 sgodin 8401 conversationProfile->setDefaultRegistrationRetryTime(120); // 2 mins
1428 sgodin 7686 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 dpocock 10157 // For G.722, it is necessary to patch sipXmediaLib/src/mp/codecs/plgg722/plgg722.c
1444 dpocock 10167 // #define USE_8K_SAMPLES G722_SAMPLE_RATE_8000
1445 dpocock 10157 // and change sample rate from 16000 to 8000
1446     // (tested against a Polycom device configured for G.722 8000)
1447 dpocock 10167 // 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 dpocock 10157 //SdpContents::Session::Codec g722codec("G722", 8000);
1452     //g722codec.payloadType() = 9; /* RFC3551 */ ;
1453     //medium.addCodec(g722codec);
1454 sgodin 7686 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 sgodin 8445 conversationProfile->sessionCaps().session() = session;
1477 sgodin 7686 #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 sgodin 8401 MyConversationManager myConversationManager(localAudioEnabled);
1496 sgodin 7686 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 sgodin 8401 OsSysLog::shutdown();
1532 sgodin 7686 sleepSeconds(2);
1533 sgodin 8524
1534     #if defined(WIN32) && defined(_DEBUG) && defined(LEAK_CHECK)
1535     } // end FML scope
1536     #endif
1537 sgodin 7686 }
1538    
1539    
1540     /* ====================================================================
1541    
1542 sgodin 7722 Copyright (c) 2007-2008, Plantronics, Inc.
1543     All rights reserved.
1544 sgodin 7686
1545     Redistribution and use in source and binary forms, with or without
1546 sgodin 7722 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 sgodin 7686 2. Redistributions in binary form must reproduce the above copyright
1553 sgodin 7722 notice, this list of conditions and the following disclaimer in the
1554     documentation and/or other materials provided with the distribution.
1555 sgodin 7686
1556 sgodin 7722 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 sgodin 7686 ==================================================================== */

Properties

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

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27