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