include config.h in browser.c (closes #20)
[profile/ivi/pulseaudio.git] / src / pulse / browser.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5  
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10  
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H 
23 #include "config.h" 
24 #endif 
25
26 #include <assert.h>
27 #include <howl.h>
28
29 #include <pulse/xmalloc.h>
30
31 #include <pulsecore/log.h>
32 #include <pulsecore/core-util.h>
33
34 #include "browser.h"
35
36 #define SERVICE_NAME_SINK "_pulse-sink._tcp."
37 #define SERVICE_NAME_SOURCE "_pulse-source._tcp."
38 #define SERVICE_NAME_SERVER "_pulse-server._tcp."
39
40 struct pa_browser {
41     int ref;
42     pa_mainloop_api *mainloop;
43
44     pa_browse_cb_t callback;
45     void *userdata;
46     
47     sw_discovery discovery;
48     pa_io_event *io_event;
49 };
50
51 static void io_callback(pa_mainloop_api*a, PA_GCC_UNUSED pa_io_event*e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
52     pa_browser *b = userdata;
53     assert(a && b && b->mainloop == a);
54
55     if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
56         pa_log(__FILE__": connection to HOWL daemon failed.");
57         b->mainloop->io_free(b->io_event);
58         b->io_event = NULL;
59         return;
60     }
61 }
62
63 static int type_equal(const char *a, const char *b) {
64     size_t la, lb;
65     
66     if (strcasecmp(a, b) == 0)
67         return 1;
68
69     la = strlen(a);
70     lb = strlen(b);
71
72     if (la > 0 && a[la-1] == '.' && la == lb+1 && strncasecmp(a, b, la-1) == 0)
73         return 1;
74                                             
75     if (lb > 0 && b[lb-1] == '.' && lb == la+1 && strncasecmp(a, b, lb-1) == 0)
76         return 1;
77
78     return 0;
79 }
80
81 static int map_to_opcode(const char *type, int new) {
82     if (type_equal(type, SERVICE_NAME_SINK))
83         return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
84     else if (type_equal(type, SERVICE_NAME_SOURCE))
85         return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
86     else if (type_equal(type, SERVICE_NAME_SERVER))
87         return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
88
89     return -1;
90 }
91
92 static sw_result resolve_reply(
93         sw_discovery discovery,
94         sw_discovery_oid oid,
95         sw_uint32 interface_index,
96         sw_const_string name,
97         sw_const_string type,
98         sw_const_string domain,
99         sw_ipv4_address address,
100         sw_port port,
101         sw_octets text_record,
102         sw_ulong text_record_len,
103         sw_opaque extra) {
104     
105     pa_browser *b = extra;
106     pa_browse_info i;
107     char ip[256], a[256];
108     int opcode;
109     int device_found = 0;
110     uint32_t cookie;
111     pa_sample_spec ss;
112     int ss_valid = 0;
113     sw_text_record_iterator iterator;
114     int free_iterator = 0;
115     char *c = NULL;
116     
117     assert(b);
118
119     sw_discovery_cancel(discovery, oid);
120
121     memset(&i, 0, sizeof(i));
122     i.name = name;
123         
124     if (!b->callback)
125         goto fail;
126
127     opcode = map_to_opcode(type, 1);
128     assert(opcode >= 0);
129     
130     snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
131     i.server = a;
132     
133     if (text_record && text_record_len) {
134         char key[SW_TEXT_RECORD_MAX_LEN];
135         uint8_t val[SW_TEXT_RECORD_MAX_LEN];
136         uint32_t val_len;
137   
138         if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
139             pa_log_error(__FILE__": sw_text_record_string_iterator_init() failed.");
140             goto fail;
141         }
142
143         free_iterator = 1;
144         
145         while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
146             c = pa_xstrndup((char*) val, val_len);
147             
148             if (!strcmp(key, "device")) {
149                 device_found = 1;
150                 pa_xfree((char*) i.device);
151                 i.device = c;
152                 c = NULL;
153             } else if (!strcmp(key, "server-version")) {
154                 pa_xfree((char*) i.server_version);
155                 i.server_version = c;
156                 c = NULL;
157             } else if (!strcmp(key, "user-name")) {
158                 pa_xfree((char*) i.user_name);
159                 i.user_name = c;
160                 c = NULL;
161             } else if (!strcmp(key, "fqdn")) {
162                 size_t l;
163                 
164                 pa_xfree((char*) i.fqdn);
165                 i.fqdn = c;
166                 c = NULL;
167                 
168                 l = strlen(a);
169                 assert(l+1 <= sizeof(a));
170                 strncat(a, " ", sizeof(a)-l-1);
171                 strncat(a, i.fqdn, sizeof(a)-l-2);
172             } else if (!strcmp(key, "cookie")) {
173
174                 if (pa_atou(c, &cookie) < 0)
175                     goto fail;
176                 
177                 i.cookie = &cookie;
178             } else if (!strcmp(key, "description")) {
179                 pa_xfree((char*) i.description);
180                 i.description = c;
181                 c = NULL;
182             } else if (!strcmp(key, "channels")) {
183                 uint32_t ch;
184                 
185                 if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
186                     goto fail;
187
188                 ss.channels = (uint8_t) ch;
189                 ss_valid |= 1;
190
191             } else if (!strcmp(key, "rate")) {
192                 if (pa_atou(c, &ss.rate) < 0)
193                     goto fail;
194                 ss_valid |= 2;
195             } else if (!strcmp(key, "format")) {
196
197                 if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
198                     goto fail;
199                 
200                 ss_valid |= 4;
201             }
202
203             pa_xfree(c);
204             c = NULL;
205         }
206         
207     }
208
209     /* No device txt record was sent for a sink or source service */
210     if (opcode != PA_BROWSE_NEW_SERVER && !device_found) 
211         goto fail;
212
213     if (ss_valid == 7)
214         i.sample_spec = &ss;
215     
216
217     b->callback(b, opcode, &i, b->userdata);
218
219 fail:
220     pa_xfree((void*) i.device);
221     pa_xfree((void*) i.fqdn);
222     pa_xfree((void*) i.server_version);
223     pa_xfree((void*) i.user_name);
224     pa_xfree((void*) i.description);
225     pa_xfree(c);
226
227     if (free_iterator)
228         sw_text_record_iterator_fina(iterator);
229
230     
231     return SW_OKAY;
232 }
233
234 static sw_result browse_reply(
235         sw_discovery discovery,
236         sw_discovery_oid id,
237         sw_discovery_browse_status status,
238         sw_uint32 interface_index,
239         sw_const_string name,
240         sw_const_string type,
241         sw_const_string domain,
242         sw_opaque extra) {
243     
244     pa_browser *b = extra;
245     assert(b);
246
247     switch (status) {
248         case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
249             sw_discovery_oid oid;
250
251             if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
252                 pa_log_error(__FILE__": sw_discovery_resolve() failed");
253
254             break;
255         }
256             
257         case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
258             if (b->callback) {
259                 pa_browse_info i;
260                 int opcode;
261                 
262                 memset(&i, 0, sizeof(i));
263                 i.name = name;
264
265                 opcode = map_to_opcode(type, 0);
266                 assert(opcode >= 0);
267                 
268                 b->callback(b, opcode, &i, b->userdata);
269             }
270             break;
271
272         default:
273             ;
274     }
275
276     return SW_OKAY;
277 }
278
279 pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
280     pa_browser *b;
281     sw_discovery_oid oid;
282
283     b = pa_xnew(pa_browser, 1);
284     b->mainloop = mainloop;
285     b->ref = 1;
286     b->callback = NULL;
287     b->userdata = NULL;
288
289     if (sw_discovery_init(&b->discovery) != SW_OKAY) {
290         pa_log_error(__FILE__": sw_discovery_init() failed.");
291         pa_xfree(b);
292         return NULL;
293     }
294     
295     if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
296         sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
297         sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
298
299         pa_log_error(__FILE__": sw_discovery_browse() failed.");
300         
301         sw_discovery_fina(b->discovery);
302         pa_xfree(b);
303         return NULL;
304     }
305     
306     b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
307     return b;
308 }
309
310 static void browser_free(pa_browser *b) {
311     assert(b && b->mainloop);
312
313     if (b->io_event)
314         b->mainloop->io_free(b->io_event);
315     
316     sw_discovery_fina(b->discovery);
317     pa_xfree(b);
318 }
319
320 pa_browser *pa_browser_ref(pa_browser *b) {
321     assert(b && b->ref >= 1);
322     b->ref++;
323     return b;
324 }
325
326 void pa_browser_unref(pa_browser *b) {
327     assert(b && b->ref >= 1);
328
329     if ((-- (b->ref)) <= 0)
330         browser_free(b);
331 }
332
333 void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
334     assert(b);
335
336     b->callback = cb;
337     b->userdata = userdata;
338 }