/*
 * Adds a command to return the first url for a google I'm feeling lucky search
 * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
 */

#include "../common/pp_internal.h"

#include <errno.h>
#include <string.h>

#include <cmds.h>
#include <conversation.h>
#include <debug.h>
#include <plugin.h>
#include <pluginpref.h>
#include <prefs.h>
#include <proxy.h>
#include <unistd.h>
#include <util.h>

static PurpleCmdId google_cmd_id = 0;

#define GOOGLE_URL_FORMAT	"http://%s/search?q=%s&btnI=I%%27m+Feeling+Lucky"

#define GOOGLE_DOMAIN_PREF	"/core/plugins/core-plugin_pack-google/domain"

/******************************************************************************
 * Structs
 *****************************************************************************/
typedef struct {
	PurpleConversation *conv;
	gchar *host;
	gint port;
	gchar *path;

	gchar *request;
	gsize request_written;

	gint fd;
	gint inpa;

	GString *response;

	PurpleProxyConnectData *conn_data;
} GoogleFetchUrlData;

/******************************************************************************
 * GoogleFetchUrlData API
 *****************************************************************************/
static GoogleFetchUrlData *
google_fetch_url_data_new(const gchar *url) {
	GoogleFetchUrlData *gfud = g_new0(GoogleFetchUrlData, 1);

	if(!purple_url_parse(url, &gfud->host, &gfud->port, &gfud->path, NULL,
						 NULL))
	{
		g_free(gfud);
		return NULL;
	}

	gfud->response = g_string_new("");
	
	return gfud;
}

static void
google_fetch_url_data_free(GoogleFetchUrlData *gfud) {
	g_free(gfud->host);
	g_free(gfud->path);

	g_free(gfud->request);

	g_string_free(gfud->response, TRUE);
	
	if(gfud->inpa > 0)
		purple_input_remove(gfud->inpa);

	if(gfud->fd >= 0)
		close(gfud->fd);

	if(gfud->conn_data)
		purple_proxy_connect_cancel(gfud->conn_data);

	g_free(gfud);
}

/******************************************************************************
 * The final result (hiding in the middle, very sneaky)
 *****************************************************************************/
static void
google_output_url(GoogleFetchUrlData *gfud) {
	gchar *str = NULL, *url_s = NULL, *url_e = NULL;
	gsize len = 0;
	const gchar *needle = "Location: ";

	/* if our conv has disappeared from under us, drop out */
	if(!gfud->conv)
		return;

	str = gfud->response->str;
	len = gfud->response->len;

	url_s = g_strstr_len(str, len, needle);
	if(!url_s)
		return;

	len = strlen(url_s);
	url_s += strlen(needle);

	url_e = g_strstr_len(url_s, len, "\r\n");
	if(!url_e)
		return;

	*url_e = '\0';

	switch (purple_conversation_get_type(gfud->conv))
	{
		case PURPLE_CONV_TYPE_IM:
			purple_conv_im_send(PURPLE_CONV_IM(gfud->conv), url_s);
			break;
		case PURPLE_CONV_TYPE_CHAT:
			purple_conv_chat_send(PURPLE_CONV_CHAT(gfud->conv), url_s);
			break;
		default:
			break;
	}
}

/******************************************************************************
 * URL Stuff
 *****************************************************************************/
static void
im_feeling_lucky_recv_cb(gpointer data, gint source, PurpleInputCondition c) {
	GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data;
	gint len;
	gchar buff[4096];

	while((len = read(source, buff, sizeof(buff))) > 0)
		gfud->response = g_string_append_len(gfud->response, buff, len);

	if(len < 0) {
		if(errno == EAGAIN)
			return;

		/* need to die here */

		return;
	}

	if(len == 0) {
		google_output_url(gfud);
		google_fetch_url_data_free(gfud);
	}
}

static void
im_feeling_lucky_send_cb(gpointer data, gint source, PurpleInputCondition c) {
	GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data;
	gint len, total_len;

	total_len = strlen(gfud->request);

	len = write(gfud->fd, gfud->request + gfud->request_written,
				total_len - gfud->request_written);

	if(len < 0) {
		if(errno == EAGAIN)
			return;

		/* need to die here */

		return;
	}

	gfud->request_written += len;

	if(gfud->request_written < total_len)
		return;

	/* done writing the request, now read the response */
	purple_input_remove(gfud->inpa);
	gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ,
								  im_feeling_lucky_recv_cb, gfud);
}

static void
im_feeling_lucky_cb(gpointer data, gint source, const gchar *e) {
	GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data;

	gfud->conn_data = NULL;

	if(source == -1) {
		purple_debug_error("google", "unable to connect to %s: %s\n",
						   gfud->host, gfud->path);

		google_fetch_url_data_free(gfud);

		return;
	}

	gfud->fd = source;

	gfud->request = g_strdup_printf(
		"GET /%s HTTP/1.1\r\n"
		"Host: %s\r\n"
		"User-Agent: Purple/%u.%u.%u\r\n"
		"Accept: */*\r\n"
		"Connection: close\r\n"
		"Referer: %s\r\n"
		"\r\n",
		gfud->path,
		gfud->host,
		PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION,
		gfud->host);

	gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_WRITE,
								  im_feeling_lucky_send_cb, gfud);
	im_feeling_lucky_send_cb(gfud, gfud->fd, PURPLE_INPUT_WRITE);
}

/******************************************************************************
 * Command stuff
 *****************************************************************************/
static PurpleCmdRet
im_feeling_lucky(PurpleConversation *conv, const gchar *cmd, gchar **args,
				 gchar *error, void *data)
{
	GoogleFetchUrlData *gfud = NULL;
	PurplePlugin *plugin = (PurplePlugin *)data;
	gchar *url = NULL;
	const gchar *pref = NULL;

	pref = purple_prefs_get_string(GOOGLE_DOMAIN_PREF);
	url = g_strdup_printf(GOOGLE_URL_FORMAT,
						  pref ? pref : "www.google.com",
						  purple_url_encode(args[0]));
	gfud = google_fetch_url_data_new(url);
	g_free(url);

	if(!gfud)
		return PURPLE_CMD_RET_FAILED;

	gfud->conv = conv;

	/* now make the connection */
	gfud->conn_data =
		purple_proxy_connect(plugin, NULL, gfud->host, gfud->port,
							 im_feeling_lucky_cb, gfud);

	if(!gfud->conn_data) {
		google_fetch_url_data_free(gfud);
		
		return PURPLE_CMD_RET_FAILED;
	}

	return PURPLE_CMD_RET_OK;
}

/******************************************************************************
 * Prefs Stuff
 *****************************************************************************/
static PurplePluginPrefFrame *
google_pref_frame(PurplePlugin *plugin) {
	PurplePluginPrefFrame *frame = purple_plugin_pref_frame_new();
	PurplePluginPref *pref =
			purple_plugin_pref_new_with_name_and_label(GOOGLE_DOMAIN_PREF,
						_("Google Domain (i.e. www.google.com)"));

	purple_plugin_pref_frame_add(frame, pref);

	return frame;
}

/******************************************************************************
 * Plugin Stuff
 *****************************************************************************/
static gboolean
plugin_load(PurplePlugin *plugin) {
	google_cmd_id =
		purple_cmd_register("google", "s", PURPLE_CMD_P_PLUGIN,
							PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT,
							NULL, PURPLE_CMD_FUNC(im_feeling_lucky),
							_("Returns the url for a Google I'm feeling lucky "
							  "search"),
							plugin);

	return TRUE;
}

static gboolean
plugin_unload(PurplePlugin *plugin) {
	purple_cmd_unregister(google_cmd_id);

	return TRUE;
}

static PurplePluginUiInfo google_prefs_info = {
	google_pref_frame,
	0,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static PurplePluginInfo info = {
	PURPLE_PLUGIN_MAGIC,
	PURPLE_MAJOR_VERSION,
	PURPLE_MINOR_VERSION,
	PURPLE_PLUGIN_STANDARD,
	NULL,
	0,
	NULL,
	PURPLE_PRIORITY_DEFAULT,

	"core-plugin_pack-google",
	NULL,
	PP_VERSION,
	NULL,
	NULL,
	"Gary Kramlich <grim@reaperworld.com>",
	PP_WEBSITE,

	plugin_load,
	plugin_unload,
	NULL,

	NULL,
	NULL,
	&google_prefs_info,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
};

static void
init_plugin(PurplePlugin *plugin) {
#ifdef ENABLE_NLS
	bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */

	info.name = _("Google");
	info.summary = _("Returns the url for a Google \"I'm feeling lucky\" search");
	info.description = info.summary;

	purple_prefs_add_none("/core");
	purple_prefs_add_none("/core/plugins");
	purple_prefs_add_none("/core/plugins/core-plugin_pack-google");

	purple_prefs_add_string(GOOGLE_DOMAIN_PREF, "www.google.com");
}

PURPLE_INIT_PLUGIN(google, init_plugin, info)
