Add copyright notices to all relevant files. (based on svn log)
[profile/ivi/pulseaudio.git] / src / pulse / browser.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5
6   Copyright 2004-2006 Lennart Poettering
7
8   PulseAudio is free software; you can redistribute it and/or modify
9   it under the terms of the GNU Lesser General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   PulseAudio is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public
19   License along with PulseAudio; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21   USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <assert.h>
29 #include <string.h>
30
31 #include <avahi-client/lookup.h>
32 #include <avahi-common/domain.h>
33 #include <avahi-common/error.h>
34
35 #include <pulse/xmalloc.h>
36
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39
40 #include <pulsecore/avahi-wrap.h>
41
42 #include "browser.h"
43
44 #define SERVICE_TYPE_SINK "_pulse-sink._tcp."
45 #define SERVICE_TYPE_SOURCE "_pulse-source._tcp."
46 #define SERVICE_TYPE_SERVER "_pulse-server._tcp."
47
48 struct pa_browser {
49     int ref;
50     pa_mainloop_api *mainloop;
51     AvahiPoll* avahi_poll;
52
53     pa_browse_cb_t callback;
54     void *userdata;
55
56     pa_browser_error_cb_t error_callback;
57     void *error_userdata;
58
59     AvahiClient *client;
60     AvahiServiceBrowser *server_browser, *sink_browser, *source_browser;
61
62 };
63
64 static int map_to_opcode(const char *type, int new) {
65     if (avahi_domain_equal(type, SERVICE_TYPE_SINK))
66         return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
67     else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE))
68         return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
69     else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER))
70         return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
71
72     return -1;
73 }
74
75 static void resolve_callback(
76         AvahiServiceResolver *r,
77         AvahiIfIndex interface,
78         AvahiProtocol protocol,
79         AvahiResolverEvent event,
80         const char *name,
81         const char *type,
82         const char *domain,
83         const char *host_name,
84         const AvahiAddress *aa,
85         uint16_t port,
86         AvahiStringList *txt,
87         AvahiLookupResultFlags flags,
88         void *userdata) {
89
90     pa_browser *b = userdata;
91     pa_browse_info i;
92     char ip[256], a[256];
93     int opcode;
94     int device_found = 0;
95     uint32_t cookie;
96     pa_sample_spec ss;
97     int ss_valid = 0;
98     char *key = NULL, *value = NULL;
99
100     assert(b);
101
102     memset(&i, 0, sizeof(i));
103     i.name = name;
104
105     if (event != AVAHI_RESOLVER_FOUND)
106         goto fail;
107
108     if (!b->callback)
109         goto fail;
110
111     opcode = map_to_opcode(type, 1);
112     assert(opcode >= 0);
113
114     if (aa->proto == AVAHI_PROTO_INET)
115         snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
116     else {
117         assert(aa->proto == AVAHI_PROTO_INET6);
118         snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
119     }
120     i.server = a;
121
122
123     while (txt) {
124
125         if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0)
126             break;
127
128         if (!strcmp(key, "device")) {
129             device_found = 1;
130             pa_xfree((char*) i.device);
131             i.device = value;
132             value = NULL;
133         } else if (!strcmp(key, "server-version")) {
134             pa_xfree((char*) i.server_version);
135             i.server_version = value;
136             value = NULL;
137         } else if (!strcmp(key, "user-name")) {
138             pa_xfree((char*) i.user_name);
139             i.user_name = value;
140             value = NULL;
141         } else if (!strcmp(key, "fqdn")) {
142             size_t l;
143
144             pa_xfree((char*) i.fqdn);
145             i.fqdn = value;
146             value = NULL;
147
148             l = strlen(a);
149             assert(l+1 <= sizeof(a));
150             strncat(a, " ", sizeof(a)-l-1);
151             strncat(a, i.fqdn, sizeof(a)-l-2);
152         } else if (!strcmp(key, "cookie")) {
153
154             if (pa_atou(value, &cookie) < 0)
155                 goto fail;
156
157             i.cookie = &cookie;
158         } else if (!strcmp(key, "description")) {
159             pa_xfree((char*) i.description);
160             i.description = value;
161             value = NULL;
162         } else if (!strcmp(key, "channels")) {
163             uint32_t ch;
164
165             if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255)
166                 goto fail;
167
168             ss.channels = (uint8_t) ch;
169             ss_valid |= 1;
170
171         } else if (!strcmp(key, "rate")) {
172             if (pa_atou(value, &ss.rate) < 0)
173                 goto fail;
174             ss_valid |= 2;
175         } else if (!strcmp(key, "format")) {
176
177             if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID)
178                 goto fail;
179
180             ss_valid |= 4;
181         }
182
183         pa_xfree(key);
184         pa_xfree(value);
185         key = value = NULL;
186
187         txt = avahi_string_list_get_next(txt);
188     }
189
190     /* No device txt record was sent for a sink or source service */
191     if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
192         goto fail;
193
194     if (ss_valid == 7)
195         i.sample_spec = &ss;
196
197     b->callback(b, opcode, &i, b->userdata);
198
199 fail:
200     pa_xfree((void*) i.device);
201     pa_xfree((void*) i.fqdn);
202     pa_xfree((void*) i.server_version);
203     pa_xfree((void*) i.user_name);
204     pa_xfree((void*) i.description);
205
206     pa_xfree(key);
207     pa_xfree(value);
208
209     avahi_service_resolver_free(r);
210 }
211
212 static void handle_failure(pa_browser *b) {
213     const char *e = NULL;
214     assert(b);
215
216     if (b->sink_browser)
217         avahi_service_browser_free(b->sink_browser);
218     if (b->source_browser)
219         avahi_service_browser_free(b->source_browser);
220     if (b->server_browser)
221         avahi_service_browser_free(b->server_browser);
222
223     b->sink_browser = b->source_browser = b->server_browser = NULL;
224
225     if (b->client) {
226         e = avahi_strerror(avahi_client_errno(b->client));
227         avahi_client_free(b->client);
228     }
229
230     b->client = NULL;
231
232     if (b->error_callback)
233         b->error_callback(b, e, b->error_userdata);
234 }
235
236 static void browse_callback(
237         AvahiServiceBrowser *sb,
238         AvahiIfIndex interface,
239         AvahiProtocol protocol,
240         AvahiBrowserEvent event,
241         const char *name,
242         const char *type,
243         const char *domain,
244         AvahiLookupResultFlags flags,
245         void *userdata) {
246
247     pa_browser *b = userdata;
248     assert(b);
249
250     switch (event) {
251         case AVAHI_BROWSER_NEW: {
252
253             if (!avahi_service_resolver_new(
254                           b->client,
255                           interface,
256                           protocol,
257                           name,
258                           type,
259                           domain,
260                           AVAHI_PROTO_UNSPEC,
261                           0,
262                           resolve_callback,
263                           b))
264                 handle_failure(b);
265
266             break;
267         }
268
269         case AVAHI_BROWSER_REMOVE: {
270
271             if (b->callback) {
272                 pa_browse_info i;
273                 int opcode;
274
275                 memset(&i, 0, sizeof(i));
276                 i.name = name;
277
278                 opcode = map_to_opcode(type, 0);
279                 assert(opcode >= 0);
280
281                 b->callback(b, opcode, &i, b->userdata);
282             }
283             break;
284         }
285
286         case AVAHI_BROWSER_FAILURE: {
287             handle_failure(b);
288             break;
289         }
290
291         default:
292             ;
293     }
294 }
295
296 static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {
297     pa_browser *b = userdata;
298     assert(s);
299
300     if (state == AVAHI_CLIENT_FAILURE)
301         handle_failure(b);
302 }
303
304 static void browser_free(pa_browser *b);
305
306 pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
307     return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
308 }
309
310 pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
311     pa_browser *b;
312     int error;
313
314     assert(mainloop);
315
316     if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)
317         return NULL;
318
319     b = pa_xnew(pa_browser, 1);
320     b->mainloop = mainloop;
321     b->ref = 1;
322     b->callback = NULL;
323     b->userdata = NULL;
324     b->error_callback = NULL;
325     b->error_userdata = NULL;
326     b->sink_browser = b->source_browser = b->server_browser = NULL;
327
328     b->avahi_poll = pa_avahi_poll_new(mainloop);
329
330     if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) {
331         if (error_string)
332             *error_string = avahi_strerror(error);
333         goto fail;
334     }
335
336     if ((flags & PA_BROWSE_FOR_SERVERS) &&
337         !(b->server_browser = avahi_service_browser_new(
338                   b->client,
339                   AVAHI_IF_UNSPEC,
340                   AVAHI_PROTO_UNSPEC,
341                   SERVICE_TYPE_SERVER,
342                   NULL,
343                   0,
344                   browse_callback,
345                   b))) {
346
347         if (error_string)
348             *error_string = avahi_strerror(avahi_client_errno(b->client));
349         goto fail;
350     }
351
352     if ((flags & PA_BROWSE_FOR_SINKS) &&
353         !(b->sink_browser = avahi_service_browser_new(
354                   b->client,
355                   AVAHI_IF_UNSPEC,
356                   AVAHI_PROTO_UNSPEC,
357                   SERVICE_TYPE_SINK,
358                   NULL,
359                   0,
360                   browse_callback,
361                   b))) {
362
363         if (error_string)
364             *error_string = avahi_strerror(avahi_client_errno(b->client));
365         goto fail;
366     }
367
368     if ((flags & PA_BROWSE_FOR_SOURCES) &&
369         !(b->source_browser = avahi_service_browser_new(
370                   b->client,
371                   AVAHI_IF_UNSPEC,
372                   AVAHI_PROTO_UNSPEC,
373                   SERVICE_TYPE_SOURCE,
374                   NULL,
375                   0,
376                   browse_callback,
377                   b))) {
378
379         if (error_string)
380             *error_string = avahi_strerror(avahi_client_errno(b->client));
381         goto fail;
382     }
383
384     return b;
385
386 fail:
387     if (b)
388         browser_free(b);
389
390     return NULL;
391 }
392
393 static void browser_free(pa_browser *b) {
394     assert(b && b->mainloop);
395
396     if (b->sink_browser)
397         avahi_service_browser_free(b->sink_browser);
398     if (b->source_browser)
399         avahi_service_browser_free(b->source_browser);
400     if (b->server_browser)
401         avahi_service_browser_free(b->server_browser);
402
403     if (b->client)
404         avahi_client_free(b->client);
405
406     if (b->avahi_poll)
407         pa_avahi_poll_free(b->avahi_poll);
408
409     pa_xfree(b);
410 }
411
412 pa_browser *pa_browser_ref(pa_browser *b) {
413     assert(b);
414     assert(b->ref >= 1);
415     b->ref++;
416     return b;
417 }
418
419 void pa_browser_unref(pa_browser *b) {
420     assert(b);
421     assert(b->ref >= 1);
422
423     if ((-- (b->ref)) <= 0)
424         browser_free(b);
425 }
426
427 void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
428     assert(b);
429
430     b->callback = cb;
431     b->userdata = userdata;
432 }
433
434 void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
435     assert(b);
436
437     b->error_callback = cb;
438     b->error_userdata = userdata;
439 }