static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *self,
														MidoriView *inView,
														SoupMessage *inMessage,
														GSList *inUnknownCookies)
{
	/* Ask user for policy of unkndown domains in an undistracting way.
	 * The idea is to put the message not in a modal window but into midori's info bar.
	 * Then we'll set up our own GMainLoop to simulate a modal info bar. We need to
	 * connect to all possible signals of info bar, web view and so on to handle user's
	 * decision and to get out of our own GMainLoop. After that webkit resumes processing
	 * data.
	 */
	CookiePermissionManagerPrivate			*priv=self->priv;
	GtkWidget								*infobar;
/* FIXME: Find a way to add "details" widget */
#ifndef NO_INFOBAR_DETAILS
	GtkWidget								*widget;
	GtkWidget								*contentArea;
	GtkWidget								*vbox, *hbox;
	GtkWidget								*expander;
	GtkListStore							*listStore;
	GtkTreeIter								listIter;
	GtkWidget								*scrolled;
	GtkWidget								*list;
	GtkCellRenderer							*renderer;
	GtkTreeViewColumn						*column;
#endif
	gchar									*text;
	gint									numberDomains, numberCookies;
	GSList									*sortedCookies, *cookies;
	WebKitWebView							*webkitView;
	CookiePermissionManagerModalInfobar		*modalInfo;

	/* Get webkit view of midori view */
	webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(inView));
	modalInfo=g_new0(CookiePermissionManagerModalInfobar, 1);

	/* Create a copy of cookies and sort them */
	sortedCookies=_cookie_permission_manager_get_number_domains_and_cookies(self,
																			inUnknownCookies,
																			&numberDomains,
																			&numberCookies);

/* FIXME: Find a way to add "details" widget */
#ifndef NO_INFOBAR_DETAILS
	/* Create list model and fill in data */
	listStore=gtk_list_store_new(N_COLUMN,
									G_TYPE_STRING,	/* DOMAIN_COLUMN */
									G_TYPE_STRING,	/* PATH_COLUMN */
									G_TYPE_STRING,	/* NAME_COLUMN */
									G_TYPE_STRING,	/* VALUE_COLUMN */
									G_TYPE_STRING	/* EXPIRE_DATE_COLUMN */);

	for(cookies=sortedCookies; cookies; cookies=cookies->next)
	{
		SoupCookie				*cookie=(SoupCookie*)cookies->data;
		SoupDate				*cookieDate=soup_cookie_get_expires(cookie);

		if(cookieDate) text=soup_date_to_string(cookieDate, SOUP_DATE_HTTP);
			else text=g_strdup(_("Till session end"));

		gtk_list_store_append(listStore, &listIter);
		gtk_list_store_set(listStore,
							&listIter,
							DOMAIN_COLUMN, soup_cookie_get_domain(cookie),
							PATH_COLUMN, soup_cookie_get_path(cookie),
							NAME_COLUMN, soup_cookie_get_name(cookie),
							VALUE_COLUMN, soup_cookie_get_value(cookie),
							EXPIRE_DATE_COLUMN, text,
							-1);

		g_free(text);
	}
#endif

	/* Create description text */
	if(numberDomains==1)
	{
		const gchar					*cookieDomain=soup_cookie_get_domain((SoupCookie*)sortedCookies->data);

		if(*cookieDomain=='.') cookieDomain++;
		
		if(numberCookies>1)
			text=g_strdup_printf(_("The website %s wants to store %d cookies."), cookieDomain, numberCookies);
		else
			text=g_strdup_printf(_("The website %s wants to store a cookie."), cookieDomain);
	}
		else
		{
			text=g_strdup_printf(_("Multiple websites want to store %d cookies in total."), numberCookies);
		}

	/* Create info bar message and buttons */
	infobar=midori_view_add_info_bar(inView,
										GTK_MESSAGE_QUESTION,
										text,
										G_CALLBACK(_cookie_permission_manager_on_infobar_policy_decision),
										NULL,
										_("_Accept"), COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT,
										_("Accept for this _session"), COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION,
										_("De_ny"), COOKIE_PERMISSION_MANAGER_POLICY_BLOCK,
										_("Deny _this time"), COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED,
										NULL);
	g_free(text);

	/* midori_view_add_info_bar() in version 0.4.8 expects a GObject as user data
	 * but I don't want to create an GObject just for a simple struct. So set object
	 * data by our own
	 */
	g_object_set_data_full(G_OBJECT(infobar), "cookie-permission-manager-infobar-data", modalInfo, (GDestroyNotify)g_free);

/* FIXME: Find a way to add "details" widget */
#ifndef NO_INFOBAR_DETAILS
	/* Get content area of infobar */
	contentArea=gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar));

	/* Create list and set up columns of list */
	list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore));
#ifndef HAVE_GTK3
	gtk_widget_set_size_request(list, -1, 100);
#endif

	renderer=gtk_cell_renderer_text_new();
	column=gtk_tree_view_column_new_with_attributes(_("Domain"),
													renderer,
													"text", DOMAIN_COLUMN,
													NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

	renderer=gtk_cell_renderer_text_new();
	column=gtk_tree_view_column_new_with_attributes(_("Path"),
													renderer,
													"text", PATH_COLUMN,
													NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

	renderer=gtk_cell_renderer_text_new();
	column=gtk_tree_view_column_new_with_attributes(_("Name"),
													renderer,
													"text", NAME_COLUMN,
													NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

	renderer=gtk_cell_renderer_text_new();
	column=gtk_tree_view_column_new_with_attributes(_("Value"),
													renderer,
													"text", VALUE_COLUMN,
													NULL);
	g_object_set(G_OBJECT(renderer),
					"ellipsize", PANGO_ELLIPSIZE_END,
					"width-chars", 30,
					NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

	renderer=gtk_cell_renderer_text_new();
	column=gtk_tree_view_column_new_with_attributes(_("Expire date"),
													renderer,
													"text", EXPIRE_DATE_COLUMN,
													NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

	scrolled=gtk_scrolled_window_new(NULL, NULL);
#ifdef HAVE_GTK3
	gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), 100);
#endif
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(scrolled), list);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
	gtk_container_add(GTK_CONTAINER(expander), scrolled);

	gtk_widget_show_all(vbox);
	gtk_container_add(GTK_CONTAINER(contentArea), vbox);

	/* Set state of expander based on config 'show-details-when-ask' */
	gtk_expander_set_expanded(GTK_EXPANDER(expander),
								midori_extension_get_boolean(priv->extension, "show-details-when-ask"));
	g_signal_connect_swapped(expander, "notify::expanded", G_CALLBACK(_cookie_permission_manager_when_ask_expander_changed), self);
#endif

	/* Show all widgets of info bar */
	gtk_widget_show_all(infobar);

	/* Connect signals to quit main loop */
	g_signal_connect(webkitView, "navigation-policy-decision-requested", G_CALLBACK(_cookie_permission_manager_on_infobar_webview_navigate), infobar);
	g_signal_connect(infobar, "destroy", G_CALLBACK(_cookie_permission_manager_on_infobar_destroy), modalInfo);

	/* Let info bar be modal and set response to default */
	modalInfo->response=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED;
	modalInfo->mainLoop=g_main_loop_new(NULL, FALSE);

	GDK_THREADS_LEAVE();
	g_main_loop_run(modalInfo->mainLoop);
	GDK_THREADS_ENTER();

	g_main_loop_unref(modalInfo->mainLoop);

	modalInfo->mainLoop=NULL;

	/* Disconnect signal handler to webkit's web view  */
	g_signal_handlers_disconnect_by_func(webkitView, G_CALLBACK(_cookie_permission_manager_on_infobar_webview_navigate), infobar);

	/* Store user's decision in database if it is not a temporary block.
	 * We use the already sorted list of cookies to prevent multiple
	 * updates of database for the same domain. This sorted list is a copy
	 * to avoid a reorder of cookies
	 */
	if(modalInfo->response!=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED)
	{
		const gchar					*lastDomain=NULL;

		/* Iterate through cookies and store decision for each domain once */
		for(cookies=sortedCookies; cookies; cookies=cookies->next)
		{
			SoupCookie				*cookie=(SoupCookie*)cookies->data;
			const gchar				*cookieDomain=soup_cookie_get_domain(cookie);

			if(*cookieDomain=='.') cookieDomain++;

			/* Store decision if new domain found while iterating through cookies */
			if(!lastDomain || g_ascii_strcasecmp(lastDomain, cookieDomain)!=0)
			{
				gchar	*sql;
				gchar	*error=NULL;
				gint	success;

				sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (domain, value) VALUES ('%q', %d);",
										cookieDomain,
										modalInfo->response);
				success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
				if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
				if(error) sqlite3_free(error);
				sqlite3_free(sql);

				lastDomain=cookieDomain;
			}
		}
	}

	/* Free up allocated resources */
	g_slist_free(sortedCookies);

	/* Return response */
	return(modalInfo->response==COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED ?
			COOKIE_PERMISSION_MANAGER_POLICY_BLOCK : modalInfo->response);
}
Example #2
0
/* FIXME: moar tests! */
static void
do_cookies_parsing_test (void)
{
	SoupSession *session;
	SoupMessage *msg;
	SoupCookieJar *jar;
	GSList *cookies, *iter;
	SoupCookie *cookie;
	gboolean got1, got2, got3;

	debug_printf (1, "\nSoupCookie parsing test\n");

	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
	soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
	jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));

	/* "httponly" is case-insensitive, and its value (if any) is ignored */
	msg = soup_message_new_from_uri ("GET", first_party_uri);
	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
				     "one=1; httponly; max-age=100");
	soup_session_send_message (session, msg);
	g_object_unref (msg);

	msg = soup_message_new_from_uri ("GET", first_party_uri);
	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
				     "two=2; HttpOnly; max-age=100");
	soup_session_send_message (session, msg);
	g_object_unref (msg);

	msg = soup_message_new_from_uri ("GET", first_party_uri);
	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
				     "three=3; httpONLY=Wednesday; max-age=100");
	soup_session_send_message (session, msg);
	g_object_unref (msg);

	cookies = soup_cookie_jar_get_cookie_list (jar, first_party_uri, TRUE);
	got1 = got2 = got3 = FALSE;

	for (iter = cookies; iter; iter = iter->next) {
		cookie = iter->data;

		if (!strcmp (soup_cookie_get_name (cookie), "one")) {
			got1 = TRUE;
			if (!soup_cookie_get_http_only (cookie)) {
				debug_printf (1, "  cookie 1 is not HttpOnly!\n");
				errors++;
			}
			if (!soup_cookie_get_expires (cookie)) {
				debug_printf (1, "  cookie 1 did not fully parse!\n");
				errors++;
			}
		} else if (!strcmp (soup_cookie_get_name (cookie), "two")) {
			got2 = TRUE;
			if (!soup_cookie_get_http_only (cookie)) {
				debug_printf (1, "  cookie 2 is not HttpOnly!\n");
				errors++;
			}
			if (!soup_cookie_get_expires (cookie)) {
				debug_printf (1, "  cookie 3 did not fully parse!\n");
				errors++;
			}
		} else if (!strcmp (soup_cookie_get_name (cookie), "three")) {
			got3 = TRUE;
			if (!soup_cookie_get_http_only (cookie)) {
				debug_printf (1, "  cookie 3 is not HttpOnly!\n");
				errors++;
			}
			if (!soup_cookie_get_expires (cookie)) {
				debug_printf (1, "  cookie 3 did not fully parse!\n");
				errors++;
			}
		} else {
			debug_printf (1, "  got unexpected cookie '%s'\n",
				      soup_cookie_get_name (cookie));
			errors++;
		}

		soup_cookie_free (cookie);
	}
	g_slist_free (cookies);

	if (!got1) {
		debug_printf (1, "  didn't get cookie 1\n");
		errors++;
	}
	if (!got2) {
		debug_printf (1, "  didn't get cookie 2\n");
		errors++;
	}
	if (!got3) {
		debug_printf (1, "  didn't get cookie 3\n");
		errors++;
	}

	soup_test_session_abort_unref (session);
}