blob: f3f9b7008bff59ce8ed4c9bc124d70b7c1ac9859 [file] [log] [blame]
Benny Prijonoba5926a2007-05-02 11:29:37 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijonoba5926a2007-05-02 11:29:37 +000019#include <pjsua-lib/pjsua.h>
Benny Prijono72a81aa2007-05-02 23:06:11 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijonoba5926a2007-05-02 11:29:37 +000021#include "ua.h"
22
23#define THIS_FILE "symbian_ua.cpp"
24
Benny Prijono72a81aa2007-05-02 23:06:11 +000025//
Benny Prijonoc71ad432007-05-04 07:25:19 +000026// Basic config.
27//
28#define SIP_PORT 5060
29
30
31//
Benny Prijonob2c96822007-05-03 13:31:21 +000032// Destination URI (to make call, or to subscribe presence)
33//
Benny Prijono897f9f82007-05-03 19:56:21 +000034#define SIP_DST_URI "sip:192.168.0.7:5061"
Benny Prijonob2c96822007-05-03 13:31:21 +000035
36//
Benny Prijono72a81aa2007-05-02 23:06:11 +000037// Account
38//
39#define HAS_SIP_ACCOUNT 0 // 0 to disable registration
Benny Prijono897f9f82007-05-03 19:56:21 +000040#define SIP_DOMAIN "server"
41#define SIP_USER "user"
42#define SIP_PASSWD "password"
Benny Prijonoba5926a2007-05-02 11:29:37 +000043
Benny Prijono72a81aa2007-05-02 23:06:11 +000044//
45// Outbound proxy for all accounts
46//
47#define SIP_PROXY NULL
Benny Prijono897f9f82007-05-03 19:56:21 +000048//#define SIP_PROXY "sip:192.168.0.8"
49
50
51//
52// Configure nameserver if DNS SRV is to be used with both SIP
53// or STUN (for STUN see other settings below)
54//
55#define NAMESERVER NULL
56//#define NAMESERVER "62.241.163.201"
57
58//
59// STUN server
60#if 0
61 // Use this to have the STUN server resolved normally
62# define STUN_DOMAIN NULL
63# define STUN_SERVER "stun.fwdnet.net"
64#elif 0
65 // Use this to have the STUN server resolved with DNS SRV
66# define STUN_DOMAIN "iptel.org"
67# define STUN_SERVER NULL
68#else
69 // Use this to disable STUN
70# define STUN_DOMAIN NULL
71# define STUN_SERVER NULL
72#endif
73
74//
75// Use ICE?
76//
77#define USE_ICE 1
Benny Prijono72a81aa2007-05-02 23:06:11 +000078
79
Benny Prijonob2c96822007-05-03 13:31:21 +000080//
81// Globals
82//
83static pjsua_acc_id g_acc_id = PJSUA_INVALID_ID;
84static pjsua_call_id g_call_id = PJSUA_INVALID_ID;
85static pjsua_buddy_id g_buddy_id = PJSUA_INVALID_ID;
Benny Prijono72a81aa2007-05-02 23:06:11 +000086
Benny Prijonoba5926a2007-05-02 11:29:37 +000087
88/* Callback called by the library upon receiving incoming call */
89static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
90 pjsip_rx_data *rdata)
91{
92 pjsua_call_info ci;
93
94 PJ_UNUSED_ARG(acc_id);
95 PJ_UNUSED_ARG(rdata);
96
Benny Prijonob2c96822007-05-03 13:31:21 +000097 if (g_call_id != PJSUA_INVALID_ID) {
98 pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, NULL, NULL);
99 return;
100 }
101
Benny Prijonoba5926a2007-05-02 11:29:37 +0000102 pjsua_call_get_info(call_id, &ci);
103
104 PJ_LOG(3,(THIS_FILE, "Incoming call from %.*s!!",
105 (int)ci.remote_info.slen,
106 ci.remote_info.ptr));
107
Benny Prijonob2c96822007-05-03 13:31:21 +0000108 g_call_id = call_id;
109
Benny Prijono897f9f82007-05-03 19:56:21 +0000110 /* Automatically answer incoming calls with 180/Ringing */
111 pjsua_call_answer(call_id, 180, NULL, NULL);
Benny Prijonoba5926a2007-05-02 11:29:37 +0000112}
113
114/* Callback called by the library when call's state has changed */
115static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
116{
117 pjsua_call_info ci;
118
119 PJ_UNUSED_ARG(e);
120
121 pjsua_call_get_info(call_id, &ci);
Benny Prijonob2c96822007-05-03 13:31:21 +0000122
123 if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
124 if (call_id == g_call_id)
125 g_call_id = PJSUA_INVALID_ID;
Benny Prijono897f9f82007-05-03 19:56:21 +0000126 } else if (ci.state != PJSIP_INV_STATE_INCOMING) {
Benny Prijonob2c96822007-05-03 13:31:21 +0000127 if (g_call_id == PJSUA_INVALID_ID)
128 g_call_id = call_id;
129 }
130
Benny Prijonoba5926a2007-05-02 11:29:37 +0000131 PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,
132 (int)ci.state_text.slen,
133 ci.state_text.ptr));
134}
135
136/* Callback called by the library when call's media state has changed */
137static void on_call_media_state(pjsua_call_id call_id)
138{
139 pjsua_call_info ci;
140
141 pjsua_call_get_info(call_id, &ci);
142
143 if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
144 // When media is active, connect call to sound device.
145 pjsua_conf_connect(ci.conf_slot, 0);
146 pjsua_conf_connect(0, ci.conf_slot);
147 }
148}
149
150
Benny Prijonob2c96822007-05-03 13:31:21 +0000151/* Handler on buddy state changed. */
152static void on_buddy_state(pjsua_buddy_id buddy_id)
153{
154 pjsua_buddy_info info;
155 pjsua_buddy_get_info(buddy_id, &info);
156
157 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
158 (int)info.uri.slen,
159 info.uri.ptr,
160 (int)info.status_text.slen,
161 info.status_text.ptr));
162}
163
164
165/* Incoming IM message (i.e. MESSAGE request)! */
166static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
167 const pj_str_t *to, const pj_str_t *contact,
168 const pj_str_t *mime_type, const pj_str_t *text)
169{
170 /* Note: call index may be -1 */
171 PJ_UNUSED_ARG(call_id);
172 PJ_UNUSED_ARG(to);
173 PJ_UNUSED_ARG(contact);
174 PJ_UNUSED_ARG(mime_type);
175
176 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s",
177 (int)from->slen, from->ptr,
178 (int)text->slen, text->ptr));
179}
180
181
182/* Received typing indication */
183static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
184 const pj_str_t *to, const pj_str_t *contact,
185 pj_bool_t is_typing)
186{
187 PJ_UNUSED_ARG(call_id);
188 PJ_UNUSED_ARG(to);
189 PJ_UNUSED_ARG(contact);
190
191 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
192 (int)from->slen, from->ptr,
193 (is_typing?"is typing..":"has stopped typing")));
194}
195
196
197/* Call transfer request status. */
198static void on_call_transfer_status(pjsua_call_id call_id,
199 int status_code,
200 const pj_str_t *status_text,
201 pj_bool_t final,
202 pj_bool_t *p_cont)
203{
204 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
205 call_id, status_code,
206 (int)status_text->slen, status_text->ptr,
207 (final ? "[final]" : "")));
208
209 if (status_code/100 == 2) {
210 PJ_LOG(3,(THIS_FILE,
211 "Call %d: call transfered successfully, disconnecting call",
212 call_id));
213 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
214 *p_cont = PJ_FALSE;
215 }
216}
217
218
219/* Notification that call is being replaced. */
220static void on_call_replaced(pjsua_call_id old_call_id,
221 pjsua_call_id new_call_id)
222{
223 pjsua_call_info old_ci, new_ci;
224
225 pjsua_call_get_info(old_call_id, &old_ci);
226 pjsua_call_get_info(new_call_id, &new_ci);
227
228 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
229 "call %d with %.*s",
230 old_call_id,
231 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
232 new_call_id,
233 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
234}
235
236
Benny Prijonoba5926a2007-05-02 11:29:37 +0000237/* Logging callback */
238static void log_writer(int level, const char *buf, unsigned len)
239{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000240 static wchar_t buf16[PJ_LOG_MAX_SIZE];
Benny Prijonoba5926a2007-05-02 11:29:37 +0000241
242 PJ_UNUSED_ARG(level);
243
244 pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
245
246 TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
247 console->Write(aBuf);
248}
249
250/*
251 * app_startup()
252 *
253 * url may contain URL to call.
254 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000255static pj_status_t app_startup()
Benny Prijonoba5926a2007-05-02 11:29:37 +0000256{
Benny Prijonoba5926a2007-05-02 11:29:37 +0000257 pj_status_t status;
258
259 /* Redirect log before pjsua_init() */
260 pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
261
262 /* Create pjsua first! */
263 status = pjsua_create();
264 if (status != PJ_SUCCESS) {
265 pjsua_perror(THIS_FILE, "pjsua_create() error", status);
266 return status;
267 }
268
Benny Prijonoba5926a2007-05-02 11:29:37 +0000269 /* Init pjsua */
Benny Prijonoc71ad432007-05-04 07:25:19 +0000270 pjsua_config cfg;
271 pjsua_logging_config log_cfg;
272 pjsua_media_config med_cfg;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000273
Benny Prijonoc71ad432007-05-04 07:25:19 +0000274 pjsua_config_default(&cfg);
275 cfg.max_calls = 2;
276 cfg.thread_cnt = 0; // Disable threading on Symbian
277 cfg.cb.on_incoming_call = &on_incoming_call;
278 cfg.cb.on_call_media_state = &on_call_media_state;
279 cfg.cb.on_call_state = &on_call_state;
280 cfg.cb.on_buddy_state = &on_buddy_state;
281 cfg.cb.on_pager = &on_pager;
282 cfg.cb.on_typing = &on_typing;
283 cfg.cb.on_call_transfer_status = &on_call_transfer_status;
284 cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000285
Benny Prijonoc71ad432007-05-04 07:25:19 +0000286 if (SIP_PROXY) {
287 cfg.outbound_proxy_cnt = 1;
288 cfg.outbound_proxy[0] = pj_str(SIP_PROXY);
289 }
290
291 if (NAMESERVER) {
292 cfg.nameserver_count = 1;
293 cfg.nameserver[0] = pj_str(NAMESERVER);
294 }
295
296 if (NAMESERVER && STUN_DOMAIN) {
297 cfg.stun_domain = pj_str(STUN_DOMAIN);
298 } else if (STUN_SERVER) {
299 cfg.stun_host = pj_str(STUN_SERVER);
300 }
301
302
303 pjsua_logging_config_default(&log_cfg);
304 log_cfg.console_level = 4;
305 log_cfg.cb = &log_writer;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000306
Benny Prijonoc71ad432007-05-04 07:25:19 +0000307 pjsua_media_config_default(&med_cfg);
308 med_cfg.thread_cnt = 0; // Disable threading on Symbian
309 med_cfg.has_ioqueue = PJ_FALSE;
310 med_cfg.clock_rate = 8000;
311 med_cfg.ec_tail_len = 0;
312 med_cfg.enable_ice = USE_ICE;
313
314 status = pjsua_init(&cfg, &log_cfg, &med_cfg);
315 if (status != PJ_SUCCESS) {
316 pjsua_perror(THIS_FILE, "pjsua_init() error", status);
317 pjsua_destroy();
318 return status;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000319 }
320
321 /* Add UDP transport. */
Benny Prijonoc71ad432007-05-04 07:25:19 +0000322 pjsua_transport_config tcfg;
323 pjsua_transport_id tid;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000324
Benny Prijonoc71ad432007-05-04 07:25:19 +0000325 pjsua_transport_config_default(&tcfg);
326 tcfg.port = SIP_PORT;
327 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &tcfg, &tid);
328 if (status != PJ_SUCCESS) {
329 pjsua_perror(THIS_FILE, "Error creating transport", status);
330 pjsua_destroy();
331 return status;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000332 }
333
Benny Prijonoc71ad432007-05-04 07:25:19 +0000334 /* Add account for the transport */
335 pjsua_acc_add_local(tid, PJ_TRUE, &g_acc_id);
336
337
Benny Prijonoba5926a2007-05-02 11:29:37 +0000338 /* Initialization is done, now start pjsua */
339 status = pjsua_start();
340 if (status != PJ_SUCCESS) {
341 pjsua_perror(THIS_FILE, "Error starting pjsua", status);
342 pjsua_destroy();
343 return status;
344 }
345
346 /* Register to SIP server by creating SIP account. */
Benny Prijono72a81aa2007-05-02 23:06:11 +0000347 if (HAS_SIP_ACCOUNT) {
Benny Prijonoba5926a2007-05-02 11:29:37 +0000348 pjsua_acc_config cfg;
349
350 pjsua_acc_config_default(&cfg);
351 cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
352 cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);
353 cfg.cred_count = 1;
354 cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);
355 cfg.cred_info[0].scheme = pj_str("digest");
356 cfg.cred_info[0].username = pj_str(SIP_USER);
357 cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
358 cfg.cred_info[0].data = pj_str(SIP_PASSWD);
359
Benny Prijonob2c96822007-05-03 13:31:21 +0000360 status = pjsua_acc_add(&cfg, PJ_TRUE, &g_acc_id);
Benny Prijonoba5926a2007-05-02 11:29:37 +0000361 if (status != PJ_SUCCESS) {
362 pjsua_perror(THIS_FILE, "Error adding account", status);
363 pjsua_destroy();
364 return status;
365 }
366 }
367
Benny Prijonob2c96822007-05-03 13:31:21 +0000368 if (SIP_DST_URI) {
369 pjsua_buddy_config bcfg;
370
371 pjsua_buddy_config_default(&bcfg);
372 bcfg.uri = pj_str(SIP_DST_URI);
373 bcfg.subscribe = PJ_FALSE;
374
375 pjsua_buddy_add(&bcfg, &g_buddy_id);
Benny Prijonoba5926a2007-05-02 11:29:37 +0000376 }
Benny Prijonoba5926a2007-05-02 11:29:37 +0000377 return PJ_SUCCESS;
378}
379
380
381////////////////////////////////////////////////////////////////////////////
Benny Prijonoc71ad432007-05-04 07:25:19 +0000382/*
383 * The interractive console UI
384 */
Benny Prijonoba5926a2007-05-02 11:29:37 +0000385#include <e32base.h>
386
387class ConsoleUI : public CActive
388{
389public:
Benny Prijonoc71ad432007-05-04 07:25:19 +0000390 ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
Benny Prijonoba5926a2007-05-02 11:29:37 +0000391
Benny Prijonoc71ad432007-05-04 07:25:19 +0000392 // Run console UI
393 void Run();
394
395 // Stop
396 void Stop();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000397
398protected:
Benny Prijonoc71ad432007-05-04 07:25:19 +0000399 // Cancel asynchronous read.
400 void DoCancel();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000401
Benny Prijonoc71ad432007-05-04 07:25:19 +0000402 // Implementation: called when read has completed.
403 void RunL();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000404
405private:
Benny Prijonoc71ad432007-05-04 07:25:19 +0000406 CActiveSchedulerWait *asw_;
407 CConsoleBase *con_;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000408};
409
410
411ConsoleUI::ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con)
412: CActive(EPriorityStandard), asw_(asw), con_(con)
413{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000414 CActiveScheduler::Add(this);
Benny Prijonoba5926a2007-05-02 11:29:37 +0000415}
416
417// Run console UI
418void ConsoleUI::Run()
419{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000420 con_->Read(iStatus);
421 SetActive();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000422}
423
424// Stop console UI
425void ConsoleUI::Stop()
426{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000427 DoCancel();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000428}
429
430// Cancel asynchronous read.
431void ConsoleUI::DoCancel()
432{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000433 con_->ReadCancel();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000434}
435
Benny Prijono72a81aa2007-05-02 23:06:11 +0000436static void PrintMenu()
437{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000438 PJ_LOG(3, (THIS_FILE, "\n\n"
439 "Menu:\n"
440 " d Dump states\n"
441 " D Dump all states (detail)\n"
442 " P Dump pool factory\n"
443 " m Make call to " SIP_DST_URI "\n"
444 " a Answer call\n"
445 " h Hangup all calls\n"
446 " s Subscribe to " SIP_DST_URI "\n"
447 " S Unsubscribe presence\n"
448 " o Set account online\n"
449 " O Set account offline\n"
450 " q Quit\n"));
Benny Prijono72a81aa2007-05-02 23:06:11 +0000451}
452
Benny Prijonoba5926a2007-05-02 11:29:37 +0000453// Implementation: called when read has completed.
454void ConsoleUI::RunL()
455{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000456 TKeyCode kc = con_->KeyCode();
457 pj_bool_t reschedule = PJ_TRUE;
458
459 switch (kc) {
460 case 'q':
461 asw_->AsyncStop();
462 reschedule = PJ_FALSE;
463 break;
464 case 'D':
465 case 'd':
466 pjsua_dump(kc == 'D');
467 break;
468 case 'p':
469 case 'P':
470 pj_pool_factory_dump(&pjsua_var.cp.factory, PJ_TRUE);
471 break;
472 case 'm':
473 if (g_call_id != PJSUA_INVALID_ID) {
474 PJ_LOG(3,(THIS_FILE, "Another call is active"));
475 break;
476 }
477
478 if (pjsua_verify_sip_url(SIP_DST_URI) == PJ_SUCCESS) {
479 pj_str_t dst = pj_str(SIP_DST_URI);
480 pjsua_call_make_call(g_acc_id, &dst, 0, NULL,
481 NULL, &g_call_id);
482 } else {
483 PJ_LOG(3,(THIS_FILE, "Invalid SIP URI"));
484 }
485 break;
486 case 'a':
487 if (g_call_id != PJSUA_INVALID_ID)
488 pjsua_call_answer(g_call_id, 200, NULL, NULL);
489 break;
490 case 'h':
491 pjsua_call_hangup_all();
492 break;
493 case 's':
494 case 'S':
495 if (g_buddy_id != PJSUA_INVALID_ID)
496 pjsua_buddy_subscribe_pres(g_buddy_id, kc=='s');
497 break;
498 case 'o':
499 case 'O':
500 pjsua_acc_set_online_status(g_acc_id, kc=='o');
501 break;
502 default:
503 PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed",
504 kc, kc));
505 break;
506 }
Benny Prijono72a81aa2007-05-02 23:06:11 +0000507
Benny Prijonoc71ad432007-05-04 07:25:19 +0000508 PrintMenu();
509
510 if (reschedule)
511 Run();
Benny Prijonoba5926a2007-05-02 11:29:37 +0000512}
513
Benny Prijono897f9f82007-05-03 19:56:21 +0000514
Benny Prijonoba5926a2007-05-02 11:29:37 +0000515////////////////////////////////////////////////////////////////////////////
516int ua_main()
517{
Benny Prijonoc71ad432007-05-04 07:25:19 +0000518 pj_status_t status;
519
520 // Initialize pjsua
521 status = app_startup();
522 if (status != PJ_SUCCESS)
523 return status;
Benny Prijono897f9f82007-05-03 19:56:21 +0000524
Benny Prijonoc71ad432007-05-04 07:25:19 +0000525 // Run the UI
526 CActiveSchedulerWait *asw = new CActiveSchedulerWait;
527 ConsoleUI *con = new ConsoleUI(asw, console);
528
529 con->Run();
530
531 PrintMenu();
532 asw->Start();
533
534 delete con;
535 delete asw;
536
537 // Shutdown pjsua
538 pjsua_destroy();
539
540 return 0;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000541}
542