• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  * Copyright (C) 2013 Igalia, S.L.
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include <libsoup/soup.h>
15 
16 static SoupSession *session;
17 static GMainLoop *loop;
18 static gboolean debug, head, quiet;
19 static const gchar *output_file_path = NULL;
20 
21 static void
finished(SoupSession * session,SoupMessage * msg,gpointer loop)22 finished (SoupSession *session, SoupMessage *msg, gpointer loop)
23 {
24 	g_main_loop_quit (loop);
25 }
26 
27 static void
get_url(const char * url)28 get_url (const char *url)
29 {
30 	const char *name;
31 	SoupMessage *msg;
32 	const char *header;
33 	FILE *output_file = NULL;
34 
35 	msg = soup_message_new (head ? "HEAD" : "GET", url);
36 	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
37 
38 	if (loop) {
39 		g_object_ref (msg);
40 		soup_session_queue_message (session, msg, finished, loop);
41 		g_main_loop_run (loop);
42 	} else
43 		soup_session_send_message (session, msg);
44 
45 	name = soup_message_get_uri (msg)->path;
46 
47 	if (!debug) {
48 		if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
49 			GTlsCertificateFlags flags;
50 
51 			if (soup_message_get_https_status (msg, NULL, &flags))
52 				g_print ("%s: %d %s (0x%x)\n", name, msg->status_code, msg->reason_phrase, flags);
53 			else
54 				g_print ("%s: %d %s (no handshake status)\n", name, msg->status_code, msg->reason_phrase);
55 		} else if (!quiet || SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
56 			g_print ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
57 	}
58 
59 	if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
60 		header = soup_message_headers_get_one (msg->response_headers,
61 						       "Location");
62 		if (header) {
63 			SoupURI *uri;
64 			char *uri_string;
65 
66 			if (!debug && !quiet)
67 				g_print ("  -> %s\n", header);
68 
69 			uri = soup_uri_new_with_base (soup_message_get_uri (msg), header);
70 			uri_string = soup_uri_to_string (uri, FALSE);
71 			get_url (uri_string);
72 			g_free (uri_string);
73 			soup_uri_free (uri);
74 		}
75 	} else if (!head && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
76 		if (output_file_path) {
77 			output_file = fopen (output_file_path, "w");
78 			if (!output_file)
79 				g_printerr ("Error trying to create file %s.\n", output_file_path);
80 		} else if (!quiet)
81 			output_file = stdout;
82 
83 		if (output_file) {
84 			fwrite (msg->response_body->data,
85 				1,
86 				msg->response_body->length,
87 				output_file);
88 
89 			if (output_file_path)
90 				fclose (output_file);
91 		}
92 	}
93 	g_object_unref (msg);
94 }
95 
96 /* Inline class for providing a pre-configured client certificate */
97 typedef struct _GetTlsCertInteraction        GetTlsCertInteraction;
98 typedef struct _GetTlsCertInteractionClass   GetTlsCertInteractionClass;
99 
100 static GType                    _get_tls_cert_interaction_get_type    (void) G_GNUC_CONST;
101 static GetTlsCertInteraction *  _get_tls_cert_interaction_new         (GTlsCertificate *cert);
102 
103 struct _GetTlsCertInteraction
104 {
105 	GTlsInteraction parent_instance;
106 	GTlsCertificate *cert;
107 };
108 
109 struct _GetTlsCertInteractionClass
110 {
111 	GTlsInteractionClass parent_class;
112 };
113 
114 G_DEFINE_TYPE (GetTlsCertInteraction, _get_tls_cert_interaction, G_TYPE_TLS_INTERACTION);
115 
116 static GTlsInteractionResult
request_certificate(GTlsInteraction * interaction,GTlsConnection * connection,GTlsCertificateRequestFlags flags,GCancellable * cancellable,GError ** error)117 request_certificate (GTlsInteraction              *interaction,
118                      GTlsConnection               *connection,
119                      GTlsCertificateRequestFlags   flags,
120                      GCancellable                 *cancellable,
121                      GError                      **error)
122 {
123 	GetTlsCertInteraction *self = (GetTlsCertInteraction*)interaction;
124 	g_tls_connection_set_certificate (connection, self->cert);
125 	return G_TLS_INTERACTION_HANDLED;
126 }
127 
128 static void
_get_tls_cert_interaction_init(GetTlsCertInteraction * interaction)129 _get_tls_cert_interaction_init (GetTlsCertInteraction *interaction)
130 {
131 }
132 
133 static void
_get_tls_cert_interaction_class_init(GetTlsCertInteractionClass * klass)134 _get_tls_cert_interaction_class_init (GetTlsCertInteractionClass *klass)
135 {
136 	GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
137 	interaction_class->request_certificate = request_certificate;
138 }
139 
140 GetTlsCertInteraction *
_get_tls_cert_interaction_new(GTlsCertificate * cert)141 _get_tls_cert_interaction_new (GTlsCertificate *cert)
142 {
143 	GetTlsCertInteraction *self = g_object_new (_get_tls_cert_interaction_get_type (), NULL);
144 	self->cert = g_object_ref (cert);
145 	return self;
146 }
147 
148 static const char *ca_file, *proxy;
149 static char *client_cert_file, *client_key_file;
150 static gboolean synchronous, ntlm;
151 static gboolean negotiate;
152 
153 static GOptionEntry entries[] = {
154 	{ "ca-file", 'c', 0,
155 	  G_OPTION_ARG_STRING, &ca_file,
156 	  "Use FILE as the TLS CA file", "FILE" },
157 	{ "cert", 0, 0,
158 	  G_OPTION_ARG_STRING, &client_cert_file,
159 	  "Use FILE as the TLS client certificate file", "FILE" },
160 	{ "key", 0, 0,
161 	  G_OPTION_ARG_STRING, &client_key_file,
162 	  "Use FILE as the TLS client key file", "FILE" },
163 	{ "debug", 'd', 0,
164 	  G_OPTION_ARG_NONE, &debug,
165 	  "Show HTTP headers", NULL },
166 	{ "head", 'h', 0,
167 	  G_OPTION_ARG_NONE, &head,
168 	  "Do HEAD rather than GET", NULL },
169 	{ "ntlm", 'n', 0,
170 	  G_OPTION_ARG_NONE, &ntlm,
171 	  "Use NTLM authentication", NULL },
172 	{ "output", 'o', 0,
173 	  G_OPTION_ARG_STRING, &output_file_path,
174 	  "Write the received data to FILE instead of stdout", "FILE" },
175 	{ "proxy", 'p', 0,
176 	  G_OPTION_ARG_STRING, &proxy,
177 	  "Use URL as an HTTP proxy", "URL" },
178 	{ "quiet", 'q', 0,
179 	  G_OPTION_ARG_NONE, &quiet,
180 	  "Don't show HTTP status code", NULL },
181 	{ "sync", 's', 0,
182 	  G_OPTION_ARG_NONE, &synchronous,
183 	  "Use SoupSessionSync rather than SoupSessionAsync", NULL },
184 	{ NULL }
185 };
186 
187 static GOptionEntry negotiate_entries[] = {
188 	{ "negotiate", 'N', 0,
189 	  G_OPTION_ARG_NONE, &negotiate,
190 	  "Use Negotiate authentication", NULL },
191 	{ NULL }
192 };
193 
194 int
main(int argc,char ** argv)195 main (int argc, char **argv)
196 {
197 	GOptionContext *opts;
198 	const char *url;
199 	SoupURI *proxy_uri, *parsed;
200 	GError *error = NULL;
201 	SoupLogger *logger = NULL;
202 	char *help;
203 
204 	opts = g_option_context_new (NULL);
205 	g_option_context_add_main_entries (opts, entries, NULL);
206 	if (soup_auth_negotiate_supported())
207 		g_option_context_add_main_entries (opts, negotiate_entries, NULL);
208 	if (!g_option_context_parse (opts, &argc, &argv, &error)) {
209 		g_printerr ("Could not parse arguments: %s\n",
210 			    error->message);
211 		g_printerr ("%s",
212 			    g_option_context_get_help (opts, TRUE, NULL));
213 		exit (1);
214 	}
215 
216 	if (argc != 2) {
217 		help = g_option_context_get_help (opts, TRUE, NULL);
218 		g_printerr ("%s", help);
219 		g_free (help);
220 		exit (1);
221 	}
222 	g_option_context_free (opts);
223 
224 	url = argv[1];
225 	parsed = soup_uri_new (url);
226 	if (!parsed) {
227 		g_printerr ("Could not parse '%s' as a URL\n", url);
228 		exit (1);
229 	}
230 	soup_uri_free (parsed);
231 
232 	session = g_object_new (SOUP_TYPE_SESSION,
233 				SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
234 				SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
235 				SOUP_SESSION_USER_AGENT, "get ",
236 				SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
237 				NULL);
238 	if (ntlm)
239 		soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
240 	if (ca_file)
241 		g_object_set (session, "ssl-ca-file", ca_file, NULL);
242 
243 	if (client_cert_file) {
244 		GTlsCertificate *client_cert;
245 		GetTlsCertInteraction *interaction;
246 		if (!client_key_file) {
247 			g_printerr ("--key is required with --cert\n");
248 			exit (1);
249 		}
250 		client_cert = g_tls_certificate_new_from_files (client_cert_file, client_key_file, &error);
251 		if (!client_cert) {
252 			g_printerr ("%s\n", error->message);
253 			exit (1);
254 		}
255 		interaction = _get_tls_cert_interaction_new (client_cert);
256 		g_object_set (session, SOUP_SESSION_TLS_INTERACTION, interaction, NULL);
257 	}
258 
259 	if (debug) {
260 		logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
261 		soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
262 		g_object_unref (logger);
263 	}
264 
265 	if (proxy) {
266 		proxy_uri = soup_uri_new (proxy);
267 		if (!proxy_uri) {
268 			g_printerr ("Could not parse '%s' as URI\n",
269 				    proxy);
270 			exit (1);
271 		}
272 
273 		g_object_set (G_OBJECT (session),
274 			      SOUP_SESSION_PROXY_URI, proxy_uri,
275 			      NULL);
276 		soup_uri_free (proxy_uri);
277 	}
278 
279 #ifdef LIBSOUP_HAVE_GSSAPI
280 	if (negotiate) {
281 		soup_session_add_feature_by_type (session,
282 						  SOUP_TYPE_AUTH_NEGOTIATE);
283 	}
284 #endif /* LIBSOUP_HAVE_GSSAPI */
285 
286 	if (!synchronous)
287 		loop = g_main_loop_new (NULL, TRUE);
288 
289 	get_url (url);
290 
291 	if (!synchronous)
292 		g_main_loop_unref (loop);
293 
294 	g_object_unref (session);
295 
296 	return 0;
297 }
298