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