]> sjero.net Git - linphone/blob - coreapi/callbacks.c
d4f5193f37f9db769a0506a3749fff964bf09888
[linphone] / coreapi / callbacks.c
1 /*
2 linphone
3 Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)
4
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.
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 */
19
20
21 #include "sal.h"
22
23 #include "linphonecore.h"
24 #include "private.h"
25 #include "mediastreamer2/mediastream.h"
26 #include "lpconfig.h"
27
28 static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
29
30 static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){
31         if (lc->ringstream!=NULL){
32                 ring_stop(lc->ringstream);
33                 lc->ringstream=NULL;
34         }
35         linphone_call_start_media_streams(call);
36 }
37
38 static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
39         MSList *elem;
40         for(elem=lc->calls;elem!=NULL;elem=elem->next){
41                 LinphoneCall *call=(LinphoneCall*)elem->data;
42                 if (linphone_address_weak_equal(call->log->from,from) &&
43                     linphone_address_weak_equal(call->log->to, to)){
44                         return TRUE;
45                 }
46         }
47         return FALSE;
48 }
49
50 static void call_received(SalOp *h){
51         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
52         char *barmesg;
53         LinphoneCall *call;
54         const char *from,*to;
55         char *tmp;
56         LinphoneAddress *from_parsed;
57         LinphoneAddress *from_addr, *to_addr;
58         const char * early_media=linphone_core_get_remote_ringback_tone (lc);
59         
60         /* first check if we can answer successfully to this invite */
61         if (lc->presence_mode==LinphoneStatusBusy ||
62             lc->presence_mode==LinphoneStatusOffline ||
63             lc->presence_mode==LinphoneStatusDoNotDisturb ||
64             lc->presence_mode==LinphoneStatusMoved){
65                 if (lc->presence_mode==LinphoneStatusBusy )
66                         sal_call_decline(h,SalReasonBusy,NULL);
67                 else if (lc->presence_mode==LinphoneStatusOffline)
68                         sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
69                 else if (lc->presence_mode==LinphoneStatusDoNotDisturb)
70                         sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
71                 else if (lc->alt_contact!=NULL && lc->presence_mode==LinphoneStatusMoved)
72                         sal_call_decline(h,SalReasonRedirect,lc->alt_contact);
73                 sal_op_release(h);
74                 return;
75         }
76         if (!linphone_core_can_we_add_call(lc)){/*busy*/
77                 sal_call_decline(h,SalReasonBusy,NULL);
78                 sal_op_release(h);
79                 return;
80         }
81         from=sal_op_get_from(h);
82         to=sal_op_get_to(h);
83         from_addr=linphone_address_new(from);
84         to_addr=linphone_address_new(to);
85
86         if (is_duplicate_call(lc,from_addr,to_addr)){
87                 ms_warning("Receiving duplicated call, refusing this one.");
88                 sal_call_decline(h,SalReasonBusy,NULL);
89                 linphone_address_destroy(from_addr);
90                 linphone_address_destroy(to_addr);
91                 return;
92         }
93         
94         call=linphone_call_new_incoming(lc,from_addr,to_addr,h);
95         sal_call_set_local_media_description(h,call->localdesc);
96         call->resultdesc=sal_call_get_final_media_description(h);
97         if (call->resultdesc)
98                 sal_media_description_ref(call->resultdesc);
99         if (call->resultdesc && sal_media_description_empty(call->resultdesc)){
100                 sal_call_decline(h,SalReasonMedia,NULL);
101                 linphone_call_unref(call);
102                 return;
103         }
104         
105         
106         /* the call is acceptable so we can now add it to our list */
107         linphone_core_add_call(lc,call);
108         
109         from_parsed=linphone_address_new(sal_op_get_from(h));
110         linphone_address_clean(from_parsed);
111         tmp=linphone_address_as_string(from_parsed);
112         linphone_address_destroy(from_parsed);
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);
118
119         /* play the ring if this is the only call*/
120         if (lc->sound_conf.ring_sndcard!=NULL && ms_list_size(lc->calls)==1){
121                 lc->current_call=call;
122                 if (lc->ringstream && lc->dmfs_playing_start_time!=0){
123                         ring_stop(lc->ringstream);
124                         lc->ringstream=NULL;
125                         lc->dmfs_playing_start_time=0;
126                 }
127                 if(lc->ringstream==NULL){
128                         MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
129                         ms_message("Starting local ring...");
130                         lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard);
131                 }
132                 else
133                 {
134                         ms_message("the local ring is already started");
135                 }
136         }else{
137                 /*TODO : play a tone within the context of the current call */
138         }
139         sal_call_notify_ringing(h,early_media!=NULL);
140 #if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
141         linphone_call_init_media_streams(call);
142         if (early_media!=NULL){
143                 linphone_call_start_early_media (call);
144         }
145 #endif
146         ms_free(barmesg);
147         ms_free(tmp);
148         
149         linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call");
150         if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
151                 linphone_core_accept_call(lc,call);
152         }
153 }
154
155 static void call_ringing(SalOp *h){
156         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
157         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h);
158         SalMediaDescription *md;
159         
160         if (call==NULL) return;
161         
162         if (lc->vtable.display_status)
163                 lc->vtable.display_status(lc,_("Remote ringing."));
164         
165         md=sal_call_get_final_media_description(h);
166         if (md==NULL){
167                 if (lc->ringstream && lc->dmfs_playing_start_time!=0){
168                         ring_stop(lc->ringstream);
169                         lc->ringstream=NULL;
170                         lc->dmfs_playing_start_time=0;
171                 }
172                 if (lc->ringstream!=NULL) return;       /*already ringing !*/
173                 if (lc->sound_conf.play_sndcard!=NULL){
174                         MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
175                         ms_message("Remote ringing...");
176                         lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
177                         linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing");             
178                 }
179         }else{
180                 /*accept early media */
181                 if (call->audiostream && call->audiostream->ticker!=NULL){
182                         /*streams already started */
183                         ms_message("Early media already started.");
184                         return;
185                 }
186                 sal_media_description_ref(md);
187                 call->resultdesc=md;
188                 if (lc->vtable.show) lc->vtable.show(lc);
189                 if (lc->vtable.display_status) 
190                         lc->vtable.display_status(lc,_("Early media."));
191                 linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media");
192                 if (lc->ringstream!=NULL){
193                         ring_stop(lc->ringstream);
194                         lc->ringstream=NULL;
195                 }
196                 ms_message("Doing early media...");
197                 linphone_call_start_media_streams(call);
198                 call->media_pending=TRUE;
199         }
200 }
201
202 /*
203  * could be reach :
204  *  - when the call is accepted
205  *  - when a request is accepted (pause, resume)
206  */
207 static void call_accepted(SalOp *op){
208         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
209         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
210
211         if (call==NULL){
212                 ms_warning("No call to accept.");
213                 return ;
214         }
215         if ((call->audiostream!=NULL) && (call->audiostream->ticker!=NULL)){
216                 /*case where we accepted early media or already in call*/
217                 linphone_call_stop_media_streams(call);
218         }
219         if (call->audiostream==NULL){
220                 linphone_call_init_media_streams(call);
221         }
222         if (call->resultdesc)
223                 sal_media_description_unref(call->resultdesc);
224         call->resultdesc=sal_call_get_final_media_description(op);
225         if (call->resultdesc){
226                 sal_media_description_ref(call->resultdesc);
227                 call->media_pending=FALSE;
228         }
229         if (call->state==LinphoneCallOutgoingProgress ||
230             call->state==LinphoneCallOutgoingRinging ||
231             call->state==LinphoneCallOutgoingEarlyMedia){
232                 linphone_call_set_state(call,LinphoneCallConnected,"Connected");
233         }
234         if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
235                 if (sal_media_description_has_dir(call->resultdesc,SalStreamSendOnly) ||
236                     sal_media_description_has_dir(call->resultdesc,SalStreamInactive)){
237                         if (lc->vtable.display_status){
238                                 char *tmp=linphone_call_get_remote_address_as_string (call);
239                                 char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp);
240                                 lc->vtable.display_status(lc,msg);
241                                 ms_free(tmp);
242                                 ms_free(msg);
243                         }
244                         linphone_call_set_state(call,LinphoneCallPaused,"Call paused");
245                 }else if (sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)){
246                         /*we are put on hold when the call is initially accepted */
247                         if (lc->vtable.display_status){
248                                 char *tmp=linphone_call_get_remote_address_as_string (call);
249                                 char *msg=ms_strdup_printf(_("Call answered by %s - on hold."),tmp);
250                                 lc->vtable.display_status(lc,msg);
251                                 ms_free(tmp);
252                                 ms_free(msg);
253                         }
254                         linphone_call_set_state(call,LinphoneCallPaused,"Call paused");
255                 }else{
256                         if (lc->vtable.display_status){
257                                 lc->vtable.display_status(lc,_("Call answered - connected."));
258                         }
259                         linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
260                 }
261                 linphone_connect_incoming (lc,call);
262         }else{
263                 /*send a bye*/
264                 ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
265                 linphone_core_abort_call(lc,call,"No codec intersection");
266         }
267 }
268
269 static void call_ack(SalOp *op){
270         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
271         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
272         if (call==NULL){
273                 ms_warning("No call to be ACK'd");
274                 return ;
275         }
276         if (call->media_pending){
277                 if (call->audiostream->ticker!=NULL){
278                         /*case where we accepted early media */
279                         linphone_call_stop_media_streams(call);
280                         linphone_call_init_media_streams(call);
281                 }
282                 if (call->resultdesc)
283                         sal_media_description_unref(call->resultdesc);
284                 call->resultdesc=sal_call_get_final_media_description(op);
285                 if (call->resultdesc)
286                         sal_media_description_ref(call->resultdesc);
287                 if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
288                         linphone_connect_incoming(lc,call);
289                         linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
290                 }else{
291                         /*send a bye*/
292                         ms_error("Incompatible SDP response received in ACK, need to abort the call");
293                         linphone_core_abort_call(lc,call,"No codec intersection");
294                         return;
295                 }
296                 call->media_pending=FALSE;
297         }
298 }
299
300 /* this callback is called when an incoming re-INVITE modifies the session*/
301 static void call_updating(SalOp *op){
302         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
303         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
304         LinphoneCallState prevstate=LinphoneCallIdle;
305         
306         if (call->resultdesc)
307                 sal_media_description_unref(call->resultdesc);
308         call->resultdesc=sal_call_get_final_media_description(op);
309         if (call->resultdesc)
310                 sal_media_description_ref(call->resultdesc);
311
312         if (call->resultdesc && !sal_media_description_empty(call->resultdesc))
313         {
314                 if ((call->state==LinphoneCallPausedByRemote || call->state==LinphoneCallPaused) &&
315                     sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv) && strcmp(call->resultdesc->addr,"0.0.0.0")!=0){
316                         /*make sure we can be resumed */
317                         if (lc->current_call!=NULL && lc->current_call!=call){
318                                 ms_warning("Attempt to be resumed but already in call with somebody else!");
319                                 /*we are actively running another call, reject with a busy*/
320                                 sal_call_decline (op,SalReasonBusy,NULL);
321                                 return;
322                         }
323                         if(lc->vtable.display_status)
324                                 lc->vtable.display_status(lc,_("We have been resumed..."));
325                         linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
326                 }
327                 else if(call->state==LinphoneCallStreamsRunning &&
328                         ( sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly) 
329                          || sal_media_description_has_dir(call->resultdesc,SalStreamInactive)
330                          || strcmp(call->resultdesc->addr,"0.0.0.0")==0)){
331                         if(lc->vtable.display_status)
332                                 lc->vtable.display_status(lc,_("We are being paused..."));
333                         linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
334                         if (lc->current_call!=call){
335                                 ms_error("Inconsitency detected: current call is %p but call %p is being paused !",lc->current_call,call);
336                         }
337                 }else{
338                         prevstate=call->state;
339                         linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
340                 }
341                 /*accept the modification (sends a 200Ok)*/
342                 sal_call_accept(op);
343                 linphone_call_stop_media_streams (call);
344                 linphone_call_init_media_streams (call);
345                 linphone_call_start_media_streams (call);
346                 if (prevstate!=LinphoneCallIdle){
347                         linphone_call_set_state (call,prevstate,"Connected (streams running)");
348                 }
349         }
350 }
351
352 static void call_terminated(SalOp *op, const char *from){
353         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
354         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
355
356         if (call==NULL) return;
357         
358         if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){
359                 ms_warning("call_terminated: ignoring.");
360                 return;
361         }
362         ms_message("Current call terminated...");
363         //we stop the call only if we have this current call or if we are in call
364         if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls)  == 1) || linphone_core_in_call(lc) )) {
365                 ring_stop(lc->ringstream);
366                 lc->ringstream=NULL;
367         }
368         linphone_call_stop_media_streams(call);
369         if (lc->vtable.show!=NULL)
370                 lc->vtable.show(lc);
371         if (lc->vtable.display_status!=NULL)
372                 lc->vtable.display_status(lc,_("Call terminated."));
373
374         linphone_call_set_state(call, LinphoneCallEnd,"Call ended");
375 }
376
377 static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){
378         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
379         char *msg486=_("User is busy.");
380         char *msg480=_("User is temporarily unavailable.");
381         /*char *retrymsg=_("%s. Retry after %i minute(s).");*/
382         char *msg600=_("User does not want to be disturbed.");
383         char *msg603=_("Call declined.");
384         const char *msg=details;
385         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
386
387         if (call==NULL){
388                 ms_warning("Call faillure reported on already cleaned call ?");
389                 return ;
390         }
391         
392         if (lc->vtable.show) lc->vtable.show(lc);
393
394         if (error==SalErrorNoResponse){
395                 msg=_("No response.");
396                 if (lc->vtable.display_status)
397                         lc->vtable.display_status(lc,msg);
398         }else if (error==SalErrorProtocol){
399                 msg=details ? details : _("Protocol error.");
400                 if (lc->vtable.display_status)
401                         lc->vtable.display_status(lc, msg);
402         }else if (error==SalErrorFailure){
403                 switch(sr){
404                         case SalReasonDeclined:
405                                 msg=msg603;
406                                 if (lc->vtable.display_status)
407                                         lc->vtable.display_status(lc,msg603);
408                         break;
409                         case SalReasonBusy:
410                                 msg=msg486;
411                                 if (lc->vtable.display_status)
412                                         lc->vtable.display_status(lc,msg486);
413                         break;
414                         case SalReasonRedirect:
415                                 msg=_("Redirected");
416                                 if (lc->vtable.display_status)
417                                         lc->vtable.display_status(lc,msg);
418                         break;
419                         case SalReasonTemporarilyUnavailable:
420                                 msg=msg480;
421                                 if (lc->vtable.display_status)
422                                         lc->vtable.display_status(lc,msg480);
423                         break;
424                         case SalReasonNotFound:
425                                 msg=_("Not found");
426                                 if (lc->vtable.display_status)
427                                         lc->vtable.display_status(lc,msg);
428                         break;
429                         case SalReasonDoNotDisturb:
430                                 msg=msg600;
431                                 if (lc->vtable.display_status)
432                                         lc->vtable.display_status(lc,msg600);
433                         break;
434                         case SalReasonMedia:
435                                 msg=_("No common codecs");
436                                 if (lc->vtable.display_status)
437                                         lc->vtable.display_status(lc,msg);
438                         break;
439                         default:
440                                 if (lc->vtable.display_status)
441                                         lc->vtable.display_status(lc,_("Call failed."));
442                 }
443         }
444
445         if (lc->ringstream!=NULL) {
446                 ring_stop(lc->ringstream);
447                 lc->ringstream=NULL;
448         }
449         linphone_call_stop_media_streams (call);
450         if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg);
451         else{
452                 call->reason=LinphoneReasonDeclined;
453                 linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
454         }
455 }
456
457 static void auth_requested(SalOp *h, const char *realm, const char *username){
458         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
459         LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
460         ms_message("auth_requested() for realm=%s, username=%s",realm,username);
461         if (ai && (ai->works || ai->usecount<3)){
462                 SalAuthInfo sai;
463                 sai.username=ai->username;
464                 sai.userid=ai->userid;
465                 sai.realm=ai->realm;
466                 sai.password=ai->passwd;
467                 ms_message("auth_requested(): authenticating realm=%s, username=%s",realm,username);
468                 sal_op_authenticate(h,&sai);
469                 ai->usecount++;
470         }else{
471                 if (ai && ai->works==FALSE) {
472                         register_failure(h, SalErrorFailure, SalReasonForbidden, _("Authentication failure"));
473                 } 
474                 if (lc->vtable.auth_info_requested)
475                         lc->vtable.auth_info_requested(lc,realm,username);
476         }
477 }
478
479 static void auth_success(SalOp *h, const char *realm, const char *username){
480         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
481         LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
482         if (ai){
483                 ms_message("%s/%s authentication works.",realm,username);
484                 ai->works=TRUE;
485         }
486 }
487
488 static void register_success(SalOp *op, bool_t registered){
489         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
490         LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
491         char *msg;
492         
493         cfg->registered=registered;
494         linphone_proxy_config_set_error(cfg,LinphoneReasonNone);
495         linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
496                                         registered ? "Registration sucessful" : "Unregistration done");
497         if (lc->vtable.display_status){
498                 if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op));
499                 else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op));
500                 lc->vtable.display_status(lc,msg);
501                 ms_free(msg);
502         }
503         
504 }
505
506 static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){
507         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
508         LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
509
510         if (cfg==NULL){
511                 ms_warning("Registration failed for unknown proxy config.");
512                 return ;
513         }
514         if (details==NULL)
515                 details=_("no response timeout");
516         
517         if (lc->vtable.display_status) {
518                 char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),details  );
519                 lc->vtable.display_status(lc,msg);
520                 ms_free(msg);
521         }
522         if (error== SalErrorFailure && reason == SalReasonForbidden) {
523                 linphone_proxy_config_set_error(cfg, LinphoneReasonBadCredentials);
524         } else if (error == SalErrorNoResponse) {
525                 linphone_proxy_config_set_error(cfg, LinphoneReasonNoResponse);
526         }
527         linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
528 }
529
530 static void vfu_request(SalOp *op){
531 #ifdef VIDEO_ENABLED
532         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op);
533         if (call==NULL){
534                 ms_warning("VFU request but no call !");
535                 return ;
536         }
537         if (call->videostream)
538                 video_stream_send_vfu(call->videostream);
539 #endif
540 }
541
542 static void dtmf_received(SalOp *op, char dtmf){
543         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
544         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
545         if (lc->vtable.dtmf_received != NULL)
546                 lc->vtable.dtmf_received(lc, call, dtmf);
547 }
548
549 static void refer_received(Sal *sal, SalOp *op, const char *referto){
550         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
551         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
552         if (call){
553                 if (call->refer_to!=NULL){
554                         ms_free(call->refer_to);
555                 }
556                 call->refer_to=ms_strdup(referto);
557                 call->refer_pending=TRUE;
558                 linphone_call_set_state(call,LinphoneCallRefered,"Refered");
559                 if (lc->vtable.display_status){
560                         char *msg=ms_strdup_printf(_("We are transferred to %s"),referto);
561                         lc->vtable.display_status(lc,msg);
562                         ms_free(msg);
563                 }
564                 if (call->state!=LinphoneCallPaused){
565                         ms_message("Automatically pausing current call to accept transfer.");
566                         linphone_core_pause_call(lc,call);
567                 }
568                 linphone_core_start_refered_call(lc,call);
569                 sal_call_accept_refer(op);
570         }else if (lc->vtable.refer_received){
571                 lc->vtable.refer_received(lc,referto);
572                 sal_call_accept_refer(op);
573         }
574 }
575
576 static void text_received(Sal *sal, const char *from, const char *msg){
577         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
578         linphone_core_text_received(lc,from,msg);
579 }
580
581 static void notify(SalOp *op, const char *from, const char *msg){
582         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
583         LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op);
584         ms_message("get a %s notify from %s",msg,from);
585         if(lc->vtable.notify_recv)
586                 lc->vtable.notify_recv(lc,call,from,msg);
587 }
588
589 static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){
590         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
591         linphone_notify_recv(lc,op,ss,status);
592 }
593
594 static void subscribe_received(SalOp *op, const char *from){
595         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
596         linphone_subscription_new(lc,op,from);
597 }
598
599 static void subscribe_closed(SalOp *op, const char *from){
600         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
601         linphone_subscription_closed(lc,op);
602 }
603
604 static void internal_message(Sal *sal, const char *msg){
605         LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
606         if (lc->vtable.show)
607                 lc->vtable.show(lc);
608 }
609
610 static void ping_reply(SalOp *op){
611         LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
612         ms_message("ping reply !");
613         if (call){
614                 if (call->state==LinphoneCallOutgoingInit){
615                         linphone_core_start_invite(call->core,call,NULL);
616                 }
617         }
618         else
619         {
620                 ms_warning("ping reply without call attached...");
621         }
622 }
623
624 SalCallbacks linphone_sal_callbacks={
625         call_received,
626         call_ringing,
627         call_accepted,
628         call_ack,
629         call_updating,
630         call_terminated,
631         call_failure,
632         auth_requested,
633         auth_success,
634         register_success,
635         register_failure,
636         vfu_request,
637         dtmf_received,
638         refer_received,
639         text_received,
640         notify,
641         notify_presence,
642         subscribe_received,
643         subscribe_closed,
644         internal_message,
645         ping_reply
646 };
647
648