6 * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "glib/gstdio.h"
29 #include <gst/gststructure.h>
30 #include <gst/gstvalue.h>
32 #include "sipe-common.h"
34 #include "mediamanager.h"
38 #include "sipe-backend.h"
39 #include "sipe-core.h"
41 #include "purple-private.h"
43 struct sipe_backend_media
{
48 struct sipe_backend_stream
{
51 gboolean candidates_prepared
;
52 gboolean local_on_hold
;
53 gboolean remote_on_hold
;
57 backend_stream_free(struct sipe_backend_stream
*stream
)
60 g_free(stream
->sessionid
);
61 g_free(stream
->participant
);
66 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
);
67 static PurpleMediaCandidateType
sipe_candidate_type_to_purple(SipeCandidateType type
);
68 static SipeCandidateType
purple_candidate_type_to_sipe(PurpleMediaCandidateType type
);
69 static PurpleMediaNetworkProtocol
sipe_network_protocol_to_purple(SipeNetworkProtocol proto
);
70 static SipeNetworkProtocol
purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
);
72 struct stream_info_context
{
73 struct sipe_media_call
*call
;
74 struct sipe_backend_media
*backend_media
;
78 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
80 SIPE_UNUSED_PARAMETER gchar
*participant
,
81 struct stream_info_context
*ctx
)
83 struct sipe_backend_stream
*stream
;
84 stream
= sipe_backend_media_get_stream_by_id(ctx
->backend_media
, sessionid
);
86 stream
->candidates_prepared
= TRUE
;
88 if (ctx
->call
->candidates_prepared_cb
) {
89 GSList
*streams
= ctx
->backend_media
->streams
;
90 for (; streams
; streams
= streams
->next
) {
91 struct sipe_backend_stream
*s
= streams
->data
;
92 if (!s
->candidates_prepared
)
95 ctx
->call
->candidates_prepared_cb(ctx
->call
, stream
);
100 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
101 PurpleMediaState state
,
104 struct sipe_media_call
*call
)
106 // TODO: this should be aware of legacy backend_media
107 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state
, sessionid
, participant
);
108 if (state
== PURPLE_MEDIA_STATE_CONNECTED
&& call
->media_connected_cb
)
109 call
->media_connected_cb(call
);
113 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
114 PurpleMediaInfoType type
,
118 struct stream_info_context
*ctx
)
120 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& ctx
->call
->call_accept_cb
121 && !sessionid
&& !participant
)
122 ctx
->call
->call_accept_cb(ctx
->call
, local
);
123 else if (type
== PURPLE_MEDIA_INFO_HOLD
|| type
== PURPLE_MEDIA_INFO_UNHOLD
) {
125 gboolean state
= (type
== PURPLE_MEDIA_INFO_HOLD
);
128 // Hold specific stream
129 struct sipe_backend_stream
*stream
;
130 stream
= sipe_backend_media_get_stream_by_id(ctx
->backend_media
,
134 stream
->local_on_hold
= state
;
136 stream
->remote_on_hold
= state
;
139 GSList
*i
= sipe_backend_media_get_streams(ctx
->backend_media
);
140 for (; i
; i
= i
->next
) {
141 struct sipe_backend_stream
*stream
= i
->data
;
144 stream
->local_on_hold
= state
;
146 stream
->remote_on_hold
= state
;
150 if (ctx
->call
->call_hold_cb
)
151 ctx
->call
->call_hold_cb(ctx
->call
, local
, state
);
152 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
153 if (!sessionid
&& !participant
) {
154 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& ctx
->call
->call_hangup_cb
)
155 ctx
->call
->call_hangup_cb(ctx
->call
, ctx
->backend_media
, local
);
156 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& ctx
->call
->call_reject_cb
)
157 ctx
->call
->call_reject_cb(ctx
->call
, local
);
158 } else if (sessionid
&& participant
) {
159 struct sipe_backend_stream
*stream
;
160 stream
= sipe_backend_media_get_stream_by_id(ctx
->backend_media
,
164 ctx
->backend_media
->streams
= g_slist_remove(ctx
->backend_media
->streams
, stream
);
165 backend_stream_free(stream
);
171 struct sipe_backend_media
*
172 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
173 struct sipe_media_call
*call
,
174 const gchar
*participant
,
177 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
178 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
179 PurpleMediaManager
*manager
= purple_media_manager_get();
180 struct stream_info_context
*ctx
= g_new0(struct stream_info_context
, 1);
182 media
->m
= purple_media_manager_create_media(manager
,
183 purple_private
->account
,
185 participant
, initiator
);
188 ctx
->backend_media
= media
;
190 /* Passing the same ctx structure to all signal handlers, only the first
191 * of them has destroy_data closure and is responsible for freeing the
193 g_signal_connect_data(G_OBJECT(media
->m
), "candidates-prepared",
194 G_CALLBACK(on_candidates_prepared_cb
), ctx
,
195 (GClosureNotify
) g_free
, 0);
196 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
197 G_CALLBACK(on_stream_info_cb
), ctx
);
198 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
199 G_CALLBACK(on_state_changed_cb
), call
);
205 sipe_backend_media_free(struct sipe_backend_media
*media
)
208 GSList
*stream
= media
->streams
;
209 g_object_unref(media
->m
);
211 for (; stream
; stream
= g_slist_delete_link(stream
, stream
))
212 backend_stream_free(stream
->data
);
218 #define FS_CODECS_CONF \
219 "# Automatically created by SIPE plugin\n" \
221 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=512 ! rtph263pay mtu=512\n";
227 filename
= g_build_filename(purple_user_dir(), "fs-codec.conf", NULL
);
229 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
230 int fd
= g_open(filename
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
231 gchar
*fs_codecs_conf
= FS_CODECS_CONF
;
232 if (!fd
|| write(fd
, fs_codecs_conf
, strlen(fs_codecs_conf
)) == -1)
233 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
240 struct sipe_backend_media_relays
*
241 sipe_backend_media_relays_convert(GSList
*media_relays
, gchar
*username
, gchar
*password
)
243 GValueArray
*relay_info
= g_value_array_new(0);
245 for (; media_relays
; media_relays
= media_relays
->next
) {
246 struct sipe_media_relay
*relay
= media_relays
->data
;
248 GstStructure
*gst_relay_info
= gst_structure_new("relay-info",
249 "ip", G_TYPE_STRING
, relay
->hostname
,
250 "port", G_TYPE_UINT
, relay
->udp_port
,
251 "username", G_TYPE_STRING
, username
,
252 "password", G_TYPE_STRING
, password
,
255 if (!gst_relay_info
) {
256 g_value_array_free(relay_info
);
260 memset(&value
, 0, sizeof(GValue
));
261 g_value_init(&value
, GST_TYPE_STRUCTURE
);
262 gst_value_set_structure(&value
, gst_relay_info
);
264 relay_info
= g_value_array_append(relay_info
, &value
);
265 gst_structure_free(gst_relay_info
);
268 return (struct sipe_backend_media_relays
*)relay_info
;
272 sipe_backend_media_relays_free(struct sipe_backend_media_relays
*media_relays
)
274 g_value_array_free((GValueArray
*)media_relays
);
277 struct sipe_backend_stream
*
278 sipe_backend_media_add_stream(struct sipe_backend_media
*media
,
280 const gchar
*participant
,
282 SipeIceVersion ice_version
,
284 struct sipe_backend_media_relays
*media_relays
)
286 struct sipe_backend_stream
*stream
= NULL
;
287 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
288 GParameter
*params
= NULL
;
289 guint params_cnt
= 0;
292 if (ice_version
!= SIPE_ICE_NO_ICE
) {
293 transmitter
= "nice";
296 params
= g_new0(GParameter
, params_cnt
);
298 params
[0].name
= "compatibility-mode";
299 g_value_init(¶ms
[0].value
, G_TYPE_UINT
);
300 g_value_set_uint(¶ms
[0].value
,
301 ice_version
== SIPE_ICE_DRAFT_6
?
302 NICE_COMPATIBILITY_OC2007
:
303 NICE_COMPATIBILITY_OC2007R2
);
306 params
[1].name
= "relay-info";
307 g_value_init(¶ms
[1].value
, G_TYPE_VALUE_ARRAY
);
308 g_value_set_boxed(¶ms
[1].value
, media_relays
);
312 // TODO: session naming here, Communicator needs audio/video
313 transmitter
= "rawudp";
314 //sessionid = "sipe-voice-rawudp";
317 ensure_codecs_conf();
319 if (purple_media_add_stream(media
->m
, id
, participant
, prpl_type
,
320 initiator
, transmitter
, params_cnt
, params
)) {
321 stream
= g_new0(struct sipe_backend_stream
, 1);
322 stream
->sessionid
= g_strdup(id
);
323 stream
->participant
= g_strdup(participant
);
324 stream
->candidates_prepared
= FALSE
;
326 media
->streams
= g_slist_append(media
->streams
, stream
);
335 sipe_backend_media_remove_stream(struct sipe_backend_media
*media
,
336 struct sipe_backend_stream
*stream
)
338 g_return_if_fail(media
&& stream
);
340 purple_media_end(media
->m
, stream
->sessionid
, NULL
);
341 media
->streams
= g_slist_remove(media
->streams
, stream
);
342 backend_stream_free(stream
);
345 GSList
*sipe_backend_media_get_streams(struct sipe_backend_media
*media
)
347 return media
->streams
;
350 struct sipe_backend_stream
*
351 sipe_backend_media_get_stream_by_id(struct sipe_backend_media
*media
,
355 for (i
= media
->streams
; i
; i
= i
->next
) {
356 struct sipe_backend_stream
*stream
= i
->data
;
357 if (sipe_strequal(stream
->sessionid
, id
))
364 sipe_backend_media_add_remote_candidates(struct sipe_backend_media
*media
,
365 struct sipe_backend_stream
*stream
,
368 purple_media_add_remote_candidates(media
->m
, stream
->sessionid
,
369 stream
->participant
, candidates
);
372 gboolean
sipe_backend_media_is_initiator(struct sipe_backend_media
*media
,
373 struct sipe_backend_stream
*stream
)
375 return purple_media_is_initiator(media
->m
,
376 stream
? stream
->sessionid
: NULL
,
377 stream
? stream
->participant
: NULL
);
380 gboolean
sipe_backend_media_accepted(struct sipe_backend_media
*media
)
382 return purple_media_accepted(media
->m
, NULL
, NULL
);
386 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media
*media
,
387 struct sipe_backend_stream
*stream
)
389 return purple_media_get_active_local_candidates(media
->m
,
391 stream
->participant
);
395 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media
*media
,
396 struct sipe_backend_stream
*stream
)
398 return purple_media_get_active_remote_candidates(media
->m
,
400 stream
->participant
);
404 sipe_backend_stream_get_id(struct sipe_backend_stream
*stream
)
406 return stream
->sessionid
;
409 void sipe_backend_stream_hold(struct sipe_backend_media
*media
,
410 struct sipe_backend_stream
*stream
,
413 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HOLD
,
414 stream
->sessionid
, stream
->participant
,
418 void sipe_backend_stream_unhold(struct sipe_backend_media
*media
,
419 struct sipe_backend_stream
*stream
,
422 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
423 stream
->sessionid
, stream
->participant
,
427 gboolean
sipe_backend_stream_is_held(struct sipe_backend_stream
*stream
)
429 g_return_val_if_fail(stream
, FALSE
);
431 return stream
->local_on_hold
|| stream
->remote_on_hold
;
434 struct sipe_backend_codec
*
435 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
437 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
438 sipe_media_to_purple(type
),
443 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
446 g_object_unref(codec
);
450 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
452 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
456 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
458 /* Not explicitly documented, but return value must be g_free()'d */
459 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
463 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
465 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
469 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
470 const gchar
*name
, const gchar
*value
)
472 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
476 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
478 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
482 sipe_backend_set_remote_codecs(struct sipe_backend_media
*media
,
483 struct sipe_backend_stream
*stream
,
486 return purple_media_set_remote_codecs(media
->m
,
493 sipe_backend_get_local_codecs(struct sipe_backend_media
*media
,
494 struct sipe_backend_stream
*stream
)
496 GList
*codecs
= purple_media_get_codecs(media
->m
,
501 * Do not announce Theora. Its optional parameters are too long,
502 * Communicator rejects such SDP message and does not support the codec
506 PurpleMediaCodec
*codec
= i
->data
;
507 gchar
*encoding_name
= purple_media_codec_get_encoding_name(codec
);
509 if (sipe_strequal(encoding_name
,"THEORA")) {
511 g_object_unref(codec
);
513 codecs
= g_list_delete_link(codecs
, i
);
518 g_free(encoding_name
);
524 struct sipe_backend_candidate
*
525 sipe_backend_candidate_new(const gchar
*foundation
,
526 SipeComponentType component
,
527 SipeCandidateType type
, SipeNetworkProtocol proto
,
528 const gchar
*ip
, guint port
,
529 const gchar
*username
,
530 const gchar
*password
)
532 PurpleMediaCandidate
*c
= purple_media_candidate_new(
533 /* Libnice and Farsight rely on non-NULL foundation to
534 * distinguish between candidates of a component. When NULL
535 * foundation is passed (ie. ICE draft 6 does not use foudation),
536 * use username instead. If no foundation is provided, Farsight
537 * may signal an active candidate different from the one actually
538 * in use. See Farsight's agent_new_selected_pair() in
539 * fs-nice-stream-transmitter.h where first candidate in the
540 * remote list is always selected when no foundation. */
541 foundation
? foundation
: username
,
543 sipe_candidate_type_to_purple(type
),
544 sipe_network_protocol_to_purple(proto
),
547 g_object_set(c
, "username", username
, "password", password
, NULL
);
548 return (struct sipe_backend_candidate
*)c
;
552 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
555 g_object_unref(candidate
);
559 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
561 /* Not explicitly documented, but return value must be g_free()'d */
562 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
566 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
568 /* Not explicitly documented, but return value must be g_free()'d */
569 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
573 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
575 /* Not explicitly documented, but return value must be g_free()'d */
576 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
580 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
582 /* Not explicitly documented, but return value must be g_free()'d */
583 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
587 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
589 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
593 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
595 /* Not explicitly documented, but return value must be g_free()'d */
596 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
600 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
602 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
606 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
608 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
612 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
614 g_object_set(candidate
, "priority", priority
, NULL
);
618 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
620 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
624 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
626 PurpleMediaCandidateType type
=
627 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
628 return purple_candidate_type_to_sipe(type
);
632 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
634 PurpleMediaNetworkProtocol proto
=
635 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
636 return purple_network_protocol_to_sipe(proto
);
640 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
644 GList
*entry
= value
;
645 GList
**candidates
= user_data
;
647 g_object_unref(entry
->data
);
648 *candidates
= g_list_delete_link(*candidates
, entry
);
652 ensure_candidate_pairs(GList
*candidates
)
654 GHashTable
*lone_cand_links
;
657 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
659 for (i
= candidates
; i
; i
= i
->next
) {
660 PurpleMediaCandidate
*c
= i
->data
;
661 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
663 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
664 g_hash_table_remove(lone_cand_links
, foundation
);
667 g_hash_table_insert(lone_cand_links
, foundation
, i
);
671 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
672 g_hash_table_destroy(lone_cand_links
);
678 sipe_backend_get_local_candidates(struct sipe_backend_media
*media
,
679 struct sipe_backend_stream
*stream
)
681 GList
*candidates
= purple_media_get_local_candidates(media
->m
,
683 stream
->participant
);
685 * Sometimes purple will not return complete list of candidates, even
686 * after "candidates-prepared" signal is emitted. This is a feature of
687 * libnice, namely affecting candidates discovered via UPnP. Nice does
688 * not wait until discovery is finished and can signal end of candidate
689 * gathering before all responses from UPnP enabled gateways are received.
691 * Remove any incomplete RTP+RTCP candidate pairs from the list.
693 candidates
= ensure_candidate_pairs(candidates
);
698 sipe_backend_media_accept(struct sipe_backend_media
*media
, gboolean local
)
701 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_ACCEPT
,
706 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
709 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
714 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
717 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
721 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
724 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
725 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
726 default: return PURPLE_MEDIA_NONE
;
730 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
733 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
734 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
735 default: return SIPE_MEDIA_AUDIO;
739 static PurpleMediaCandidateType
740 sipe_candidate_type_to_purple(SipeCandidateType type
)
743 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
744 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
745 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
746 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
747 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
751 static SipeCandidateType
752 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
755 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
756 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
757 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
758 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
759 default: return SIPE_CANDIDATE_TYPE_HOST
;
763 static PurpleMediaNetworkProtocol
764 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
767 case SIPE_NETWORK_PROTOCOL_TCP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
768 case SIPE_NETWORK_PROTOCOL_UDP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
769 default: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
773 static SipeNetworkProtocol
774 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
777 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
: return SIPE_NETWORK_PROTOCOL_TCP
;
778 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
: return SIPE_NETWORK_PROTOCOL_UDP
;
779 default: return SIPE_NETWORK_PROTOCOL_UDP
;