Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip-apps / src / pocketpj / PocketPJDlg.cpp
1 // PocketPJDlg.cpp : implementation file
2 //
3
4 #include "stdafx.h"
5 #include "PocketPJ.h"
6 #include "PocketPJDlg.h"
7 #include <iphlpapi.h>
8
9 #ifdef _DEBUG
10 #define new DEBUG_NEW
11 #undef THIS_FILE
12 static char THIS_FILE[] = __FILE__;
13 #endif
14
15 #define TIMER_ID    101
16 static CPocketPJDlg *theDlg;
17
18 /////////////////////////////////////////////////////////////////////////////
19 // CPocketPJDlg dialog
20
21 CPocketPJDlg::CPocketPJDlg(CWnd* pParent /*=NULL*/)
22         : CDialog(CPocketPJDlg::IDD, pParent), m_PopUp(NULL)
23 {
24         //{{AFX_DATA_INIT(CPocketPJDlg)
25                 // NOTE: the ClassWizard will add member initialization here
26         //}}AFX_DATA_INIT
27         // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
28         m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
29
30         theDlg = this;
31
32         m_PopUp = new CPopUpWnd(this);
33         m_PopUp->Hide();
34
35         unsigned i;
36         m_PopUpCount = 0;
37         for (i=0; i<POPUP_MAX_TYPE; ++i) {
38             m_PopUpState[i] = FALSE;
39         }
40 }
41
42 void CPocketPJDlg::DoDataExchange(CDataExchange* pDX)
43 {
44         CDialog::DoDataExchange(pDX);
45         //{{AFX_DATA_MAP(CPocketPJDlg)
46         DDX_Control(pDX, IDC_URL, m_Url);
47         DDX_Control(pDX, IDC_BUDDY_LIST, m_BuddyList);
48         DDX_Control(pDX, IDC_BTN_ACTION, m_BtnUrlAction);
49         DDX_Control(pDX, IDC_BTN_ACC, m_BtnAcc);
50         DDX_Control(pDX, IDC_ACC_ID, m_AccId);
51         //}}AFX_DATA_MAP
52 }
53
54 BEGIN_MESSAGE_MAP(CPocketPJDlg, CDialog)
55         //{{AFX_MSG_MAP(CPocketPJDlg)
56         ON_BN_CLICKED(IDC_BTN_ACC, OnBtnAcc)
57         ON_BN_CLICKED(IDC_BTN_ACTION, OnBtnAction)
58         ON_COMMAND(IDC_ACC_SETTINGS, OnSettings)
59         ON_COMMAND(IDC_URI_CALL, OnUriCall)
60         ON_WM_TIMER()
61         ON_COMMAND(IDC_URI_ADD_BUDDY, OnUriAddBuddy)
62         ON_COMMAND(IDC_URI_DEL_BUDDY, OnUriDelBuddy)
63         ON_COMMAND(IDC_ACC_ONLINE, OnAccOnline)
64         ON_COMMAND(IDC_ACC_INVISIBLE, OnAccInvisible)
65         ON_NOTIFY(NM_CLICK, IDC_BUDDY_LIST, OnClickBuddyList)
66         //}}AFX_MSG_MAP
67 END_MESSAGE_MAP()
68
69
70 void CPocketPJDlg::Error(const CString &title, pj_status_t rc)
71 {
72     char errmsg[PJ_ERR_MSG_SIZE];
73     wchar_t werrmsg[PJ_ERR_MSG_SIZE];
74
75     pj_strerror(rc, errmsg, sizeof(errmsg));
76     pj_ansi_to_unicode(errmsg, strlen(errmsg), werrmsg, PJ_ARRAY_SIZE(werrmsg));
77
78     AfxMessageBox(title + _T(": ") + werrmsg);
79 }
80
81 BOOL CPocketPJDlg::Restart()
82 {
83     unsigned i;
84     pj_status_t status;
85
86     char ver[80];
87     sprintf(ver, "PocketPJ/%s", pj_get_version());
88
89     ShowWindow(SW_SHOW);
90     PopUp_Show(POPUP_REGISTRATION, ver,
91                "Starting up....", "", "", "", 0);
92
93     KillTimer(TIMER_ID);
94
95     // Destroy first.
96     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Cleaning up..");
97     pjsua_destroy();
98
99     m_BtnAcc.SetBitmap(::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_OFFLINE)) );
100     UpdateWindow();
101
102
103     // Create
104     PopUp_Show(POPUP_REGISTRATION, ver,
105                "Starting up....", "Creating stack..", "", "", 0);
106
107     status = pjsua_create();
108     if (status != PJ_SUCCESS) {
109         Error(_T("Error in creating library"), status);
110         PopUp_Hide(POPUP_REGISTRATION);
111         return FALSE;
112     }
113
114     pjsua_config cfg;
115     pjsua_logging_config log_cfg;
116     pjsua_media_config media_cfg;
117
118     pjsua_config_default(&cfg);
119     cfg.max_calls = 1;
120     cfg.thread_cnt = 0;
121     cfg.user_agent = pj_str(ver);
122
123     cfg.cb.on_call_state = &on_call_state;
124     cfg.cb.on_call_media_state = &on_call_media_state;
125     cfg.cb.on_incoming_call = &on_incoming_call;
126     cfg.cb.on_reg_state = &on_reg_state;
127     cfg.cb.on_buddy_state = &on_buddy_state;
128     cfg.cb.on_pager = &on_pager;
129
130     /* Configure nameserver */
131     char nameserver[60];
132     {
133         FIXED_INFO fi;
134         PIP_ADDR_STRING pDNS = NULL;
135         ULONG len = sizeof(fi);
136         CString err;
137
138         PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Retrieving network parameters..");
139         if (GetNetworkParams(&fi, &len) != ERROR_SUCCESS) {
140             err = _T("Info: Error querying network parameters. You must configure DNS server.");
141         } else if (fi.CurrentDnsServer) {
142             pDNS = fi.CurrentDnsServer;
143         } else if (fi.DnsServerList.IpAddress.String[0] != 0) {
144             pDNS = &fi.DnsServerList;
145         } else {
146             err = _T("Info: DNS server not configured. You must configure DNS server.");
147         } 
148         
149         if (err.GetLength()) {
150             if (m_Cfg.m_DNS.GetLength()) {
151                 pj_unicode_to_ansi((LPCTSTR)m_Cfg.m_DNS, m_Cfg.m_DNS.GetLength(),
152                                    nameserver, sizeof(nameserver));
153                 cfg.nameserver_count = 1;
154                 cfg.nameserver[0] = pj_str(nameserver);
155             } else {
156                 AfxMessageBox(err);
157                 pjsua_destroy();
158                 PopUp_Hide(POPUP_REGISTRATION);
159                 return FALSE;
160             }
161         } else {
162             strcpy(nameserver, pDNS->IpAddress.String);
163             cfg.nameserver_count = 1;
164             cfg.nameserver[0] = pj_str(nameserver);
165         }
166     }
167
168     char tmp_stun[80];
169     if (m_Cfg.m_UseStun) {
170         pj_unicode_to_ansi((LPCTSTR)m_Cfg.m_StunSrv, m_Cfg.m_StunSrv.GetLength(),
171                            tmp_stun, sizeof(tmp_stun));
172         cfg.stun_host = pj_str(tmp_stun);
173     }
174
175     pjsua_logging_config_default(&log_cfg);
176     log_cfg.msg_logging = PJ_TRUE;
177     log_cfg.log_filename = pj_str("\\PocketPJ.TXT");
178
179     pjsua_media_config_default(&media_cfg);
180     media_cfg.clock_rate = 8000;
181     media_cfg.audio_frame_ptime = 40;
182     media_cfg.ec_tail_len = 0;
183     media_cfg.ilbc_mode = 30;
184     media_cfg.max_media_ports = 8;
185     // use default quality setting
186     //media_cfg.quality = 5;
187     media_cfg.thread_cnt = 1;
188     media_cfg.enable_ice = m_Cfg.m_UseIce;
189     media_cfg.no_vad = !m_Cfg.m_VAD;
190
191     if (m_Cfg.m_EchoSuppress) {
192         media_cfg.ec_options = PJMEDIA_ECHO_SIMPLE;
193         media_cfg.ec_tail_len = m_Cfg.m_EcTail;
194     }
195
196     // Init
197     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Initializing..");
198     status = pjsua_init(&cfg, &log_cfg, &media_cfg);
199     if (status != PJ_SUCCESS) {
200         Error(_T("Error initializing library"), status);
201         pjsua_destroy();
202         PopUp_Hide(POPUP_REGISTRATION);
203         return FALSE;
204     }
205
206     // Create one UDP transport
207     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Adding UDP transport..");
208     pjsua_transport_id transport_id;
209     pjsua_transport_config udp_cfg;
210
211     pjsua_transport_config_default(&udp_cfg);
212     udp_cfg.port = 0;
213     status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
214                                         &udp_cfg, &transport_id);
215     if (status != PJ_SUCCESS) {
216         Error(_T("Error creating UDP transport"), status);
217         pjsua_destroy();
218         PopUp_Hide(POPUP_REGISTRATION);
219         return FALSE;
220     }
221
222     // Always instantiate TCP to support auto-switching to TCP when
223     // packet is larger than 1300 bytes. If TCP is disabled when
224     // no auto-switching will occur
225     if (1) {
226         // Create one TCP transport
227         PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Adding TCP transport..");
228         pjsua_transport_id transport_id;
229         pjsua_transport_config tcp_cfg;
230
231         pjsua_transport_config_default(&tcp_cfg);
232         tcp_cfg.port = 0;
233         status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
234                                         &tcp_cfg, &transport_id);
235         if (status != PJ_SUCCESS) {
236             Error(_T("Error creating TCP transport"), status);
237             pjsua_destroy();
238             PopUp_Hide(POPUP_REGISTRATION);
239             return FALSE;
240         }
241     }
242
243     // Adjust codecs priority
244     pj_str_t tmp;
245     pjsua_codec_set_priority(pj_cstr(&tmp, "*"), 0);
246     for (i=0; i<(unsigned)m_Cfg.m_Codecs.GetSize(); ++i) {
247         CString codec = m_Cfg.m_Codecs.GetAt(i);
248         char tmp_nam[80];
249
250         pj_unicode_to_ansi((LPCTSTR)codec, codec.GetLength(),
251                            tmp_nam, sizeof(tmp_nam));
252         pjsua_codec_set_priority(pj_cstr(&tmp, tmp_nam), (pj_uint8_t)(200-i));
253     }
254
255     // Start!
256     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Starting..");
257     status = pjsua_start();
258     if (status != PJ_SUCCESS) {
259         Error(_T("Error starting library"), status);
260         pjsua_destroy();
261         PopUp_Hide(POPUP_REGISTRATION);
262         return FALSE;
263     }
264
265     // Add account
266     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "Adding account..");
267     char domain[80], username[80], passwd[80];
268     char id[80], reg_uri[80];
269     pjsua_acc_config acc_cfg;
270
271     pj_unicode_to_ansi((LPCTSTR)m_Cfg.m_Domain, m_Cfg.m_Domain.GetLength(),
272                        domain, sizeof(domain));
273     pj_unicode_to_ansi((LPCTSTR)m_Cfg.m_User, m_Cfg.m_User.GetLength(),
274                        username, sizeof(username));
275     pj_unicode_to_ansi((LPCTSTR)m_Cfg.m_Password, m_Cfg.m_Password.GetLength(),
276                        passwd, sizeof(passwd));
277
278     snprintf(id, sizeof(id), "<sip:%s@%s>", username, domain);
279     snprintf(reg_uri, sizeof(reg_uri), "sip:%s", domain);
280
281     pjsua_acc_config_default(&acc_cfg);
282     acc_cfg.id = pj_str(id);
283     acc_cfg.reg_uri = pj_str(reg_uri);
284     acc_cfg.cred_count = 1;
285     acc_cfg.cred_info[0].scheme = pj_str("Digest");
286     acc_cfg.cred_info[0].realm = pj_str("*");
287     acc_cfg.cred_info[0].username = pj_str(username);
288     acc_cfg.cred_info[0].data_type = 0;
289     acc_cfg.cred_info[0].data = pj_str(passwd);
290
291 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
292     acc_cfg.use_srtp = (m_Cfg.m_UseSrtp ? PJMEDIA_SRTP_OPTIONAL : PJMEDIA_SRTP_DISABLED);
293     acc_cfg.srtp_secure_signaling = 0;
294 #endif
295
296     acc_cfg.publish_enabled = m_Cfg.m_UsePublish;
297     
298     char route[80];
299     if (m_Cfg.m_TCP) {
300         snprintf(route, sizeof(route), "<sip:%s;lr;transport=tcp>", domain);
301         acc_cfg.proxy[acc_cfg.proxy_cnt++] = pj_str(route);
302     } else {
303         snprintf(route, sizeof(route), "<sip:%s;lr>", domain);
304         acc_cfg.proxy[acc_cfg.proxy_cnt++] = pj_str(route);
305     }
306
307     status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &m_PjsuaAccId);
308     if (status != PJ_SUCCESS) {
309         Error(_T("Invalid account settings"), status);
310         pjsua_destroy();
311         PopUp_Hide(POPUP_REGISTRATION);
312         return FALSE;
313     }
314
315     CString acc_text = m_Cfg.m_User + _T("@") + m_Cfg.m_Domain;
316     m_AccId.SetWindowText(acc_text);
317
318     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE1, acc_text);
319     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE2, "Registering..");
320     PopUp_Modify(POPUP_REGISTRATION, POPUP_EL_TITLE3, "");
321
322     SetTimer(TIMER_ID, 100, NULL);
323     return TRUE;
324 }
325
326
327 void CPocketPJDlg::PopUp_Show( PopUpType type, 
328                                 const CString& title1,
329                                 const CString& title2,
330                                 const CString& title3,
331                                 const CString& btn1,
332                                 const CString& btn2,
333                                 unsigned userData)
334 {
335     PJ_UNUSED_ARG(userData);
336
337     if (!m_PopUpState[type])
338         ++m_PopUpCount;
339
340     m_PopUpState[type] = TRUE;
341
342     m_PopUpContent[type].m_Title1 = title1;
343     m_PopUpContent[type].m_Title2 = title2;
344     m_PopUpContent[type].m_Title3 = title3;
345     m_PopUpContent[type].m_Btn1 = btn1;
346     m_PopUpContent[type].m_Btn2 = btn2;
347
348     m_PopUp->SetContent(m_PopUpContent[type]);
349     m_PopUp->Show();
350 }
351
352 void CPocketPJDlg::PopUp_Modify(PopUpType type,
353                                 PopUpElement el,
354                                 const CString& text)
355 {
356     switch (el) {
357     case POPUP_EL_TITLE1:
358         m_PopUpContent[type].m_Title1 = text;
359         break;
360     case POPUP_EL_TITLE2:
361         m_PopUpContent[type].m_Title2 = text;
362         break;
363     case POPUP_EL_TITLE3:
364         m_PopUpContent[type].m_Title3 = text;
365         break;
366     case POPUP_EL_BUTTON1:
367         m_PopUpContent[type].m_Btn1 = text;
368         break;
369     case POPUP_EL_BUTTON2:
370         m_PopUpContent[type].m_Btn1 = text;
371         break;
372     }
373
374     m_PopUp->SetContent(m_PopUpContent[type]);
375 }
376
377 void CPocketPJDlg::PopUp_Hide(PopUpType type)
378 {
379     if (m_PopUpState[type])
380         --m_PopUpCount;
381
382     m_PopUpState[type] = FALSE;
383
384     if (m_PopUpCount == 0) {
385         m_PopUp->Hide();
386         UpdateWindow();
387     } else {
388         for (int i=POPUP_MAX_TYPE-1; i>=0; --i) {
389             if (m_PopUpState[i]) {
390                 m_PopUp->SetContent(m_PopUpContent[i]);
391                 break;
392             }
393         }
394     }
395 }
396
397 void CPocketPJDlg::OnIncomingCall()
398 {
399     pjsua_call_info ci;
400
401     pjsua_call_get_info(0, &ci);
402
403     PopUp_Show(POPUP_CALL, "Incoming call..", ci.remote_info.ptr, "",
404                "Answer", "Hangup", 0);
405     pjsua_call_answer(0, 180, NULL, NULL);
406     if (m_Cfg.m_AutoAnswer)
407         OnPopUpButton(1);
408 }
409
410 void CPocketPJDlg::OnCallState()
411 {
412     pjsua_call_info ci;
413
414     pjsua_call_get_info(0, &ci);
415     
416     switch (ci.state) {
417     case PJSIP_INV_STATE_NULL:      /**< Before INVITE is sent or received  */
418         break;
419     case PJSIP_INV_STATE_CALLING:   /**< After INVITE is sent               */
420         PopUp_Show(POPUP_CALL, "Calling..", ci.remote_info.ptr, "",
421                    "", "Hangup", 0);
422         break;
423     case PJSIP_INV_STATE_INCOMING:  /**< After INVITE is received.          */
424         OnIncomingCall();
425         break;
426     case PJSIP_INV_STATE_EARLY:     /**< After response with To tag.        */
427     case PJSIP_INV_STATE_CONNECTING:/**< After 2xx is sent/received.        */
428     case PJSIP_INV_STATE_CONFIRMED:  /**< After ACK is sent/received.       */
429         {
430             CString stateText = ci.state_text.ptr;
431             PopUp_Modify(POPUP_CALL, POPUP_EL_TITLE3, stateText);
432         }
433         break;
434     case PJSIP_INV_STATE_DISCONNECTED:/**< Session is terminated.           */
435         PopUp_Modify(POPUP_CALL, POPUP_EL_TITLE3, "Disconnected");
436         PopUp_Hide(POPUP_CALL);
437         break;
438     }    
439 }
440
441 void CPocketPJDlg::on_call_state(pjsua_call_id call_id, pjsip_event *e)
442 {
443     PJ_UNUSED_ARG(e);
444     PJ_UNUSED_ARG(call_id);
445
446     theDlg->OnCallState();
447 }
448
449 void CPocketPJDlg::on_call_media_state(pjsua_call_id call_id)
450 {
451     pjsua_call_info call_info;
452
453     pjsua_call_get_info(call_id, &call_info);
454     if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
455         pjsua_conf_connect(call_info.conf_slot, 0);
456         pjsua_conf_connect(0, call_info.conf_slot);
457     }
458 }
459
460 void CPocketPJDlg::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
461                                     pjsip_rx_data *rdata)
462 {
463     PJ_UNUSED_ARG(acc_id);
464     PJ_UNUSED_ARG(call_id);
465     PJ_UNUSED_ARG(rdata);
466
467     theDlg->OnIncomingCall();
468 }
469
470 void CPocketPJDlg::OnRegState()
471 {
472     pjsua_acc_info ai;
473     pjsua_acc_get_info(m_PjsuaAccId, &ai);
474
475     CString acc_text = m_Cfg.m_User + _T("@") + m_Cfg.m_Domain;
476
477     if (ai.expires>0 && ai.status/100==2) {
478         /* Registration success */
479         HBITMAP old = m_BtnAcc.SetBitmap(::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_ONLINE)) );
480         PJ_UNUSED_ARG(old);
481         acc_text += " (OK)";
482         m_AccId.SetWindowText(acc_text);
483     } else if (ai.status/100 != 2) {
484         acc_text += " (err)";
485         Error(_T("SIP registration error"), PJSIP_ERRNO_FROM_SIP_STATUS(ai.status));
486         m_AccId.SetWindowText(acc_text);
487     }
488     PopUp_Hide(POPUP_REGISTRATION);
489 }
490
491 void CPocketPJDlg::on_reg_state(pjsua_acc_id acc_id)
492 {
493     PJ_UNUSED_ARG(acc_id);
494
495     theDlg->OnRegState();
496 }
497
498 void CPocketPJDlg::on_buddy_state(pjsua_buddy_id buddy_id)
499 {
500     PJ_UNUSED_ARG(buddy_id);
501
502     theDlg->RedrawBuddyList();
503 }
504
505 void CPocketPJDlg::on_pager(pjsua_call_id call_id, const pj_str_t *from, 
506                             const pj_str_t *to, const pj_str_t *contact,
507                             const pj_str_t *mime_type, const pj_str_t *text)
508 {
509     PJ_UNUSED_ARG(call_id);
510     PJ_UNUSED_ARG(from);
511     PJ_UNUSED_ARG(to);
512     PJ_UNUSED_ARG(contact);
513     PJ_UNUSED_ARG(mime_type);
514     PJ_UNUSED_ARG(text);
515 }
516
517 /////////////////////////////////////////////////////////////////////////////
518 // CPocketPJDlg message handlers
519
520 BOOL CPocketPJDlg::OnInitDialog()
521 {
522         CDialog::OnInitDialog();
523
524         // Set the icon for this dialog.  The framework does this automatically
525         //  when the application's main window is not a dialog
526         SetIcon(m_hIcon, TRUE);                 // Set big icon
527         SetIcon(m_hIcon, FALSE);                // Set small icon
528         
529         CenterWindow(GetDesktopWindow());       // center to the hpc screen
530  
531         // TODO: Add extra initialization here
532         
533         m_Cfg.LoadRegistry();
534         //ShowWindow(SW_SHOW);
535         m_AccId.SetWindowText(m_Cfg.m_User);
536
537         CImageList *il = new CImageList;
538         VERIFY(il->Create(16, 16, ILC_COLOR|ILC_MASK, 2, 4));
539
540         CBitmap *bmp = new CBitmap;
541         bmp->LoadBitmap(MAKEINTRESOURCE(IDB_BLANK));
542         il->Add(bmp, RGB(255,255,255));
543         bmp = new CBitmap;
544         bmp->LoadBitmap(MAKEINTRESOURCE(IDB_ONLINE));
545         il->Add(bmp, RGB(255,255,255));
546         
547         m_BuddyList.SetImageList(il, LVSIL_SMALL);
548
549         if (m_Cfg.m_Domain.GetLength()==0 || Restart() == FALSE) {
550             for (;;) {
551                 CSettingsDlg dlg(m_Cfg);
552                 if (dlg.DoModal() != IDOK) {
553                     EndDialog(IDOK);
554                     return TRUE;
555                 }
556
557                 m_Cfg.SaveRegistry();
558
559                 if (Restart())
560                     break;
561             }
562         }
563
564         RedrawBuddyList();
565         return TRUE;  // return TRUE  unless you set the focus to a control
566 }
567
568
569
570 void CPocketPJDlg::OnBtnAcc() 
571 {
572     CMenu menu;
573     VERIFY(menu.LoadMenu(IDR_ACC_MENU));
574     CMenu* pPopup = menu.GetSubMenu(0);
575     ASSERT(pPopup != NULL);
576
577     RECT r;
578     m_BtnAcc.GetWindowRect(&r);
579     pPopup->TrackPopupMenu(TPM_LEFTALIGN, r.left+4, r.top+4, this);
580 }
581
582 void CPocketPJDlg::OnBtnAction() 
583 {
584     CMenu menu;
585     VERIFY(menu.LoadMenu(IDR_URI_MENU));
586     CMenu* pPopup = menu.GetSubMenu(0);
587     ASSERT(pPopup != NULL);
588
589     RECT r;
590     this->m_BtnUrlAction.GetWindowRect(&r);
591     pPopup->TrackPopupMenu(TPM_LEFTALIGN, r.left+4, r.top+4, this);
592 }
593
594 void CPocketPJDlg::OnSettings() 
595 {
596     for (;;) {
597         CSettingsDlg dlg(m_Cfg);
598         if (dlg.DoModal() != IDOK) {
599             return;
600         }
601
602         m_Cfg.SaveRegistry();
603
604         if (Restart())
605             break;
606     }
607 }
608
609 void CPocketPJDlg::OnOK()
610 {
611     if (AfxMessageBox(_T("Quit PocketPJ?"), MB_YESNO)==IDYES) {
612         KillTimer(TIMER_ID);
613         PopUp_Show(POPUP_REGISTRATION, "", "Shutting down..", "", "", "", 0);
614         pjsua_destroy();
615         CDialog::OnOK();
616         PopUp_Hide(POPUP_REGISTRATION);
617         m_Cfg.SaveRegistry();
618         return;
619     }
620 }
621
622 void CPocketPJDlg::OnTimer(UINT nIDEvent) 
623 {
624     pjsua_handle_events(10);
625     CDialog::OnTimer(nIDEvent);
626 }
627
628 int  CPocketPJDlg::FindBuddyInPjsua(const CString &Uri)
629 {
630     char uri[80];
631     pjsua_buddy_id  id[128];
632     unsigned i, count = PJ_ARRAY_SIZE(id);
633
634     if (pjsua_enum_buddies(id, &count) != PJ_SUCCESS)
635         return PJSUA_INVALID_ID;
636     if (count==0)
637         return PJSUA_INVALID_ID;
638
639     pj_unicode_to_ansi((LPCTSTR)Uri, Uri.GetLength(), uri, sizeof(uri));
640
641     for (i=0; i<count; ++i) {
642         pjsua_buddy_info bi;
643         pjsua_buddy_get_info(id[i], &bi);
644         if (pj_strcmp2(&bi.uri, uri)==0)
645             return i;
646     }
647
648     return PJSUA_INVALID_ID;
649 }
650
651 int  CPocketPJDlg::FindBuddyInCfg(const CString &uri)
652 {
653     int i;
654     for (i=0; i<m_Cfg.m_BuddyList.GetSize(); ++i) {
655         if (m_Cfg.m_BuddyList.GetAt(0) == uri) {
656             return i;
657         }
658     }
659     return -1;
660 }
661
662 void CPocketPJDlg::RedrawBuddyList()
663 {
664     int i;
665
666     m_BuddyList.DeleteAllItems();
667
668     for (i=0; i<m_Cfg.m_BuddyList.GetSize(); ++i) {
669         int isOnline;
670         int id;
671
672         id = FindBuddyInPjsua(m_Cfg.m_BuddyList.GetAt(i));
673         if (id != PJSUA_INVALID_ID) {
674             pjsua_buddy_info bi;
675             pjsua_buddy_get_info(id, &bi);
676             isOnline = (bi.status == PJSUA_BUDDY_STATUS_ONLINE);
677         } else {
678             isOnline = 0;
679         }
680
681         LVITEM lvi;
682         memset(&lvi, 0, sizeof(lvi));
683         lvi.mask = LVIF_TEXT  | LVIF_IMAGE;
684         lvi.iItem = i;
685         lvi.iImage = isOnline;
686         lvi.pszText = (LPTSTR)(LPCTSTR)m_Cfg.m_BuddyList.GetAt(i);
687
688         m_BuddyList.InsertItem(&lvi);
689     }
690 }
691
692 void CPocketPJDlg::OnUriCall() 
693 {
694     char tmp[120];
695     CString uri;
696     pj_status_t status;
697
698     m_Url.GetWindowText(uri);
699     pj_unicode_to_ansi((LPCTSTR)uri, uri.GetLength(), tmp, sizeof(tmp));
700     if ((status=pjsua_verify_sip_url(tmp)) != PJ_SUCCESS) {
701         Error("The URL is not valid SIP URL", status);
702         return;
703     }
704
705     pj_str_t dest_uri = pj_str(tmp);
706     pjsua_call_id call_id;
707
708     status = pjsua_call_make_call(m_PjsuaAccId, &dest_uri, 0, NULL, NULL, &call_id);
709
710     if (status != PJ_SUCCESS)
711         Error("Unable to make call", status);
712 }
713
714 void CPocketPJDlg::OnUriAddBuddy() 
715 {
716     int i;
717     char tmp[120];
718     CString uri;
719     pj_status_t status;
720
721     m_Url.GetWindowText(uri);
722     pj_unicode_to_ansi((LPCTSTR)uri, uri.GetLength(), tmp, sizeof(tmp));
723     if ((status=pjsua_verify_sip_url(tmp)) != PJ_SUCCESS) {
724         Error("The URL is not valid SIP URL", status);
725         return;
726     }
727
728     for (i=0; i<m_Cfg.m_BuddyList.GetSize(); ++i) {
729         if (m_Cfg.m_BuddyList.GetAt(0) == uri) {
730             AfxMessageBox(_T("The URI is already in the buddy list"));
731             return;
732         }
733     }
734
735     m_Cfg.m_BuddyList.Add(uri);
736     RedrawBuddyList();
737 }
738
739 void CPocketPJDlg::OnUriDelBuddy() 
740 {
741     CString uri;
742
743     m_Url.GetWindowText(uri);
744     int i = FindBuddyInCfg(uri);
745     if (i<0) {
746         /* Buddy not found */
747         return;
748     }
749
750     m_Cfg.m_BuddyList.RemoveAt(i);
751     RedrawBuddyList();
752     AfxMessageBox(_T("Buddy " + uri + " deleted"));
753 }
754
755 void CPocketPJDlg::OnAccOnline() 
756 {
757     pjsua_acc_set_online_status(m_PjsuaAccId, PJ_TRUE);
758     m_BtnAcc.SetBitmap(::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_ONLINE)) );
759 }
760
761 void CPocketPJDlg::OnAccInvisible() 
762 {
763     pjsua_acc_set_online_status(m_PjsuaAccId, PJ_FALSE);
764     m_BtnAcc.SetBitmap(::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_INVISIBLE)) );
765 }
766
767 void CPocketPJDlg::OnPopUpButton(int btnNo)
768 {
769     if (btnNo == 1) {
770         pjsua_call_answer(0, 200, NULL, 0);
771         PopUp_Modify(POPUP_CALL, POPUP_EL_BUTTON1, "");
772     } else if (btnNo == 2) {
773         // Hangup button
774         PopUp_Modify(POPUP_CALL, POPUP_EL_TITLE2, "Hang up..");
775         PopUp_Modify(POPUP_CALL, POPUP_EL_TITLE3, "");
776         pjsua_call_hangup(0, PJSIP_SC_DECLINE, 0, 0);
777     }
778 }
779
780 void CPocketPJDlg::OnClickBuddyList(NMHDR* pNMHDR, LRESULT* pResult) 
781 {
782     POSITION pos = m_BuddyList.GetFirstSelectedItemPosition();
783
784     PJ_UNUSED_ARG(pNMHDR);
785
786     if (pos != NULL) {
787         int iItem = m_BuddyList.GetNextSelectedItem(pos);
788         CString uri = m_BuddyList.GetItemText(iItem, 0);
789         m_Url.SetWindowText(uri);
790     }
791     *pResult = 0;
792 }