1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00
elinks/src/scripting/python/hooks.c
2022-06-06 16:23:15 +02:00

275 lines
6.3 KiB
C

/* Python scripting hooks */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "scripting/python/pythoninc.h"
#include <iconv.h>
#include <stdarg.h>
#include <string.h>
#include "elinks.h"
#include "cache/cache.h"
#include "main/event.h"
#include "protocol/header.h"
#include "protocol/uri.h"
#include "scripting/python/core.h"
#include "session/session.h"
#include "util/memory.h"
#include "util/string.h"
extern PyObject *python_hooks;
/*
* A utility function for script_hook_url() and script_hook_get_proxy():
* Free a char * and replace it with the contents of a Python string.
* (Py_None is ignored.)
*/
static PyObject *
replace_with_python_string(char **dest, PyObject *object)
{
char *str;
if (object == Py_None) {
return object;
}
str = (char *) PyUnicode_AsUTF8(object);
if (!str) return NULL;
str = stracpy(str);
if (!str) return PyErr_NoMemory();
mem_free_set(dest, str);
return object;
}
/* Call a Python hook for a goto-url or follow-url event. */
static enum evhook_status
script_hook_url(va_list ap, void *data)
{
char **url = va_arg(ap, char **);
struct session *ses = va_arg(ap, struct session *);
char *method = (char *)data;
struct session *saved_python_ses = python_ses;
PyObject *result;
evhook_use_params(url && ses);
if (!python_hooks || !url || !*url
|| !PyObject_HasAttrString(python_hooks, method))
return EVENT_HOOK_STATUS_NEXT;
python_ses = ses;
result = PyObject_CallMethod(python_hooks, method, "s", *url);
if (!result || !replace_with_python_string(url, result))
alert_python_error();
Py_XDECREF(result);
python_ses = saved_python_ses;
return EVENT_HOOK_STATUS_NEXT;
}
static int
get_codepage(char *head)
{
int cp_index = -1;
char *part = head;
if (!head) {
goto none;
}
while (cp_index == -1) {
char *ct_charset;
/* scan_http_equiv() appends the meta http-equiv directives to
* the protocol header before this function is called, but the
* HTTP Content-Type header has precedence, so the HTTP header
* will be used if it exists and the meta header is only used
* as a fallback. See bug 983. */
char *a = parse_header(part, "Content-Type", &part);
if (!a) break;
parse_header_param(a, "charset", &ct_charset, 0);
if (ct_charset) {
cp_index = get_cp_index(ct_charset);
mem_free(ct_charset);
}
mem_free(a);
}
if (cp_index == -1) {
char *a = parse_header(head, "Content-Charset", NULL);
if (a) {
cp_index = get_cp_index(a);
mem_free(a);
}
}
if (cp_index == -1) {
char *a = parse_header(head, "Charset", NULL);
if (a) {
cp_index = get_cp_index(a);
mem_free(a);
}
}
none:
if (cp_index == -1) {
cp_index = get_cp_index("ISO-8859-1");
}
return cp_index;
}
/* Call a Python hook for a pre-format-html event. */
static enum evhook_status
script_hook_pre_format_html(va_list ap, void *data)
{
struct session *ses = va_arg(ap, struct session *);
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
struct fragment *fragment = get_cache_fragment(cached);
char *url = struri(cached->uri);
int codepage = get_codepage(cached->head);
int utf8_cp = get_cp_index("utf-8");
const char *method = "pre_format_html_hook";
struct session *saved_python_ses = python_ses;
PyObject *result = NULL;
int success = 0;
evhook_use_params(ses && cached);
if (!python_hooks || !cached->length || !*fragment->data
|| !PyObject_HasAttrString(python_hooks, method))
return EVENT_HOOK_STATUS_NEXT;
python_ses = ses;
if (!is_cp_utf8(codepage)) {
int width;
struct conv_table *ctable = get_translation_table(codepage, utf8_cp);
char *utf8_data = convert_string(ctable, fragment->data, fragment->length, utf8_cp, CSM_NONE,
&width, NULL, NULL);
if (!utf8_data) {
goto error;
}
result = PyObject_CallMethod(python_hooks, method, "ss#", url, utf8_data, width);
mem_free(utf8_data);
} else {
result = PyObject_CallMethod(python_hooks, method, "ss#", url, fragment->data, fragment->length);
}
if (!result) goto error;
if (result != Py_None) {
const char *str;
Py_ssize_t len;
str = PyUnicode_AsUTF8AndSize(result, &len);
if (!str) {
goto error;
}
if (!is_cp_utf8(codepage)) {
int width;
struct conv_table *ctable = get_translation_table(utf8_cp, codepage);
char *dec_data = convert_string(ctable, str, len, codepage, CSM_NONE,
&width, NULL, NULL);
if (!dec_data) {
goto error;
}
(void) add_fragment(cached, 0, dec_data, width);
mem_free(dec_data);
} else {
/* This assumes the Py_ssize_t len is not too large to
* fit in the off_t parameter of normalize_cache_entry().
* add_fragment() itself seems to assume the same thing,
* and there is no standard OFF_MAX macro against which
* ELinks could check the value. */
(void) add_fragment(cached, 0, str, len);
}
normalize_cache_entry(cached, len);
}
success = 1;
error:
if (!success) alert_python_error();
Py_XDECREF(result);
python_ses = saved_python_ses;
return EVENT_HOOK_STATUS_NEXT;
}
/* Call a Python hook for a get-proxy event. */
static enum evhook_status
script_hook_get_proxy(va_list ap, void *data)
{
char **proxy = va_arg(ap, char **);
char *url = va_arg(ap, char *);
const char *method = "proxy_for_hook";
PyObject *result;
evhook_use_params(proxy && url);
if (!python_hooks || !proxy || !url
|| !PyObject_HasAttrString(python_hooks, method))
return EVENT_HOOK_STATUS_NEXT;
result = PyObject_CallMethod(python_hooks, method, "s", url);
if (!result || !replace_with_python_string(proxy, result))
alert_python_error();
Py_XDECREF(result);
return EVENT_HOOK_STATUS_NEXT;
}
/* Call a Python hook for a quit event. */
static enum evhook_status
script_hook_quit(va_list ap, void *data)
{
const char *method = "quit_hook";
PyObject *result;
if (!python_hooks || !PyObject_HasAttrString(python_hooks, method))
return EVENT_HOOK_STATUS_NEXT;
result = PyObject_CallMethod(python_hooks, method, NULL);
if (!result) alert_python_error();
Py_XDECREF(result);
return EVENT_HOOK_STATUS_NEXT;
}
struct event_hook_info python_scripting_hooks[] = {
{ "goto-url", 0, script_hook_url, {"goto_url_hook"} },
{ "follow-url", 0, script_hook_url, {"follow_url_hook"} },
{ "pre-format-html", 0, script_hook_pre_format_html, {NULL} },
{ "get-proxy", 0, script_hook_get_proxy, {NULL} },
{ "quit", 0, script_hook_quit, {NULL} },
NULL_EVENT_HOOK_INFO,
};