2005-09-15 09:58:31 -04:00
|
|
|
/* Python scripting hooks */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2022-01-29 06:21:13 -05:00
|
|
|
#include "scripting/python/pythoninc.h"
|
2006-10-19 09:21:39 -04:00
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
#include <iconv.h>
|
2006-10-19 09:21:39 -04:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
2005-12-17 01:32:08 -05:00
|
|
|
#include "cache/cache.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "main/event.h"
|
2020-08-09 11:45:22 -04:00
|
|
|
#include "protocol/header.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "protocol/uri.h"
|
2006-04-05 18:52:17 -04:00
|
|
|
#include "scripting/python/core.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "session/session.h"
|
2006-10-19 09:21:39 -04:00
|
|
|
#include "util/memory.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "util/string.h"
|
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
extern PyObject *python_hooks;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
/*
|
|
|
|
* 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.)
|
|
|
|
*/
|
2006-01-28 14:54:11 -05:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
static PyObject *
|
2021-01-02 10:20:27 -05:00
|
|
|
replace_with_python_string(char **dest, PyObject *object)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *str;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2019-11-11 07:33:37 -05:00
|
|
|
if (object == Py_None) {
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
str = (char *) PyUnicode_AsUTF8(object);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!str) return NULL;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
str = stracpy(str);
|
|
|
|
if (!str) return PyErr_NoMemory();
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
mem_free_set(dest, str);
|
|
|
|
return object;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
/* Call a Python hook for a goto-url or follow-url event. */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
static enum evhook_status
|
2006-10-19 09:21:39 -04:00
|
|
|
script_hook_url(va_list ap, void *data)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char **url = va_arg(ap, char **);
|
2006-07-24 12:52:25 -04:00
|
|
|
struct session *ses = va_arg(ap, struct session *);
|
2022-01-29 06:08:30 -05:00
|
|
|
char *method = (char *)data;
|
2006-10-19 09:21:39 -04:00
|
|
|
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;
|
|
|
|
|
Python: Give goto_url_hook only one argument, like follow_url_hook.
On Dec 31, 2006, at 11:30am, Kalle Olavi Niemitalo writes:
>src/scripting/python/hooks.c (script_hook_url) calls hooks as
>goto_url_hook(new-url, current-url) and follow_url_hook(new-url).
>It has a comment saying that the current-url parameter exists
>only for compatibility and that the script can instead use
>elinks.current_url(). However, the current-url parameter was
>added in commit 87e27b9b3e47671484c7eb77d61b75fffc89624f and is
>not in ELinks 0.11.2, so any compatibility problems would only
>hit people who have been using 0.12.GIT snapshots. Can we remove
>the second parameter now before releasing ELinks 0.12pre1?
The decision isn't up to me, but I think this is a good idea. Here's a
patch that would update the documentation and hooks.py, as well as hooks.c.
FYI, if this patch is applied then anyone who's still trying to use a
goto_url_hook that expects a second argument will get a "Browser scripting
error" dialog box that says:
An error occurred while running a Python script:
TypeError: goto_url_hook() takes exactly 2 arguments (1 given)
2006-12-31 10:00:34 -05:00
|
|
|
result = PyObject_CallMethod(python_hooks, method, "s", *url);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!result || !replace_with_python_string(url, result))
|
|
|
|
alert_python_error();
|
|
|
|
|
|
|
|
Py_XDECREF(result);
|
|
|
|
|
|
|
|
python_ses = saved_python_ses;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
|
|
|
}
|
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
static int
|
2021-01-02 10:20:27 -05:00
|
|
|
get_codepage(char *head)
|
2020-08-09 11:45:22 -04:00
|
|
|
{
|
|
|
|
int cp_index = -1;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *part = head;
|
2020-08-09 11:45:22 -04:00
|
|
|
|
|
|
|
if (!head) {
|
|
|
|
goto none;
|
|
|
|
}
|
|
|
|
while (cp_index == -1) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *ct_charset;
|
2020-08-09 11:45:22 -04:00
|
|
|
/* 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. */
|
2021-01-02 10:20:27 -05:00
|
|
|
char *a = parse_header(part, "Content-Type", &part);
|
2020-08-09 11:45:22 -04:00
|
|
|
|
|
|
|
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) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *a = parse_header(head, "Content-Charset", NULL);
|
2020-08-09 11:45:22 -04:00
|
|
|
|
|
|
|
if (a) {
|
|
|
|
cp_index = get_cp_index(a);
|
|
|
|
mem_free(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cp_index == -1) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *a = parse_header(head, "Charset", NULL);
|
2020-08-09 11:45:22 -04:00
|
|
|
|
|
|
|
if (a) {
|
|
|
|
cp_index = get_cp_index(a);
|
|
|
|
mem_free(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
none:
|
|
|
|
if (cp_index == -1) {
|
2021-01-16 15:05:02 -05:00
|
|
|
cp_index = get_cp_index("ISO-8859-1");
|
2020-08-09 11:45:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return cp_index;
|
|
|
|
}
|
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
/* Call a Python hook for a pre-format-html event. */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
static enum evhook_status
|
|
|
|
script_hook_pre_format_html(va_list ap, void *data)
|
|
|
|
{
|
|
|
|
struct session *ses = va_arg(ap, struct session *);
|
2005-12-17 01:32:08 -05:00
|
|
|
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
|
|
|
|
struct fragment *fragment = get_cache_fragment(cached);
|
2021-01-02 10:20:27 -05:00
|
|
|
char *url = struri(cached->uri);
|
2020-08-09 11:45:22 -04:00
|
|
|
int codepage = get_codepage(cached->head);
|
2006-10-19 09:21:39 -04:00
|
|
|
char *method = "pre_format_html_hook";
|
|
|
|
struct session *saved_python_ses = python_ses;
|
2020-08-09 11:45:22 -04:00
|
|
|
PyObject *result = NULL;
|
2006-10-19 09:21:39 -04:00
|
|
|
int success = 0;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
evhook_use_params(ses && cached);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!python_hooks || !cached->length || !*fragment->data
|
|
|
|
|| !PyObject_HasAttrString(python_hooks, method))
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
python_ses = ses;
|
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
if (!is_cp_utf8(codepage)) {
|
|
|
|
size_t iconv_res;
|
|
|
|
size_t ileft;
|
|
|
|
size_t oleft;
|
|
|
|
char *inbuf, *outbuf;
|
2022-01-16 15:17:36 -05:00
|
|
|
char *utf8_data = (char *)mem_alloc(fragment->length * 8);
|
2020-08-09 11:45:22 -04:00
|
|
|
iconv_t cd;
|
|
|
|
|
|
|
|
if (!utf8_data) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
cd = iconv_open("utf-8", get_cp_mime_name(codepage));
|
|
|
|
if (cd == (iconv_t)-1) {
|
|
|
|
mem_free(utf8_data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
inbuf = fragment->data;
|
|
|
|
outbuf = utf8_data;
|
|
|
|
ileft = fragment->length;
|
|
|
|
oleft = fragment->length * 8;
|
|
|
|
iconv_res = iconv(cd, &inbuf, &ileft, &outbuf, &oleft);
|
|
|
|
|
|
|
|
if (iconv_res == -1) {
|
|
|
|
mem_free(utf8_data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
iconv_close(cd);
|
|
|
|
result = PyObject_CallMethod(python_hooks, method, "ss#", url, utf8_data, fragment->length * 8 - oleft);
|
|
|
|
mem_free(utf8_data);
|
|
|
|
} else {
|
|
|
|
result = PyObject_CallMethod(python_hooks, method, "ss#", url, fragment->data, fragment->length);
|
|
|
|
}
|
2021-01-02 10:20:27 -05:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!result) goto error;
|
|
|
|
|
|
|
|
if (result != Py_None) {
|
2021-01-02 10:20:27 -05:00
|
|
|
const char *str;
|
2008-06-09 16:18:03 -04:00
|
|
|
Py_ssize_t len;
|
2006-10-19 09:21:39 -04:00
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
|
2019-11-11 07:33:37 -05:00
|
|
|
str = PyUnicode_AsUTF8AndSize(result, &len);
|
2021-01-02 10:20:27 -05:00
|
|
|
|
2019-11-11 07:33:37 -05:00
|
|
|
if (!str) {
|
2006-10-19 09:21:39 -04:00
|
|
|
goto error;
|
2019-11-11 07:33:37 -05:00
|
|
|
}
|
2006-10-19 09:21:39 -04:00
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
if (!is_cp_utf8(codepage)) {
|
|
|
|
size_t iconv_res;
|
|
|
|
size_t ileft;
|
|
|
|
size_t oleft;
|
|
|
|
char *inbuf, *outbuf;
|
2022-01-16 15:17:36 -05:00
|
|
|
char *dec_data = (char *)mem_alloc(len * 4);
|
2020-08-09 11:45:22 -04:00
|
|
|
iconv_t cd;
|
|
|
|
|
|
|
|
if (!dec_data) {
|
|
|
|
goto error;
|
|
|
|
}
|
2021-01-02 10:20:27 -05:00
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
cd = iconv_open(get_cp_mime_name(codepage), "utf-8");
|
2021-01-02 10:20:27 -05:00
|
|
|
|
2020-08-09 11:45:22 -04:00
|
|
|
if (cd == (iconv_t)-1) {
|
|
|
|
mem_free(dec_data);
|
|
|
|
goto error;
|
|
|
|
}
|
2020-08-09 13:50:19 -04:00
|
|
|
inbuf = (char *)str;
|
2020-08-09 11:45:22 -04:00
|
|
|
outbuf = dec_data;
|
|
|
|
ileft = len;
|
|
|
|
oleft = len * 4;
|
|
|
|
iconv_res = iconv(cd, &inbuf, &ileft, &outbuf, &oleft);
|
|
|
|
|
|
|
|
if (iconv_res == -1) {
|
|
|
|
mem_free(dec_data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
iconv_close(cd);
|
|
|
|
(void) add_fragment(cached, 0, dec_data, len * 4 - oleft);
|
|
|
|
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);
|
|
|
|
}
|
2006-10-19 09:21:39 -04:00
|
|
|
normalize_cache_entry(cached, len);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
2006-10-19 09:21:39 -04:00
|
|
|
|
|
|
|
success = 1;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (!success) alert_python_error();
|
|
|
|
|
|
|
|
Py_XDECREF(result);
|
|
|
|
|
|
|
|
python_ses = saved_python_ses;
|
|
|
|
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
/* Call a Python hook for a get-proxy event. */
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
static enum evhook_status
|
|
|
|
script_hook_get_proxy(va_list ap, void *data)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char **proxy = va_arg(ap, char **);
|
|
|
|
char *url = va_arg(ap, char *);
|
2006-10-19 09:21:39 -04:00
|
|
|
char *method = "proxy_for_hook";
|
|
|
|
PyObject *result;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
evhook_use_params(proxy && url);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!python_hooks || !proxy || !url
|
|
|
|
|| !PyObject_HasAttrString(python_hooks, method))
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
result = PyObject_CallMethod(python_hooks, method, "s", url);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
if (!result || !replace_with_python_string(proxy, result))
|
|
|
|
alert_python_error();
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
Py_XDECREF(result);
|
|
|
|
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2006-10-19 09:21:39 -04:00
|
|
|
/* Call a Python hook for a quit event. */
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
static enum evhook_status
|
|
|
|
script_hook_quit(va_list ap, void *data)
|
|
|
|
{
|
2006-10-19 09:21:39 -04:00
|
|
|
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);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct event_hook_info python_scripting_hooks[] = {
|
2006-10-19 09:21:39 -04:00
|
|
|
{ "goto-url", 0, script_hook_url, "goto_url_hook" },
|
|
|
|
{ "follow-url", 0, script_hook_url, "follow_url_hook" },
|
2005-09-15 09:58:31 -04:00
|
|
|
{ "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,
|
|
|
|
};
|