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