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