soup-auth.c 17.4 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
/*
3
 * soup-auth.c: HTTP Authentication framework
4
 *
5
 * Copyright (C) 2001-2003, Ximian, Inc.
6
7
8
9
10
11
12
13
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>

14
#include "soup-auth.h"
15
16
#include "soup.h"
#include "soup-connection-auth.h"
17
#include "soup-message-private.h"
18
#include "soup-uri-utils-private.h"
19
20
21
22

/**
 * SoupAuth:
 *
23
24
25
26
27
28
29
30
 * The abstract base class for handling authentication.
 *
 * Specific HTTP Authentication mechanisms are implemented by its subclasses,
 * but applications never need to be aware of the specific subclasses being
 * used.
 *
 * #SoupAuth objects store the authentication data associated with a given bit
 * of web space. They are created automatically by [class@Session].
31
32
33
 **/

typedef struct {
34
        char *realm;
35
	char *authority;
36
	gboolean proxy;
37
	gboolean cancelled;
38
} SoupAuthPrivate;
39

40
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SoupAuth, soup_auth, G_TYPE_OBJECT)
41

42
43
44
45
46
enum {
	PROP_0,

	PROP_SCHEME_NAME,
	PROP_REALM,
47
	PROP_AUTHORITY,
48
49
	PROP_IS_FOR_PROXY,
	PROP_IS_AUTHENTICATED,
50
	PROP_IS_CANCELLED,
51

52
	LAST_PROPERTY
53
54
};

55
56
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };

57
58
59
60
static void
soup_auth_init (SoupAuth *auth)
{
}
61

62
63
64
65
66
67
68
69
70
71
72
73
static void
soup_auth_dispose (GObject *object)
{
	SoupAuth *auth = SOUP_AUTH (object);
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

	if (!priv->cancelled && !soup_auth_is_authenticated (auth))
		soup_auth_cancel (auth);

	G_OBJECT_CLASS (soup_auth_parent_class)->dispose (object);
}

74
static void
75
soup_auth_finalize (GObject *object)
76
77
{
	SoupAuth *auth = SOUP_AUTH (object);
78
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
79

80
	g_free (priv->realm);
81
	g_free (priv->authority);
82
83
84
85

	G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
}

86
87
88
89
90
static void
soup_auth_set_property (GObject *object, guint prop_id,
			const GValue *value, GParamSpec *pspec)
{
	SoupAuth *auth = SOUP_AUTH (object);
91
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
92
93
94

	switch (prop_id) {
	case PROP_REALM:
95
96
		g_free (priv->realm);
		priv->realm = g_value_dup_string (value);
97
		break;
98
99
100
	case PROP_AUTHORITY:
		g_free (priv->authority);
		priv->authority = g_value_dup_string (value);
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
		break;
	case PROP_IS_FOR_PROXY:
		priv->proxy = g_value_get_boolean (value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
soup_auth_get_property (GObject *object, guint prop_id,
			GValue *value, GParamSpec *pspec)
{
	SoupAuth *auth = SOUP_AUTH (object);
116
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
117
118
119
120
121
122
123
124

	switch (prop_id) {
	case PROP_SCHEME_NAME:
		g_value_set_string (value, soup_auth_get_scheme_name (auth));
		break;
	case PROP_REALM:
		g_value_set_string (value, soup_auth_get_realm (auth));
		break;
125
126
	case PROP_AUTHORITY:
		g_value_set_string (value, soup_auth_get_authority (auth));
127
128
129
130
131
132
133
		break;
	case PROP_IS_FOR_PROXY:
		g_value_set_boolean (value, priv->proxy);
		break;
	case PROP_IS_AUTHENTICATED:
		g_value_set_boolean (value, soup_auth_is_authenticated (auth));
		break;
134
135
	case PROP_IS_CANCELLED:
		g_value_set_boolean (value, priv->cancelled);
Patrick Griffis's avatar
Patrick Griffis committed
136
                break;
137
138
139
140
141
142
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

143
144
145
146
147
static gboolean
auth_can_authenticate (SoupAuth *auth)
{
	return TRUE;
}
148

149
static void
Dan Winship's avatar
Dan Winship committed
150
soup_auth_class_init (SoupAuthClass *auth_class)
151
{
152
153
	GObjectClass *object_class = G_OBJECT_CLASS (auth_class);

154
155
	auth_class->can_authenticate = auth_can_authenticate;

156
	object_class->dispose = soup_auth_dispose;
157
158
159
	object_class->finalize     = soup_auth_finalize;
	object_class->set_property = soup_auth_set_property;
	object_class->get_property = soup_auth_get_property;
160
161

	/* properties */
162
	/**
163
         * SoupAuth:scheme-name: (attributes org.gtk.Property.get=soup_auth_get_scheme_name)
Patrick Griffis's avatar
Patrick Griffis committed
164
165
         *
         * The authentication scheme name.
166
	 **/
167
        properties[PROP_SCHEME_NAME] =
Patrick Griffis's avatar
Patrick Griffis committed
168
		g_param_spec_string ("scheme-name",
169
170
171
				     "Scheme name",
				     "Authentication scheme name",
				     NULL,
172
				     G_PARAM_READABLE |
173
				     G_PARAM_STATIC_STRINGS);
174
	/**
175
	 * SoupAuth:realm: (attributes org.gtk.Property.get=soup_auth_get_realm)
176
	 *
Patrick Griffis's avatar
Patrick Griffis committed
177
	 * The authentication realm.
178
	 **/
179
        properties[PROP_REALM] =
Patrick Griffis's avatar
Patrick Griffis committed
180
		g_param_spec_string ("realm",
181
182
183
				     "Realm",
				     "Authentication realm",
				     NULL,
184
				     G_PARAM_READWRITE |
185
				     G_PARAM_STATIC_STRINGS);
186
	/**
187
	 * SoupAuth:authority: (attributes org.gtk.Property.get=soup_auth_get_authority)
188
	 *
189
	 * The authority (host:port) being authenticated to.
190
	 **/
191
        properties[PROP_AUTHORITY] =
192
193
194
		g_param_spec_string ("authority",
				     "Authority",
				     "Authentication authority",
195
				     NULL,
196
				     G_PARAM_READWRITE |
197
				     G_PARAM_STATIC_STRINGS);
198
	/**
199
	 * SoupAuth:is-for-proxy: (attributes org.gtk.Property.get=soup_auth_is_for_proxy)
200
	 *
Patrick Griffis's avatar
Patrick Griffis committed
201
	 * Whether or not the auth is for a proxy server.
202
	 **/
203
        properties[PROP_IS_FOR_PROXY] =
Patrick Griffis's avatar
Patrick Griffis committed
204
		g_param_spec_boolean ("is-for-proxy",
205
206
207
				      "For Proxy",
				      "Whether or not the auth is for a proxy server",
				      FALSE,
208
				      G_PARAM_READWRITE |
209
				      G_PARAM_STATIC_STRINGS);
210
	/**
211
	 * SoupAuth:is-authenticated: (attributes org.gtk.Property.get=soup_auth_is_authenticated)
212
	 *
Patrick Griffis's avatar
Patrick Griffis committed
213
	 * Whether or not the auth has been authenticated.
214
	 **/
215
        properties[PROP_IS_AUTHENTICATED] =
Patrick Griffis's avatar
Patrick Griffis committed
216
		g_param_spec_boolean ("is-authenticated",
217
218
219
				      "Authenticated",
				      "Whether or not the auth is authenticated",
				      FALSE,
220
				      G_PARAM_READABLE |
221
				      G_PARAM_STATIC_STRINGS);
222
	/**
223
	 * SoupAuth:is-cancelled: (attributes org.gtk.Property.get=soup_auth_is_cancelled)
224
	 *
225
	 * Whether or not the auth has been cancelled.
226
	 **/
227
        properties[PROP_IS_CANCELLED] =
228
229
230
231
232
		g_param_spec_boolean ("is-cancelled",
				      "Cancelled",
				      "Whether or not the auth is cancelled",
				      FALSE,
				      G_PARAM_READABLE |
233
234
235
				      G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
236
237
}

Dan Winship's avatar
Dan Winship committed
238
/**
239
 * soup_auth_new: (constructor)
240
241
242
 * @type: the type of auth to create (a subtype of #SoupAuth)
 * @msg: the #SoupMessage the auth is being created for
 * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
Dan Winship's avatar
Dan Winship committed
243
 *
244
245
 * Creates a new #SoupAuth of type @type with the information from
 * @msg and @auth_header.
Dan Winship's avatar
Dan Winship committed
246
 *
247
 * This is called by [class@Session]; you will normally not create auths
248
249
 * yourself.
 *
250
 * Returns: (nullable): the new #SoupAuth, or %NULL if it could
251
 *   not be created
Dan Winship's avatar
Dan Winship committed
252
 **/
253
SoupAuth *
254
soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
255
{
256
	SoupAuth *auth;
257
	GHashTable *params;
258
	const char *scheme;
Patrick Griffis's avatar
Patrick Griffis committed
259
	GUri *uri;
260
	char *authority;
261

262
263
264
	g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
	g_return_val_if_fail (auth_header != NULL, NULL);
265

266
267
268
269
	uri = soup_message_get_uri_for_auth (msg);
	if (!uri)
		return NULL;

Patrick Griffis's avatar
Patrick Griffis committed
270
	authority = g_strdup_printf ("%s:%d", g_uri_get_host (uri), g_uri_get_port (uri));
271
	auth = g_object_new (type,
272
			     "is-for-proxy", (soup_message_get_status (msg) == SOUP_STATUS_PROXY_UNAUTHORIZED),
273
			     "authority", authority,
274
			     NULL);
275
	g_free (authority);
276

277
278
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

279
	scheme = soup_auth_get_scheme_name (auth);
280
	if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0) {
281
282
		g_object_unref (auth);
		return NULL;
283
284
	}

285
	params = soup_header_parse_param_list (auth_header + strlen (scheme));
286
287
	if (!params)
		params = g_hash_table_new (NULL, NULL);
288

289
	priv->realm = g_strdup (g_hash_table_lookup (params, "realm"));
290

291
292
293
294
295
	if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
		g_object_unref (auth);
		auth = NULL;
	}
	soup_header_free_param_list (params);
296
297
298
	return auth;
}

299
300
301
302
303
304
305
/**
 * soup_auth_update:
 * @auth: a #SoupAuth
 * @msg: the #SoupMessage @auth is being updated for
 * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
 *
 * Updates @auth with the information from @msg and @auth_header,
306
307
308
 * possibly un-authenticating it.
 *
 * As with [ctor@Auth.new], this is normally only used by [class@Session].
309
 *
310
 * Returns: %TRUE if @auth is still a valid (but potentially
311
312
 *   unauthenticated) #SoupAuth. %FALSE if something about @auth_params
 *   could not be parsed or incorporated into @auth at all.
313
314
315
316
317
318
319
 **/
gboolean
soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
{
	GHashTable *params;
	const char *scheme, *realm;
	gboolean was_authenticated, success;
320
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
321
322
323
324
325

	g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
	g_return_val_if_fail (auth_header != NULL, FALSE);

326
327
328
	if (priv->cancelled)
		return FALSE;

329
	scheme = soup_auth_get_scheme_name (auth);
330
	if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
331
332
333
334
		return FALSE;

	params = soup_header_parse_param_list (auth_header + strlen (scheme));
	if (!params)
335
		params = g_hash_table_new (NULL, NULL);
336
337

	realm = g_hash_table_lookup (params, "realm");
338
	if (realm && priv->realm && strcmp (realm, priv->realm) != 0) {
339
340
341
342
343
344
345
		soup_header_free_param_list (params);
		return FALSE;
	}

	was_authenticated = soup_auth_is_authenticated (auth);
	success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
	if (was_authenticated != soup_auth_is_authenticated (auth))
346
		g_object_notify_by_pspec (G_OBJECT (auth), properties[PROP_IS_AUTHENTICATED]);
347
348
349
350
	soup_header_free_param_list (params);
	return success;
}

Dan Winship's avatar
Dan Winship committed
351
352
353
354
355
356
/**
 * soup_auth_authenticate:
 * @auth: a #SoupAuth
 * @username: the username provided by the user or client
 * @password: the password provided by the user or client
 *
357
358
359
360
 * Call this on an auth to authenticate it.
 *
 * Normally this will cause the auth's message to be requeued with the new
 * authentication info.
Dan Winship's avatar
Dan Winship committed
361
 **/
362
void
363
364
soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
{
365
	SoupAuthPrivate *priv;
366
367
	gboolean was_authenticated;

368
	g_return_if_fail (SOUP_IS_AUTH (auth));
369
370
	g_return_if_fail (username != NULL);
	g_return_if_fail (password != NULL);
371

372
373
374
375
	priv = soup_auth_get_instance_private (auth);
	if (priv->cancelled)
		return;

376
	was_authenticated = soup_auth_is_authenticated (auth);
377
	SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
378
	if (was_authenticated != soup_auth_is_authenticated (auth))
379
		g_object_notify_by_pspec (G_OBJECT (auth), properties[PROP_IS_AUTHENTICATED]);
380
381
}

382
383
384
385
/**
 * soup_auth_cancel:
 * @auth: a #SoupAuth
 *
386
387
388
389
 * Call this on an auth to cancel it.
 *
 * You need to cancel an auth to complete an asynchronous authenticate operation
 * when no credentials are provided ([method@Auth.authenticate] is not called).
390
391
392
393
394
395
396
397
398
399
400
401
402
403
 * The #SoupAuth will be cancelled on dispose if it hans't been authenticated.
 */
void
soup_auth_cancel (SoupAuth *auth)
{
	SoupAuthPrivate *priv;

	g_return_if_fail (SOUP_IS_AUTH (auth));

	priv = soup_auth_get_instance_private (auth);
	if (priv->cancelled)
		return;

	priv->cancelled = TRUE;
404
	g_object_notify_by_pspec (G_OBJECT (auth), properties[PROP_IS_CANCELLED]);
405
406
}

407
/**
408
 * soup_auth_is_for_proxy: (attributes org.gtk.Method.get_property=is-for-proxy)
409
410
411
412
413
 * @auth: a #SoupAuth
 *
 * Tests whether or not @auth is associated with a proxy server rather
 * than an "origin" server.
 *
414
 * Returns: %TRUE or %FALSE
415
416
417
418
 **/
gboolean
soup_auth_is_for_proxy (SoupAuth *auth)
{
419
420
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

421
422
	g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);

423
	return priv->proxy;
424
425
}

Dan Winship's avatar
Dan Winship committed
426
427
/**
 * soup_auth_get_scheme_name:
428
 * soup_auth_get_scheme_name: (attributes org.gtk.Method.get_property=scheme-name)
Dan Winship's avatar
Dan Winship committed
429
430
 * @auth: a #SoupAuth
 *
431
 * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
Dan Winship's avatar
Dan Winship committed
432
 *
433
 * Returns: the scheme name
Dan Winship's avatar
Dan Winship committed
434
 **/
435
436
437
438
439
440
441
442
const char *
soup_auth_get_scheme_name (SoupAuth *auth)
{
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);

	return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
}

443
/**
444
 * soup_auth_get_authority: (attributes org.gtk.Method.get_property=authority)
445
446
 * @auth: a #SoupAuth
 *
447
 * Returns the authority (host:port) that @auth is associated with.
448
 *
449
 * Returns: the authority
450
451
 **/
const char *
452
soup_auth_get_authority (SoupAuth *auth)
453
{
454
455
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

456
457
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);

458
	return priv->authority;
459
460
}

Dan Winship's avatar
Dan Winship committed
461
/**
462
 * soup_auth_get_realm: (attributes org.gtk.Method.get_property=realm)
Dan Winship's avatar
Dan Winship committed
463
464
 * @auth: a #SoupAuth
 *
465
466
467
468
469
 * Returns @auth's realm.
 *
 * This is an identifier that distinguishes separate authentication spaces on a
 * given server, and may be some string that is meaningful to the user.
 * (Although it is probably not localized.)
Dan Winship's avatar
Dan Winship committed
470
 *
471
 * Returns: the realm name
Dan Winship's avatar
Dan Winship committed
472
 **/
473
474
const char *
soup_auth_get_realm (SoupAuth *auth)
475
{
476
477
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

478
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
479

480
	return priv->realm;
481
482
483
484
485
486
}

/**
 * soup_auth_get_info:
 * @auth: a #SoupAuth
 *
487
488
489
490
491
492
 * Gets an opaque identifier for @auth.
 *
 * The identifier can be used as a hash key or the like. #SoupAuth objects from
 * the same server with the same identifier refer to the same authentication
 * domain (eg, the URLs associated with them take the same usernames and
 * passwords).
493
 *
494
 * Returns: the identifier
495
496
497
498
 **/
char *
soup_auth_get_info (SoupAuth *auth)
{
499
500
	SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);

501
502
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);

503
504
505
506
507
	if (SOUP_IS_CONNECTION_AUTH (auth))
		return g_strdup (SOUP_AUTH_GET_CLASS (auth)->scheme_name);
	else {
		return g_strdup_printf ("%s:%s",
					SOUP_AUTH_GET_CLASS (auth)->scheme_name,
508
					priv->realm);
509
	}
510
511
}

Dan Winship's avatar
Dan Winship committed
512
/**
513
 * soup_auth_is_authenticated: (attributes org.gtk.Method.get_property=is-authenticated)
Dan Winship's avatar
Dan Winship committed
514
515
 * @auth: a #SoupAuth
 *
516
 * Tests if @auth has been given a username and password.
Dan Winship's avatar
Dan Winship committed
517
 *
518
 * Returns: %TRUE if @auth has been given a username and password
Dan Winship's avatar
Dan Winship committed
519
 **/
520
521
gboolean
soup_auth_is_authenticated (SoupAuth *auth)
522
{
523
524
	SoupAuthPrivate *priv;

525
	g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
526

527
528
529
530
	priv = soup_auth_get_instance_private (auth);
	if (priv->cancelled)
		return FALSE;

531
	return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
532
533
}

534
/**
535
 * soup_auth_is_cancelled: (attributes org.gtk.Method.get_property=is-cancelled)
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
 * @auth: a #SoupAuth
 *
 * Tests if @auth has been cancelled
 *
 * Returns: %TRUE if @auth has been cancelled
 */
gboolean
soup_auth_is_cancelled (SoupAuth *auth)
{
 	SoupAuthPrivate *priv;

	g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);

	priv = soup_auth_get_instance_private (auth);
	return priv->cancelled;
}

Dan Winship's avatar
Dan Winship committed
553
554
555
556
557
/**
 * soup_auth_get_authorization:
 * @auth: a #SoupAuth
 * @msg: the #SoupMessage to be authorized
 *
558
559
560
561
 * Generates an appropriate "Authorization" header for @msg.
 *
 * (The session will only call this if [method@Auth.is_authenticated] returned
 * %TRUE.)
Dan Winship's avatar
Dan Winship committed
562
 *
563
 * Returns: the "Authorization" header, which must be freed.
Dan Winship's avatar
Dan Winship committed
564
 **/
565
566
char *
soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
567
{
568
569
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
	g_return_val_if_fail (msg != NULL, NULL);
570

571
	return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
572
}
573

574
575
576
577
578
/**
 * soup_auth_is_ready:
 * @auth: a #SoupAuth
 * @msg: a #SoupMessage
 *
579
580
581
 * Tests if @auth is ready to make a request for @msg with.
 *
 * For most auths, this is equivalent to [method@Auth.is_authenticated], but for
582
583
584
 * some auth types (eg, NTLM), the auth may be sendable (eg, as an
 * authentication request) even before it is authenticated.
 *
585
 * Returns: %TRUE if @auth is ready to make a request with.
586
587
588
589
590
591
 *
 **/
gboolean
soup_auth_is_ready (SoupAuth    *auth,
		    SoupMessage *msg)
{
592
593
	SoupAuthPrivate *priv;

594
595
596
	g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), TRUE);

597
598
599
600
	priv = soup_auth_get_instance_private (auth);
	if (priv->cancelled)
		return FALSE;

601
602
603
604
605
606
	if (SOUP_AUTH_GET_CLASS (auth)->is_ready)
		return SOUP_AUTH_GET_CLASS (auth)->is_ready (auth, msg);
	else
		return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
}

607
608
609
610
611
/**
 * soup_auth_can_authenticate:
 * @auth: a #SoupAuth
 *
 * Tests if @auth is able to authenticate by providing credentials to the
612
 * [method@Auth.authenticate].
613
 *
614
 * Returns: %TRUE if @auth is able to accept credentials.
615
616
617
618
619
 *
 **/
gboolean
soup_auth_can_authenticate (SoupAuth *auth)
{
620
621
	SoupAuthPrivate *priv;

622
623
	g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);

624
625
626
627
	priv = soup_auth_get_instance_private (auth);
	if (priv->cancelled)
		return FALSE;

628
629
630
	return SOUP_AUTH_GET_CLASS (auth)->can_authenticate (auth);
}

Dan Winship's avatar
Dan Winship committed
631
632
633
634
/**
 * soup_auth_get_protection_space:
 * @auth: a #SoupAuth
 * @source_uri: the URI of the request that @auth was generated in
635
 *   response to.
Dan Winship's avatar
Dan Winship committed
636
637
 *
 * Returns a list of paths on the server which @auth extends over.
638
 *
Dan Winship's avatar
Dan Winship committed
639
640
641
642
 * (All subdirectories of these paths are also assumed to be part
 * of @auth's protection space, unless otherwise discovered not to
 * be.)
 *
643
 * Returns: (element-type utf8) (transfer full): the list of
644
 *   paths, which can be freed with [method@Auth.free_protection_space].
Dan Winship's avatar
Dan Winship committed
645
 **/
646
GSList *
Patrick Griffis's avatar
Patrick Griffis committed
647
soup_auth_get_protection_space (SoupAuth *auth, GUri *source_uri)
648
{
649
	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
Patrick Griffis's avatar
Patrick Griffis committed
650
        g_return_val_if_fail (SOUP_URI_IS_VALID (source_uri), NULL);
651

Patrick Griffis's avatar
Patrick Griffis committed
652
653
654
655
        GUri *source_uri_normalized = soup_uri_copy_with_normalized_flags (source_uri);
	GSList *ret = SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri_normalized);
        g_uri_unref (source_uri_normalized);
        return ret;
656
657
}

Dan Winship's avatar
Dan Winship committed
658
/**
659
 * soup_auth_free_protection_space: (skip)
Dan Winship's avatar
Dan Winship committed
660
 * @auth: a #SoupAuth
661
 * @space: the return value from [method@Auth.get_protection_space]
Dan Winship's avatar
Dan Winship committed
662
663
664
 *
 * Frees @space.
 **/
665
666
667
void
soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
{
Dan Winship's avatar
Dan Winship committed
668
	g_slist_free_full (space, g_free);
669
}