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