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