media: TURN support
[siplcs.git] / src / purple / purple-media.c
blob70fb330fbc03e0a5ac79ba0fce0d3bef072d2801
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
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
23 #include "glib.h"
24 #include "glib/gstdio.h"
25 #include <fcntl.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include <gst/gststructure.h>
30 #include <gst/gstvalue.h>
32 #include "sipe-common.h"
34 #include "mediamanager.h"
35 #include "request.h"
36 #include "agent.h"
38 #include "sipe-backend.h"
39 #include "sipe-core.h"
41 #include "purple-private.h"
43 struct sipe_backend_media {
44 PurpleMedia *m;
45 GSList *streams;
48 struct sipe_backend_stream {
49 gchar *sessionid;
50 gchar *participant;
51 gboolean candidates_prepared;
52 gboolean local_on_hold;
53 gboolean remote_on_hold;
56 static void
57 backend_stream_free(struct sipe_backend_stream *stream)
59 if (stream) {
60 g_free(stream->sessionid);
61 g_free(stream->participant);
62 g_free(stream);
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;
77 static void
78 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
79 gchar *sessionid,
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)
93 return;
95 ctx->call->candidates_prepared_cb(ctx->call, stream);
99 static void
100 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
101 PurpleMediaState state,
102 gchar *sessionid,
103 gchar *participant,
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);
112 static void
113 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
114 PurpleMediaInfoType type,
115 gchar *sessionid,
116 gchar *participant,
117 gboolean local,
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);
127 if (sessionid) {
128 // Hold specific stream
129 struct sipe_backend_stream *stream;
130 stream = sipe_backend_media_get_stream_by_id(ctx->backend_media,
131 sessionid);
133 if (local)
134 stream->local_on_hold = state;
135 else
136 stream->remote_on_hold = state;
137 } else {
138 // Hold all streams
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;
143 if (local)
144 stream->local_on_hold = state;
145 else
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,
161 sessionid);
163 if (stream) {
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,
175 gboolean initiator)
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,
184 "fsrtpconference",
185 participant, initiator);
187 ctx->call = call;
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
192 * context. */
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);
201 return media;
204 void
205 sipe_backend_media_free(struct sipe_backend_media *media)
207 if (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);
214 g_free(media);
218 #define FS_CODECS_CONF \
219 "# Automatically created by SIPE plugin\n" \
220 "[video/H263]\n" \
221 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=512 ! rtph263pay mtu=512\n";
223 static void
224 ensure_codecs_conf()
226 gchar *filename;
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!");
234 close(fd);
237 g_free(filename);
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;
247 GValue value;
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,
253 NULL);
255 if (!gst_relay_info) {
256 g_value_array_free(relay_info);
257 return NULL;
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;
271 void
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,
279 const gchar *id,
280 const gchar *participant,
281 SipeMediaType type,
282 SipeIceVersion ice_version,
283 gboolean initiator,
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;
290 gchar *transmitter;
292 if (ice_version != SIPE_ICE_NO_ICE) {
293 transmitter = "nice";
294 params_cnt = 2;
296 params = g_new0(GParameter, params_cnt);
298 params[0].name = "compatibility-mode";
299 g_value_init(&params[0].value, G_TYPE_UINT);
300 g_value_set_uint(&params[0].value,
301 ice_version == SIPE_ICE_DRAFT_6 ?
302 NICE_COMPATIBILITY_OC2007 :
303 NICE_COMPATIBILITY_OC2007R2);
305 if (media_relays) {
306 params[1].name = "relay-info";
307 g_value_init(&params[1].value, G_TYPE_VALUE_ARRAY);
308 g_value_set_boxed(&params[1].value, media_relays);
309 } else
310 --params_cnt;
311 } else {
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);
329 g_free(params);
331 return stream;
334 void
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,
352 const gchar *id)
354 GSList *i;
355 for (i = media->streams; i; i = i->next) {
356 struct sipe_backend_stream *stream = i->data;
357 if (sipe_strequal(stream->sessionid, id))
358 return stream;
360 return NULL;
363 void
364 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
365 struct sipe_backend_stream *stream,
366 GList *candidates)
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);
385 GList *
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,
390 stream->sessionid,
391 stream->participant);
394 GList *
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,
399 stream->sessionid,
400 stream->participant);
403 gchar *
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,
411 gboolean local)
413 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
414 stream->sessionid, stream->participant,
415 local);
418 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
419 struct sipe_backend_stream *stream,
420 gboolean local)
422 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
423 stream->sessionid, stream->participant,
424 local);
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),
439 clock_rate);
442 void
443 sipe_backend_codec_free(struct sipe_backend_codec *codec)
445 if (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);
455 gchar *
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);
462 guint
463 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
465 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
468 void
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);
475 GList *
476 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
478 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
481 gboolean
482 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
483 struct sipe_backend_stream *stream,
484 GList *codecs)
486 return purple_media_set_remote_codecs(media->m,
487 stream->sessionid,
488 stream->participant,
489 codecs);
492 GList*
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,
497 stream->sessionid);
498 GList *i = codecs;
501 * Do not announce Theora. Its optional parameters are too long,
502 * Communicator rejects such SDP message and does not support the codec
503 * anyway.
505 while (i) {
506 PurpleMediaCodec *codec = i->data;
507 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
509 if (sipe_strequal(encoding_name,"THEORA")) {
510 GList *tmp;
511 g_object_unref(codec);
512 tmp = i->next;
513 codecs = g_list_delete_link(codecs, i);
514 i = tmp;
515 } else
516 i = i->next;
518 g_free(encoding_name);
521 return codecs;
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,
542 component,
543 sipe_candidate_type_to_purple(type),
544 sipe_network_protocol_to_purple(proto),
546 port);
547 g_object_set(c, "username", username, "password", password, NULL);
548 return (struct sipe_backend_candidate *)c;
551 void
552 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
554 if (candidate)
555 g_object_unref(candidate);
558 gchar *
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);
565 gchar *
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);
572 gchar *
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);
579 gchar *
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);
586 guint
587 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
589 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
592 gchar *
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);
599 guint
600 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
602 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
605 guint32
606 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
608 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
611 void
612 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
614 g_object_set(candidate, "priority", priority, NULL);
617 SipeComponentType
618 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
620 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
623 SipeCandidateType
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);
631 SipeNetworkProtocol
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);
639 static void
640 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
641 gpointer value,
642 gpointer user_data)
644 GList *entry = value;
645 GList **candidates = user_data;
647 g_object_unref(entry->data);
648 *candidates = g_list_delete_link(*candidates, entry);
651 static GList *
652 ensure_candidate_pairs(GList *candidates)
654 GHashTable *lone_cand_links;
655 GList *i;
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);
665 g_free(foundation);
666 } else {
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);
674 return candidates;
677 GList *
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,
682 stream->sessionid,
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);
694 return candidates;
697 void
698 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
700 if (media)
701 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
702 NULL, NULL, local);
705 void
706 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
708 if (media)
709 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
710 NULL, NULL, local);
713 void
714 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
716 if (media)
717 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
718 NULL, NULL, local);
721 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
723 switch (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)
732 switch (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)
742 switch (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)
754 switch (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)
766 switch (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)
776 switch (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;
784 Local Variables:
785 mode: c
786 c-file-style: "bsd"
787 indent-tabs-mode: t
788 tab-width: 8
789 End: