3 Copyright (C) 2010 Simon MORLAT (simon.morlat@free.fr)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
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.
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.
23 #include "linphonecore.h"
25 #include "mediastreamer2/mediastream.h"
28 static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){
31 if (lc->vtable.display_status)
32 lc->vtable.display_status(lc,_("Connected."));
33 if (lc->vtable.connected_recv)
34 lc->vtable.connected_recv(lc,call);
35 call->state=LinphoneCallAVRunning;
36 if (lc->ringstream!=NULL){
37 ring_stop(lc->ringstream);
40 if(!linphone_core_in_call(lc))
42 linphone_core_set_as_current_call(lc,call);
44 if(call == linphone_core_get_current_call(lc))
45 linphone_core_start_media_streams(lc,call);
48 static void call_received(SalOp *h){
49 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
54 LinphoneAddress *from_parsed;
56 /* first check if we can answer successfully to this invite */
57 if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){
58 ms_message("Not present !! presence mode : %d\n",lc->presence_mode);
59 if (lc->presence_mode==LINPHONE_STATUS_BUSY)
60 sal_call_decline(h,SalReasonBusy,NULL);
61 else if (lc->presence_mode==LINPHONE_STATUS_AWAY
62 ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK
63 ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE
64 ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH
65 ||lc->presence_mode==LINPHONE_STATUS_OFFLINE)
66 sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
67 else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB)
68 sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
69 else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED)
70 sal_call_decline(h,SalReasonRedirect,lc->alt_contact);
72 sal_call_decline(h,SalReasonBusy,NULL);
76 if (!linphone_core_can_we_add_call(lc)){/*busy*/
77 sal_call_decline(h,SalReasonBusy,NULL);
81 from=sal_op_get_from(h);
84 call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),h);
85 if(linphone_core_add_call(lc,call)!= 0)
87 ms_warning("we cannot have more calls\n");
88 sal_call_decline(h,SalReasonMedia,NULL);
89 linphone_call_unref(call);
92 if(linphone_core_get_current_call(lc)!=NULL) //we are already in call just inform that an incoming call is going on
95 snprintf(temp,sizeof(temp),"A new incoming call from %s during call",from);
96 lc->vtable.display_status(lc,temp);
98 sal_call_set_local_media_description(h,call->localdesc);
99 call->resultdesc=sal_call_get_final_media_description(h);
100 if (call->resultdesc)
101 sal_media_description_ref(call->resultdesc);
102 if (call->resultdesc && sal_media_description_empty(call->resultdesc)){
103 sal_call_decline(h,SalReasonMedia,NULL);
104 linphone_call_unref(call);
108 from_parsed=linphone_address_new(sal_op_get_from(h));
109 linphone_address_clean(from_parsed);
110 tmp=linphone_address_as_string(from_parsed);
111 linphone_address_destroy(from_parsed);
112 gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp);
113 barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"),
114 (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_("."));
115 if (lc->vtable.show) lc->vtable.show(lc);
116 if (lc->vtable.display_status)
117 lc->vtable.display_status(lc,barmesg);
120 if (lc->sound_conf.ring_sndcard!=NULL && !linphone_core_in_call(lc)){
121 if(lc->ringstream==NULL){
122 MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
123 ms_message("Starting local ring...");
124 lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard);
128 ms_message("the local ring is already started");
131 call->state=LinphoneCallRinging;
132 sal_call_notify_ringing(h);
133 linphone_core_init_media_streams(lc,call);
134 if (lc->vtable.inv_recv) lc->vtable.inv_recv(lc,call);
139 static void call_ringing(SalOp *h){
140 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
141 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h);
142 SalMediaDescription *md;
143 if (call==NULL) return;
144 if (lc->vtable.display_status)
145 lc->vtable.display_status(lc,_("Remote ringing."));
146 if (lc->vtable.ringing_recv)
147 lc->vtable.ringing_recv(lc,call);
148 md=sal_call_get_final_media_description(h);
150 if (lc->ringstream!=NULL) return; /*already ringing !*/
151 if (lc->sound_conf.play_sndcard!=NULL){
152 MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
153 ms_message("Remote ringing...");
154 lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
157 /*accept early media */
158 if (lc->audiostream && lc->audiostream->ticker!=NULL){
159 /*streams already started */
160 ms_message("Early media already started.");
163 sal_media_description_ref(md);
165 if (lc->vtable.show) lc->vtable.show(lc);
166 if (lc->vtable.display_status)
167 lc->vtable.display_status(lc,_("Early media."));
168 gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL);
169 if (lc->ringstream!=NULL){
170 ring_stop(lc->ringstream);
173 ms_message("Doing early media...");
174 if(call == linphone_core_get_current_call(lc))
175 linphone_core_start_media_streams(lc,call);
176 call->media_pending=TRUE;
178 call->state=LinphoneCallRinging;
183 * - when the call is accepted
184 * - when a request is accepted (pause, resume)
186 static void call_accepted(SalOp *op){
187 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
188 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
190 ms_warning("No call to accept.");
193 if (call->state==LinphoneCallAVRunning){
194 ms_message("GET ACK of resume\n");
195 if(lc->vtable.ack_resumed_recv)
196 lc->vtable.ack_resumed_recv(lc,call);
197 return ; //already accepted
199 if ((lc->audiostream!=NULL) && (lc->audiostream->ticker!=NULL)){
200 /*case where we accepted early media */
201 if(call == linphone_core_get_current_call(lc))
203 linphone_core_stop_media_streams(lc,call);
204 linphone_core_init_media_streams(lc,call);
207 if (call->resultdesc)
208 sal_media_description_unref(call->resultdesc);
209 call->resultdesc=sal_call_get_final_media_description(op);
210 if (call->resultdesc){
211 sal_media_description_ref(call->resultdesc);
212 call->media_pending=FALSE;
214 if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
215 //if we initiate a pause
216 if(call->state == LinphoneCallPaused)
218 ms_message("GET ACK of pause\n");
219 if(lc->vtable.ack_paused_recv)
220 lc->vtable.ack_paused_recv(lc,call);
221 }//if there is an accepted incoming call
224 gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL);
225 linphone_connect_incoming(lc,call);
229 ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
230 linphone_core_terminate_call(lc,NULL);
234 static void call_ack(SalOp *op){
235 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
236 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
238 ms_warning("No call to be ACK'd");
241 if (call->media_pending){
242 if (lc->audiostream->ticker!=NULL){
243 /*case where we accepted early media */
244 if(call == linphone_core_get_current_call(lc))
246 linphone_core_stop_media_streams(lc,call);
247 linphone_core_init_media_streams(lc,call);
250 if (call->resultdesc)
251 sal_media_description_unref(call->resultdesc);
252 call->resultdesc=sal_call_get_final_media_description(op);
253 if (call->resultdesc)
254 sal_media_description_ref(call->resultdesc);
255 if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
256 gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL);
257 linphone_connect_incoming(lc,call);
260 ms_error("Incompatible SDP response received in ACK, need to abort the call");
261 linphone_core_terminate_call(lc,NULL);
263 call->media_pending=FALSE;
267 static void call_updated(SalOp *op){
268 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
269 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
270 if (call->resultdesc)
271 sal_media_description_unref(call->resultdesc);
272 call->resultdesc=sal_call_get_final_media_description(op);
273 if (call->resultdesc)
274 sal_media_description_ref(call->resultdesc);
276 if (call->resultdesc && !sal_media_description_empty(call->resultdesc))
278 if( (call->state == LinphoneCallPaused) && strcmp(call->resultdesc->addr,"0.0.0.0"))
280 if(lc->vtable.display_status)
281 lc->vtable.display_status(lc,"we have been resumed...");
282 call->state = LinphoneCallAVRunning;
283 lc->vtable.resumed_recv(lc,call);
284 //we have to keep sending when holded
285 //linphone_core_start_media_streams(lc,call);
287 else if( (call->state != LinphoneCallPaused) && !strcmp(call->resultdesc->addr,"0.0.0.0"))
289 if(lc->vtable.display_status)
290 lc->vtable.display_status(lc,"we have been paused...");
291 call->state = LinphoneCallPaused;
292 lc->vtable.paused_recv(lc,call);
293 //we have to keep sending when holded
295 if(call == linphone_core_get_current_call(lc))
297 linphone_core_stop_media_streams(lc,call);
298 linphone_core_init_media_streams(lc,call);
304 if(call == linphone_core_get_current_call(lc))
306 linphone_core_stop_media_streams(lc,call);
307 linphone_core_init_media_streams(lc,call);
309 linphone_connect_incoming(lc,call);
314 static void call_terminated(SalOp *op, const char *from){
315 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
316 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
317 if (linphone_call_get_state(call)==LinphoneCallTerminated){
318 ms_warning("call_terminated: ignoring.");
321 ms_message("Current call terminated...");
322 //we stop the call only if we have this current call or if we are in call
323 if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) {
324 ring_stop(lc->ringstream);
327 if(call == linphone_core_get_current_call(lc))
328 linphone_core_stop_media_streams(lc,call);
329 if (lc->vtable.show!=NULL)
331 if (lc->vtable.display_status!=NULL)
332 lc->vtable.display_status(lc,_("Call terminated."));
333 linphone_call_set_terminated(call);
334 gstate_new_state(lc, GSTATE_CALL_END, NULL);
335 if (lc->vtable.bye_recv!=NULL){
336 LinphoneAddress *addr=linphone_address_new(from);
338 linphone_address_clean(addr);
339 tmp=linphone_address_as_string(addr);
340 lc->vtable.bye_recv(lc,call);
342 linphone_address_destroy(addr);
344 linphone_call_unref(call);
347 static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){
348 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
349 char *msg486=_("User is busy.");
350 char *msg480=_("User is temporarily unavailable.");
351 /*char *retrymsg=_("%s. Retry after %i minute(s).");*/
352 char *msg600=_("User does not want to be disturbed.");
353 char *msg603=_("Call declined.");
355 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
357 if (lc->vtable.show) lc->vtable.show(lc);
359 if (error==SalErrorNoResponse){
360 if (lc->vtable.display_status)
361 lc->vtable.display_status(lc,_("No response."));
362 }else if (error==SalErrorProtocol){
363 if (lc->vtable.display_status)
364 lc->vtable.display_status(lc, details ? details : _("Protocol error."));
365 }else if (error==SalErrorFailure){
367 case SalReasonDeclined:
369 if (lc->vtable.display_status)
370 lc->vtable.display_status(lc,msg603);
374 if (lc->vtable.display_status)
375 lc->vtable.display_status(lc,msg486);
377 case SalReasonRedirect:
379 if (lc->vtable.display_status)
380 lc->vtable.display_status(lc,msg);
382 case SalReasonTemporarilyUnavailable:
384 if (lc->vtable.display_status)
385 lc->vtable.display_status(lc,msg480);
387 case SalReasonNotFound:
389 if (lc->vtable.display_status)
390 lc->vtable.display_status(lc,msg);
392 case SalReasonDoNotDisturb:
394 if (lc->vtable.display_status)
395 lc->vtable.display_status(lc,msg600);
398 msg=_("No common codecs");
399 if (lc->vtable.display_status)
400 lc->vtable.display_status(lc,msg);
403 if (lc->vtable.display_status)
404 lc->vtable.display_status(lc,_("Call failed."));
407 if (lc->vtable.failure_recv)
408 lc->vtable.failure_recv(lc,call,code);
409 if (lc->ringstream!=NULL) {
410 ring_stop(lc->ringstream);
413 if(call == linphone_core_get_current_call(lc))
414 linphone_core_stop_media_streams(lc,call);
416 linphone_call_set_terminated(call);
417 linphone_call_unref(call);//TODO not an unref here ???//AUREL
418 if (sr!=SalReasonDeclined) gstate_new_state(lc, GSTATE_CALL_ERROR, msg);
419 else gstate_new_state(lc, GSTATE_CALL_END, NULL);
423 static void auth_requested(SalOp *h, const char *realm, const char *username){
424 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
425 LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
426 ms_message("auth_requested() for realm=%s, username=%s",realm,username);
427 if (ai && (ai->works || ai->usecount<3)){
429 sai.username=ai->username;
430 sai.userid=ai->userid;
432 sai.password=ai->passwd;
433 ms_message("auth_requested(): authenticating realm=%s, username=%s",realm,username);
434 sal_op_authenticate(h,&sai);
437 if (lc->vtable.auth_info_requested)
438 lc->vtable.auth_info_requested(lc,realm,username);
442 static void auth_success(SalOp *h, const char *realm, const char *username){
443 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
444 LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
446 ms_message("%s/%s authentication works.",realm,username);
451 static void register_success(SalOp *op, bool_t registered){
452 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
453 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
455 cfg->registered=registered;
456 gstate_new_state(lc, GSTATE_REG_OK, NULL);
457 if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op));
458 else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op));
459 if (lc->vtable.display_status)
460 lc->vtable.display_status(lc,msg);
464 static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){
465 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
466 char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),(details!=NULL) ? details : _("no response timeout"));
467 if (lc->vtable.display_status) lc->vtable.display_status(lc,msg);
468 gstate_new_state(lc, GSTATE_REG_FAILED, msg);
472 static void vfu_request(SalOp *op){
474 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
476 video_stream_send_vfu(lc->videostream);
480 static void dtmf_received(SalOp *op, char dtmf){
481 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
482 if (lc->vtable.dtmf_received != NULL)
483 lc->vtable.dtmf_received(lc, dtmf);
486 static void refer_received(Sal *sal, SalOp *op, const char *referto){
487 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
488 LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
489 if (lc->vtable.refer_received){
490 lc->vtable.refer_received(lc,call,referto);
491 if (op) sal_refer_accept(op);
495 static void text_received(Sal *sal, const char *from, const char *msg){
496 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
497 linphone_core_text_received(lc,from,msg);
500 static void notify(SalOp *op, const char *from, const char *msg){
501 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
503 ms_message("get a %s notify from %s",msg,from);
504 if(lc->vtable.notify_recv)
505 lc->vtable.notify_recv(lc,from,msg);
508 static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){
509 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
510 linphone_notify_recv(lc,op,ss,status);
513 static void subscribe_received(SalOp *op, const char *from){
514 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
515 linphone_subscription_new(lc,op,from);
518 static void subscribe_closed(SalOp *op, const char *from){
519 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
520 linphone_subscription_closed(lc,op);
523 static void internal_message(Sal *sal, const char *msg){
524 LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
529 static void ping_reply(SalOp *op){
530 LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
531 ms_message("ping reply !");
533 if (call->state==LinphoneCallPreEstablishing){
534 linphone_core_start_invite(call->core,call,NULL);
539 ms_warning("ping reply without call attached...");
543 SalCallbacks linphone_sal_callbacks={