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