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