1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2008 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: David Zeuthen <davidz@redhat.com>
21 */
22
23 #include <config.h>
24 #include <string.h>
25 #include <glib/gi18n-lib.h>
26
27 #include <avahi-client/client.h>
28 #include <avahi-client/lookup.h>
29 #include <avahi-common/error.h>
30 #include <avahi-common/timeval.h>
31 #include <avahi-glib/glib-watch.h>
32 #include <avahi-glib/glib-malloc.h>
33
34 #include "gvfsdnssdutils.h"
35
36 static gchar *
escape_service_name(const gchar * service_name)37 escape_service_name (const gchar *service_name)
38 {
39 GString *s;
40 char *res;
41 const gchar *p;
42
43 g_return_val_if_fail (service_name != NULL, NULL);
44
45 s = g_string_new (NULL);
46
47 p = service_name;
48 while (*p != '\0')
49 {
50 if (*p == '\\')
51 g_string_append (s, "\\\\");
52 else if (*p == '.')
53 g_string_append (s, "\\.");
54 else if (*p == '/')
55 g_string_append (s, "\\s");
56 else
57 g_string_append_c (s, *p);
58 p++;
59 }
60
61 res = g_uri_escape_string (s->str, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
62 g_string_free (s, TRUE);
63 return res;
64 }
65
66 static gchar *
escape_service_name2(const gchar * service_name)67 escape_service_name2 (const gchar *service_name)
68 {
69 GString *s;
70 const gchar *p;
71
72 g_return_val_if_fail (service_name != NULL, NULL);
73
74 s = g_string_new (NULL);
75
76 p = service_name;
77 while (*p != '\0')
78 {
79 if (*p == '.')
80 g_string_append (s, "%2e");
81 else
82 g_string_append_c (s, *p);
83 p++;
84 }
85
86 return g_string_free (s, FALSE);
87 }
88
89 /**
90 * g_vfs_get_dns_sd_uri_for_triple:
91 * @service_name: DNS-SD service name.
92 * @service_type: DNS-SD service type.
93 * @domain: DNS-SD domain.
94 *
95 * Creates an URI for a file on the GVfs <literal>dns-sd</literal>
96 * virtual file system that provides live data for resolving the given
97 * DNS-SD service.
98 *
99 * The URI is of the form
100 * <literal>dns-sd://domain/service_name.service_type<literal> with
101 * suitable encoding added.
102 *
103 * Note that there may not exist a file at the returned URI, the
104 * resource providing the DNS-SD service will have to be available for
105 * the file to exist.
106 *
107 * Returns: An URI. Free with g_free().
108 **/
109 gchar *
g_vfs_get_dns_sd_uri_for_triple(const gchar * service_name,const gchar * service_type,const gchar * domain)110 g_vfs_get_dns_sd_uri_for_triple (const gchar *service_name,
111 const gchar *service_type,
112 const gchar *domain)
113 {
114 gchar *escaped_service_name;
115 gchar *ret;
116
117 g_return_val_if_fail (service_name != NULL, NULL);
118 g_return_val_if_fail (service_type != NULL, NULL);
119 g_return_val_if_fail (domain != NULL, NULL);
120
121 escaped_service_name = escape_service_name (service_name);
122
123 ret = g_strdup_printf ("dns-sd://%s/%s.%s",
124 domain,
125 escaped_service_name,
126 service_type);
127 g_free (escaped_service_name);
128
129 return ret;
130 }
131
132 /**
133 * g_vfs_encode_dns_sd_triple:
134 * @service_name: DNS-SD service name.
135 * @service_type: DNS-SD service type.
136 * @domain: DNS-SD domain.
137 *
138 * Creates an encoded triple representing a DNS-SD service. The triple
139 * will be of the form
140 * <literal>service_name.service_type.domain</literal> with suitable
141 * encoding.
142 *
143 * Use g_vfs_decode_dns_sd_triple() to decode the returned string.
144 *
145 * Returns: A string representing the triple, free with g_free().
146 **/
147 gchar *
g_vfs_encode_dns_sd_triple(const gchar * service_name,const gchar * service_type,const gchar * domain)148 g_vfs_encode_dns_sd_triple (const gchar *service_name,
149 const gchar *service_type,
150 const gchar *domain)
151 {
152 char *dot_escaped_service_name;
153 char *escaped_service_name;
154 char *escaped_service_type;
155 char *escaped_domain;
156 char *s;
157
158 escaped_service_name = g_uri_escape_string (service_name, NULL, FALSE);
159 dot_escaped_service_name = escape_service_name2 (escaped_service_name);
160 escaped_service_type = g_uri_escape_string (service_type, NULL, FALSE);
161 escaped_domain = g_uri_escape_string (domain, NULL, FALSE);
162 s = g_strdup_printf ("%s.%s.%s",
163 dot_escaped_service_name,
164 escaped_service_type,
165 escaped_domain);
166 g_free (dot_escaped_service_name);
167 g_free (escaped_service_name);
168 g_free (escaped_service_type);
169 g_free (escaped_domain);
170 return s;
171 }
172
173 /**
174 * g_vfs_decode_dns_sd_triple:
175 * @encoded_triple: A string obtained from g_vfs_encode_dns_sd_triple().
176 * @out_service_name: %NULL or return location for the service name.
177 * @out_service_type: %NULL or return location for the service type.
178 * @out_domain: %NULL or return location for the domain.
179 * @error: Return location for error or %NULL.
180 *
181 * Constructs a DNS-SD triple by decoding a string generated from
182 * g_vfs_encode_dns_sd_triple(). This can fail if @encoded_triple is
183 * malformed.
184 *
185 * Returns: %TRUE unless @error is set.
186 **/
187 gboolean
g_vfs_decode_dns_sd_triple(const gchar * encoded_triple,gchar ** out_service_name,gchar ** out_service_type,gchar ** out_domain,GError ** error)188 g_vfs_decode_dns_sd_triple (const gchar *encoded_triple,
189 gchar **out_service_name,
190 gchar **out_service_type,
191 gchar **out_domain,
192 GError **error)
193 {
194 gboolean ret;
195 int n;
196 int m;
197 int service_type_pos;
198 char *escaped_service_name;
199 char *escaped_service_type;
200 char *escaped_domain;
201
202 g_return_val_if_fail (encoded_triple != NULL, FALSE);
203
204
205 escaped_service_name = NULL;
206 escaped_service_type = NULL;
207 escaped_domain = NULL;
208 ret = FALSE;
209
210 if (out_service_name != NULL)
211 *out_service_name = NULL;
212
213 if (out_service_type != NULL)
214 *out_service_type = NULL;
215
216 if (out_domain != NULL)
217 *out_domain = NULL;
218
219 /* Find first '.' followed by an underscore. */
220 for (n = 0; encoded_triple[n] != '\0'; n++)
221 {
222 if (encoded_triple[n] == '.')
223 {
224 if (encoded_triple[n + 1] == '_')
225 break;
226 }
227 }
228 if (encoded_triple[n] == '\0')
229 {
230 g_set_error (error,
231 G_IO_ERROR,
232 G_IO_ERROR_INVALID_ARGUMENT,
233 _("Malformed DNS-SD encoded_triple “%s”"),
234 encoded_triple);
235 goto out;
236 }
237
238 escaped_service_name = g_strndup (encoded_triple, n);
239 if (escaped_service_name == NULL)
240 goto out;
241
242 if (out_service_name != NULL)
243 *out_service_name = g_uri_unescape_string (escaped_service_name, NULL);
244
245 /* skip dot between service name and service type */
246 n += 1;
247
248 service_type_pos = n;
249
250 /* skip next two dots */
251 for (m = 0; m < 2; m++)
252 {
253 for (; encoded_triple[n] != '\0'; n++)
254 {
255 if (encoded_triple[n] == '.')
256 break;
257 }
258 if (encoded_triple[n] == '\0')
259 {
260 g_set_error (error,
261 G_IO_ERROR,
262 G_IO_ERROR_INVALID_ARGUMENT,
263 _("Malformed DNS-SD encoded_triple “%s”"),
264 encoded_triple);
265 goto out;
266 }
267 n++;
268 }
269
270 escaped_service_type = g_strndup (encoded_triple + service_type_pos, n - service_type_pos - 1);
271 if (out_service_type != NULL)
272 *out_service_type = g_uri_unescape_string (escaped_service_type, NULL);
273
274 /* the domain is the rest */
275 if (encoded_triple[n] == '\0')
276 {
277 g_set_error (error,
278 G_IO_ERROR,
279 G_IO_ERROR_INVALID_ARGUMENT,
280 _("Malformed DNS-SD encoded_triple “%s”"),
281 encoded_triple);
282 goto out;
283 }
284
285 escaped_domain = g_strdup (encoded_triple + n);
286 if (out_domain != NULL)
287 *out_domain = g_uri_unescape_string (escaped_domain, NULL);
288
289 ret = TRUE;
290
291 out:
292 g_free (escaped_service_name);
293 g_free (escaped_service_type);
294 g_free (escaped_domain);
295 return ret;
296 }
297
298 gchar *
g_vfs_normalize_encoded_dns_sd_triple(const gchar * encoded_triple)299 g_vfs_normalize_encoded_dns_sd_triple (const gchar *encoded_triple)
300 {
301 char *service_name;
302 char *service_type;
303 char *domain;
304 char *ret;
305
306 ret = NULL;
307
308 if (!g_vfs_decode_dns_sd_triple (encoded_triple,
309 &service_name,
310 &service_type,
311 &domain,
312 NULL))
313 goto out;
314
315 ret = g_vfs_encode_dns_sd_triple (service_name, service_type, domain);
316 g_free (service_name);
317 g_free (service_type);
318 g_free (domain);
319
320 out:
321 return ret;
322 }
323
324