1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session-feature.c: Miscellaneous session feature-provider interface
4  *
5  * Copyright (C) 2008 Red Hat, Inc.
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11 
12 #include "soup-session-feature.h"
13 #include "soup.h"
14 #include "soup-message-private.h"
15 
16 /**
17  * SECTION:soup-session-feature
18  * @short_description: Interface for miscellaneous session features
19  *
20  * #SoupSessionFeature is the interface used by classes that extend
21  * the functionality of a #SoupSession. Some features like HTTP
22  * authentication handling are implemented internally via
23  * #SoupSessionFeature<!-- -->s. Other features can be added to the session
24  * by the application. (Eg, #SoupLogger, #SoupCookieJar.)
25  *
26  * See soup_session_add_feature(), etc, to add a feature to a session.
27  **/
28 
29 /**
30  * SoupSessionFeature:
31  *
32  * An object that implement some sort of optional feature for
33  * #SoupSession.
34  *
35  * Since: 2.24
36  **/
37 
38 /**
39  * SoupSessionFeatureInterface:
40  * @parent: The parent interface.
41  * @attach: Perform setup when a feature is added to a session
42  * @detach: Perform cleanup when a feature is removed from a session
43  * @request_queued: Proxies the session's #SoupSession::request_queued signal
44  * @request_started: Proxies the session's #SoupSession::request_started signal. Deprecated 2.50. Use #SoupMessage::starting instead.
45  * @request_unqueued: Proxies the session's #SoupSession::request_unqueued signal
46  * @add_feature: adds a sub-feature to the main feature
47  * @remove_feature: removes a sub-feature from the main feature
48  * @has_feature: tests if the feature includes a sub-feature
49  *
50  * The interface implemented by #SoupSessionFeature<!-- -->s.
51  *
52  * Since: 2.24
53  **/
54 
G_DEFINE_INTERFACE(SoupSessionFeature,soup_session_feature,G_TYPE_OBJECT)55 G_DEFINE_INTERFACE (SoupSessionFeature, soup_session_feature, G_TYPE_OBJECT)
56 
57 static void
58 weak_notify_unref (gpointer feature, GObject *ex_object)
59 {
60 	g_object_unref (feature);
61 }
62 
63 static void
request_queued(SoupSession * session,SoupMessage * msg,gpointer feature)64 request_queued (SoupSession *session, SoupMessage *msg, gpointer feature)
65 {
66 	if (soup_message_disables_feature (msg, feature))
67 		return;
68 
69 	g_object_ref (feature);
70 	if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_queued) {
71 		SOUP_SESSION_FEATURE_GET_CLASS (feature)->
72 			request_queued (feature, session, msg);
73 	}
74 }
75 
76 static void
request_started(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer feature)77 request_started (SoupSession *session, SoupMessage *msg,
78 		 SoupSocket *socket, gpointer feature)
79 {
80 	if (soup_message_disables_feature (msg, feature))
81 		return;
82 
83 	SOUP_SESSION_FEATURE_GET_CLASS (feature)->
84 		request_started (feature, session, msg, socket);
85 }
86 
87 static void
request_unqueued(SoupSession * session,SoupMessage * msg,gpointer feature)88 request_unqueued (SoupSession *session, SoupMessage *msg, gpointer feature)
89 {
90 	if (soup_message_disables_feature (msg, feature))
91 		return;
92 
93 	if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_unqueued) {
94 		SOUP_SESSION_FEATURE_GET_CLASS (feature)->
95 			request_unqueued (feature, session, msg);
96 	}
97 	g_object_unref (feature);
98 }
99 
100 static void
soup_session_feature_real_attach(SoupSessionFeature * feature,SoupSession * session)101 soup_session_feature_real_attach (SoupSessionFeature *feature, SoupSession *session)
102 {
103 	g_object_weak_ref (G_OBJECT (session),
104 			   weak_notify_unref, g_object_ref (feature));
105 
106 	g_signal_connect (session, "request_queued",
107 			  G_CALLBACK (request_queued), feature);
108 
109 	if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_started) {
110 		g_signal_connect (session, "request_started",
111 				  G_CALLBACK (request_started), feature);
112 	}
113 
114 	g_signal_connect (session, "request_unqueued",
115 			  G_CALLBACK (request_unqueued), feature);
116 }
117 
118 void
soup_session_feature_attach(SoupSessionFeature * feature,SoupSession * session)119 soup_session_feature_attach (SoupSessionFeature *feature,
120 			     SoupSession        *session)
121 {
122 	g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
123 	g_return_if_fail (SOUP_IS_SESSION (session));
124 
125 	SOUP_SESSION_FEATURE_GET_CLASS (feature)->attach (feature, session);
126 }
127 
128 static void
soup_session_feature_real_detach(SoupSessionFeature * feature,SoupSession * session)129 soup_session_feature_real_detach (SoupSessionFeature *feature, SoupSession *session)
130 {
131 	g_object_weak_unref (G_OBJECT (session), weak_notify_unref, feature);
132 
133 	g_signal_handlers_disconnect_by_func (session, request_queued, feature);
134 	g_signal_handlers_disconnect_by_func (session, request_started, feature);
135 	g_signal_handlers_disconnect_by_func (session, request_unqueued, feature);
136 
137 	g_object_unref (feature);
138 }
139 
140 void
soup_session_feature_detach(SoupSessionFeature * feature,SoupSession * session)141 soup_session_feature_detach (SoupSessionFeature *feature,
142 			     SoupSession        *session)
143 {
144 	g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
145 	g_return_if_fail (SOUP_IS_SESSION (session));
146 
147 	SOUP_SESSION_FEATURE_GET_CLASS (feature)->detach (feature, session);
148 }
149 
150 static void
soup_session_feature_default_init(SoupSessionFeatureInterface * iface)151 soup_session_feature_default_init (SoupSessionFeatureInterface *iface)
152 {
153 	iface->attach = soup_session_feature_real_attach;
154 	iface->detach = soup_session_feature_real_detach;
155 }
156 
157 /**
158  * soup_session_feature_add_feature:
159  * @feature: the "base" feature
160  * @type: the #GType of a "sub-feature"
161  *
162  * Adds a "sub-feature" of type @type to the base feature @feature.
163  * This is used for features that can be extended with multiple
164  * different types. Eg, the authentication manager can be extended
165  * with subtypes of #SoupAuth.
166  *
167  * Return value: %TRUE if @feature accepted @type as a subfeature.
168  *
169  * Since: 2.34
170  */
171 gboolean
soup_session_feature_add_feature(SoupSessionFeature * feature,GType type)172 soup_session_feature_add_feature (SoupSessionFeature *feature,
173 				  GType               type)
174 {
175 	SoupSessionFeatureInterface *feature_iface =
176               SOUP_SESSION_FEATURE_GET_CLASS (feature);
177 
178 	if (feature_iface->add_feature)
179 		return feature_iface->add_feature (feature, type);
180 	else
181 		return FALSE;
182 }
183 
184 /**
185  * soup_session_feature_remove_feature:
186  * @feature: the "base" feature
187  * @type: the #GType of a "sub-feature"
188  *
189  * Removes the "sub-feature" of type @type from the base feature
190  * @feature. See soup_session_feature_add_feature().
191  *
192  * Return value: %TRUE if @type was removed from @feature
193  *
194  * Since: 2.34
195  */
196 gboolean
soup_session_feature_remove_feature(SoupSessionFeature * feature,GType type)197 soup_session_feature_remove_feature (SoupSessionFeature *feature,
198 				     GType               type)
199 {
200 	SoupSessionFeatureInterface *feature_iface =
201               SOUP_SESSION_FEATURE_GET_CLASS (feature);
202 
203 	if (feature_iface->remove_feature)
204 		return feature_iface->remove_feature (feature, type);
205 	else
206 		return FALSE;
207 }
208 
209 /**
210  * soup_session_feature_has_feature:
211  * @feature: the "base" feature
212  * @type: the #GType of a "sub-feature"
213  *
214  * Tests if @feature has a "sub-feature" of type @type. See
215  * soup_session_feature_add_feature().
216  *
217  * Return value: %TRUE if @feature has a subfeature of type @type
218  *
219  * Since: 2.34
220  */
221 gboolean
soup_session_feature_has_feature(SoupSessionFeature * feature,GType type)222 soup_session_feature_has_feature (SoupSessionFeature *feature,
223 				  GType               type)
224 {
225 	SoupSessionFeatureInterface *feature_iface =
226               SOUP_SESSION_FEATURE_GET_CLASS (feature);
227 
228 	if (feature_iface->has_feature)
229 		return feature_iface->has_feature (feature, type);
230 	else
231 		return FALSE;
232 }
233