1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-26 02:46:13 -04:00

Additional functionality for Python backend.

This commit is contained in:
M. Levinson 2006-10-19 15:21:39 +02:00 committed by Witold Filipczyk
parent b52583902f
commit ebadc9bf9e
6 changed files with 547 additions and 248 deletions

View File

@ -1,60 +1,266 @@
import re
import sys
"""Example Python hooks for ELinks.
If ELinks is compiled with an embedded Python interpreter, it will try
to import a Python module called hooks when the browser starts up. To
use Python code from within ELinks, create a file called hooks.py in
the ~/.elinks directory, or in the system-wide configuration directory
(defined when ELinks was compiled), or in the standard Python search path.
An example hooks.py file can be found in the contrib/python directory of
the ELinks source distribution.
The hooks module may implement any of several functions that will be
called automatically by ELinks in appropriate circumstances; it may also
bind ELinks keystrokes to callable Python objects so that arbitrary Python
code can be invoked at the whim of the user.
Functions that will be automatically called by ELinks (if they're defined):
follow_url_hook() -- Rewrite a URL for a link that's about to be followed.
goto_url_hook() -- Rewrite a URL received from a "Go to URL" dialog box.
pre_format_html_hook() -- Rewrite a document's body before it's formatted.
proxy_for_hook() -- Determine what proxy server to use for a given URL.
quit_hook() -- Clean up before ELinks exits.
"""
import elinks
dumbprefixes = {
"7th" : "http://7thguard.net/",
"b" : "http://babelfish.altavista.com/babelfish/tr/",
"bz" : "http://bugzilla.elinks.cz/",
"bug" : "http://bugzilla.elinks.cz/",
"d" : "http://www.dict.org/",
"g" : "http://www.google.com/",
"gg" : "http://www.google.com/",
"go" : "http://www.google.com/",
"fm" : "http://www.freshmeat.net/",
"sf" : "http://www.sourceforge.net/",
"dbug" : "http://bugs.debian.org/",
"dpkg" : "http://packages.debian.org/",
"pycur" : "http://www.python.org/doc/current/",
"pydev" : "http://www.python.org/dev/doc/devel/",
"pyhelp" : "http://starship.python.net/crew/theller/pyhelp.cgi",
"pyvault" : "http://www.vex.net/parnassus/",
"e2" : "http://www.everything2.org/",
"sd" : "http://www.slashdot.org/"
"7th" : "http://7thguard.net/",
"b" : "http://babelfish.altavista.com/babelfish/tr/",
"bz" : "http://bugzilla.elinks.cz/",
"bug" : "http://bugzilla.elinks.cz/",
"d" : "http://www.dict.org/",
"g" : "http://www.google.com/",
"gg" : "http://www.google.com/",
"go" : "http://www.google.com/",
"fm" : "http://www.freshmeat.net/",
"sf" : "http://www.sourceforge.net/",
"dbug" : "http://bugs.debian.org/",
"dpkg" : "http://packages.debian.org/",
"pycur" : "http://www.python.org/doc/current/",
"pydev" : "http://www.python.org/dev/doc/devel/",
"pyhelp" : "http://starship.python.net/crew/theller/pyhelp.cgi",
"pyvault" : "http://www.vex.net/parnassus/",
"e2" : "http://www.everything2.org/",
"sd" : "http://www.slashdot.org/"
}
cygwin = re.compile("cygwin\.com")
cygwin_sub1 = re.compile('<body bgcolor="#000000" color="#000000"')
cygwin_sub2 = '<body bgcolor="#ffffff" color="#000000"'
mbank = re.compile('^https://www\.mbank\.com\.pl/ib_navibar_3\.asp')
mbank_sub1 = re.compile('<td valign="top"><img')
mbank_sub2 = '<tr><td valign="top"><img'
google_redirect = re.compile('^http://www\.google\.com/url\?sa=D&q=(.*)')
def goto_url_hook(url, current_url):
global dumbprefixes
"""Rewrite a URL that was entered in a "Go to URL" dialog box.
if dumbprefixes.has_key(url):
return dumbprefixes[url];
else:
return None
This function should return a URL for ELinks to follow, or None if
ELinks should follow the original URL.
Arguments:
url -- The URL provided by the user.
current_url -- The URL of the document being viewed, or None if no
document is being viewed.
"""
if url in dumbprefixes:
return dumbprefixes[url]
def follow_url_hook(url):
m = google_redirect.search(url)
if m:
return m.group(1)
return None
"""Rewrite a URL for a link that's about to be followed.
This function should return a URL for ELinks to follow, or None if
ELinks should follow the original URL.
Arguments:
url -- The URL of the link.
"""
google_redirect = 'http://www.google.com/url?sa=D&q='
if url.startswith(google_redirect):
return url.replace(google_redirect, '')
def pre_format_html_hook(url, html):
if cygwin.search(url):
html2 = cygwin_sub1.sub(cygwin_sub2, html)
return html2
if mbank.search(url):
html2 = mbank_sub1.sub(mbank_sub2, html)
return html2
return None
"""Rewrite the body of a document before it's formatted.
This function should return a string for ELinks to format, or None
if ELinks should format the original document body. It can be used
to repair bad HTML, alter the layout or colors of a document, etc.
Arguments:
url -- The URL of the document.
html -- The body of the document.
"""
if "cygwin.com" in url:
return html.replace('<body bgcolor="#000000" color="#000000"',
'<body bgcolor="#ffffff" color="#000000"')
elif url.startswith("https://www.mbank.com.pl/ib_navibar_3.asp"):
return html.replace('<td valign="top"><img',
'<tr><td valign="top"><img')
def proxy_for_hook(url):
return None
"""Determine what proxy server to use for a given URL.
This function should return a string of the form "hostname:portnumber"
identifying a proxy server to use, or an empty string if the request
shouldn't use any proxy server, or None if ELinks should use its
default proxy server.
Arguments:
url -- The URL that is about to be followed.
"""
pass
def quit_hook():
return None
"""Clean up before ELinks exits.
This function should handle any clean-up tasks that need to be
performed before ELinks exits. Its return value is ignored.
"""
pass
# The rest of this file demonstrates some of the functionality available
# within hooks.py through the embedded Python interpreter's elinks module.
# Full documentation for the elinks module (as well as the hooks module)
# can be found in doc/python.txt in the ELinks source distribution.
# This class shows how to use elinks.input_box() and elinks.open(). It
# creates a dialog box to prompt the user for a URL, and provides a callback
# function that opens the URL in a new tab.
#
class goto_url_in_new_tab:
"""Prompter that opens a given URL in a new tab."""
def __init__(self):
"""Prompt for a URL."""
self.current_location = elinks.current_url()
elinks.input_box("Enter URL", self._callback, title="Go to URL")
def _callback(self, url):
"""Open the given URL in a new tab."""
if 'goto_url_hook' in globals():
# Mimic the standard "Go to URL" dialog by calling goto_url_hook().
url = goto_url_hook(url, self.current_location) or url
if url:
elinks.open(url, new_tab=True)
# The elinks.bind_key() function can be used to create a keystroke binding
# that will instantiate the class.
#
elinks.bind_key("Ctrl-g", goto_url_in_new_tab)
# This class can be used to enter arbitrary Python expressions for the embedded
# interpreter to evaluate. (Note that it can only evalute Python expressions,
# not execute arbitrary Python code.) The callback function will use
# elinks.info_box() to display the results.
#
class simple_console:
"""Simple console for passing expressions to the Python interpreter."""
def __init__(self):
"""Prompt for a Python expression."""
elinks.input_box("Enter Python expression", self._callback, "Console")
def _callback(self, input):
"""Evalute input and display the result (unless it's None)."""
if input is None:
# The user canceled.
return
result = eval(input)
if result is not None:
elinks.info_box(result, "Result")
elinks.bind_key("F5", simple_console)
# If you edit ~/.elinks/hooks.py while the browser is running, you can use
# this function to make your changes take effect immediately (without having
# to restart ELinks).
#
def reload_hooks():
"""Reload the ELinks Python hooks."""
import hooks
reload(hooks)
elinks.bind_key("F6", reload_hooks)
# This example demonstrates how to create a menu by providing a sequence of
# (string, function) tuples specifying each menu item.
#
def menu():
"""Let the user choose from a menu of Python functions."""
items = (
("~Go to URL in new tab", goto_url_in_new_tab),
("Simple Python ~console", simple_console),
("~Reload Python hooks", reload_hooks),
("Read my favorite RSS/ATOM ~feeds", feedreader),
)
elinks.menu(items)
elinks.bind_key("F4", menu)
# Finally, a more elaborate demonstration: If you install the add-on Python
# module from http://feedparser.org/ this class can use it to parse a list
# of your favorite RSS/ATOM feeds, figure out which entries you haven't seen
# yet, open each of those entries in a new tab, and report statistics on what
# it fetched. The class is instantiated by pressing the "!" key.
#
# This class demonstrates the elinks.load() function, which can be used to
# load a document into the ELinks cache without displaying it to the user;
# the document's contents are passed to a Python callback function.
my_favorite_feeds = (
"http://rss.gmane.org/messages/excerpts/gmane.comp.web.elinks.user",
# ... add any other RSS or ATOM feeds that interest you ...
# "http://elinks.cz/news.rss",
# "http://www.python.org/channews.rdf",
)
class feedreader:
"""RSS/ATOM feed reader."""
def __init__(self, feeds=my_favorite_feeds):
"""Constructor."""
self._results = {}
self._feeds = feeds
for feed in feeds:
elinks.load(feed, self._callback)
def _callback(self, header, body):
"""Read a feed, identify unseen entries, and open them in new tabs."""
import anydbm
import feedparser # you need to get this module from feedparser.org
import os
if not body:
return
seen = anydbm.open(os.path.join(elinks.home, "rss.seen"), "c")
feed = feedparser.parse(body)
new = 0
errors = 0
for entry in feed.entries:
try:
if not seen.has_key(entry.link):
elinks.open(entry.link, new_tab=True)
seen[entry.link] = ""
new += 1
except:
errors += 1
seen.close()
self._tally(feed.channel.title, new, errors)
def _tally(self, title, new, errors):
"""Record and report feed statistics."""
self._results[title] = (new, errors)
if len(self._results) == len(self._feeds):
feeds = self._results.keys()
feeds.sort()
width = max([len(title) for title in feeds])
fmt = "%*s new entries: %2d errors: %2d\n"
summary = ""
for title in feeds:
new, errors = self._results[title]
summary += fmt % (-width, title, new, errors)
elinks.info_box(summary, "Feed Statistics")
elinks.bind_key("!", feedreader)

View File

@ -3,6 +3,15 @@ include $(top_builddir)/Makefile.config
INCLUDES += $(PYTHON_CFLAGS)
OBJS = python.o hooks.o core.o
OBJS = \
core.o \
dialogs.o \
document.o \
hooks.o \
keybinding.o \
load.o \
menu.o \
open.o \
python.o
include $(top_srcdir)/Makefile.lib

View File

@ -5,37 +5,42 @@
#endif
#include <Python.h>
#include <osdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include "elinks.h"
#include "config/home.h"
#include "main/module.h"
#include "scripting/scripting.h"
#include "scripting/python/core.h"
#include "scripting/python/dialogs.h"
#include "scripting/python/document.h"
#include "scripting/python/keybinding.h"
#include "scripting/python/load.h"
#include "scripting/python/menu.h"
#include "scripting/python/open.h"
#include "scripting/python/python.h"
#include "scripting/scripting.h"
#include "session/session.h"
#include "util/env.h"
#include "util/file.h"
#include "util/string.h"
struct session *python_ses = NULL;
PyObject *pDict = NULL, *pModule = NULL;
PyObject *python_elinks_err = NULL;
PyObject *python_hooks = NULL;
/* Error reporting. */
void
alert_python_error(struct session *ses)
alert_python_error(void)
{
unsigned char *msg = "(no traceback available)";
PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
PyObject *tb_module = NULL;
PyObject *tb_dict;
PyObject *format_function;
PyObject *msg_list = NULL;
PyObject *empty_string = NULL;
PyObject *join_method = NULL;
PyObject *msg_string = NULL;
unsigned char *temp;
@ -46,17 +51,15 @@ alert_python_error(struct session *ses)
*/
PyErr_Fetch(&err_type, &err_value, &err_traceback);
PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
if (!err_traceback) goto end;
if (!err_type) goto end;
tb_module = PyImport_ImportModule("traceback");
if (!tb_module) goto end;
tb_dict = PyModule_GetDict(tb_module);
format_function = PyDict_GetItemString(tb_dict, "format_exception");
if (!format_function || !PyCallable_Check(format_function)) goto end;
msg_list = PyObject_CallFunction(format_function, "OOO",
err_type, err_value, err_traceback);
msg_list = PyObject_CallMethod(tb_module, "format_exception", "OOO",
err_type,
err_value ? err_value : Py_None,
err_traceback ? err_traceback : Py_None);
if (!msg_list) goto end;
/*
@ -67,17 +70,14 @@ alert_python_error(struct session *ses)
empty_string = PyString_FromString("");
if (!empty_string) goto end;
join_method = PyObject_GetAttrString(empty_string, "join");
if (!join_method || !PyCallable_Check(join_method)) goto end;
msg_string = PyObject_CallFunction(join_method, "O", msg_list);
msg_string = PyObject_CallMethod(empty_string, "join", "O", msg_list);
if (!msg_string) goto end;
temp = (unsigned char *)PyString_AsString(msg_string);
temp = (unsigned char *) PyString_AsString(msg_string);
if (temp) msg = temp;
end:
report_scripting_error(&python_scripting_module, ses, msg);
report_scripting_error(&python_scripting_module, python_ses, msg);
Py_XDECREF(err_type);
Py_XDECREF(err_value);
@ -85,31 +85,69 @@ end:
Py_XDECREF(tb_module);
Py_XDECREF(msg_list);
Py_XDECREF(empty_string);
Py_XDECREF(join_method);
Py_XDECREF(msg_string);
/* In case another error occurred while reporting the original error: */
PyErr_Clear();
}
void
cleanup_python(struct module *module)
/* Prepend ELinks directories to Python's search path. */
static int
set_python_search_path(void)
{
if (Py_IsInitialized()) {
Py_XDECREF(pDict);
Py_XDECREF(pModule);
Py_Finalize();
}
struct string new_python_path, *okay;
unsigned char *old_python_path;
int result = -1;
if (!init_string(&new_python_path)) return result;
old_python_path = (unsigned char *) getenv("PYTHONPATH");
if (old_python_path)
okay = add_format_to_string(&new_python_path, "%s%c%s%c%s",
elinks_home, DELIM, CONFDIR,
DELIM, old_python_path);
else
okay = add_format_to_string(&new_python_path, "%s%c%s",
elinks_home, DELIM, CONFDIR);
if (okay) result = env_set("PYTHONPATH", new_python_path.source, -1);
done_string(&new_python_path);
return result;
}
/* Module-level documentation for the Python interpreter's elinks module. */
static char module_doc[] =
PYTHON_DOCSTRING("Interface to the ELinks web browser.\n\
\n\
Functions:\n\
\n\
bind_key() -- Bind a keystroke to a callable object.\n\
current_document() -- Return the body of the document being viewed.\n\
current_header() -- Return the header of the document being viewed.\n\
current_link_url() -- Return the URL of the currently selected link.\n\
current_title() -- Return the title of the document being viewed.\n\
current_url() -- Return the URL of the document being viewed.\n\
info_box() -- Display information to the user.\n\
input_box() -- Prompt for user input.\n\
load() -- Load a document into the ELinks cache.\n\
menu() -- Display a menu.\n\
open() -- View a document.\n\
\n\
Exception classes:\n\
\n\
error -- Errors internal to ELinks.\n\
\n\
Other public objects:\n\
\n\
home -- A string containing the pathname of the ~/.elinks directory.\n");
void
init_python(struct module *module)
{
unsigned char *python_path = straconcat(elinks_home, ":", CONFDIR, NULL);
PyObject *elinks_module, *module_dict, *module_name;
if (!python_path) return;
env_set("PYTHONPATH", python_path, -1);
mem_free(python_path);
if (set_python_search_path() != 0) return;
/* Treat warnings as errors so they can be caught and handled;
* otherwise they would be printed to stderr.
@ -123,12 +161,66 @@ init_python(struct module *module)
PySys_AddWarnOption("error");
Py_Initialize();
pModule = PyImport_ImportModule("hooks");
if (pModule) {
pDict = PyModule_GetDict(pModule);
Py_INCREF(pDict);
} else {
alert_python_error(NULL);
elinks_module = Py_InitModule3("elinks", NULL, module_doc);
if (!elinks_module) goto python_error;
if (PyModule_AddStringConstant(elinks_module, "home", elinks_home) != 0)
goto python_error;
python_elinks_err = PyErr_NewException("elinks.error", NULL, NULL);
if (!python_elinks_err) goto python_error;
if (PyModule_AddObject(elinks_module, "error", python_elinks_err) != 0)
goto python_error;
module_dict = PyModule_GetDict(elinks_module);
if (!module_dict) goto python_error;
module_name = PyString_FromString("elinks");
if (!module_name) goto python_error;
if (python_init_dialogs_interface(module_dict, module_name) != 0
|| python_init_document_interface(module_dict, module_name) != 0
|| python_init_keybinding_interface(module_dict, module_name) != 0
|| python_init_load_interface(module_dict, module_name) != 0
|| python_init_menu_interface(module_dict, module_name) != 0
|| python_init_open_interface(module_dict, module_name) != 0)
goto python_error;
python_hooks = PyImport_ImportModule("hooks");
if (!python_hooks) goto python_error;
return;
python_error:
alert_python_error();
}
void
cleanup_python(struct module *module)
{
if (Py_IsInitialized()) {
python_done_keybinding_interface();
Py_XDECREF(python_hooks);
Py_Finalize();
}
}
/* Add methods to a Python extension module. */
int
add_python_methods(PyObject *dict, PyObject *name, PyMethodDef *methods)
{
PyMethodDef *method;
for (method = methods; method && method->ml_name; method++) {
PyObject *function = PyCFunction_NewEx(method, NULL, name);
int result;
if (!function) return -1;
result = PyDict_SetItemString(dict, method->ml_name, function);
Py_DECREF(function);
if (result != 0) return -1;
}
return 0;
}

View File

@ -2,11 +2,26 @@
#ifndef EL__SCRIPTING_PYTHON_CORE_H
#define EL__SCRIPTING_PYTHON_CORE_H
struct module;
struct session;
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
struct module;
extern struct session *python_ses;
extern PyObject *python_elinks_err;
void alert_python_error(void);
void alert_python_error(struct session *ses);
void init_python(struct module *module);
void cleanup_python(struct module *module);
int add_python_methods(PyObject *dict, PyObject *name, PyMethodDef *methods);
#ifndef CONFIG_SMALL
#define PYTHON_DOCSTRING(str) str
#else
#define PYTHON_DOCSTRING(str) ""
#endif
#endif

View File

@ -4,6 +4,10 @@
#include "config.h"
#endif
#include <Python.h>
#include <stdarg.h>
#include <string.h>
#include "elinks.h"
@ -11,133 +15,89 @@
#include "main/event.h"
#include "protocol/uri.h"
#include "scripting/python/core.h"
#include "scripting/python/hooks.h"
#include "session/location.h"
#include "session/session.h"
#include "util/memory.h"
#include "util/string.h"
#undef _POSIX_C_SOURCE
#include <Python.h>
extern PyObject *python_hooks;
/* The events that will trigger the functions below and what they are expected
* to do is explained in doc/events.txt */
/*
* 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.)
*/
extern PyObject *pDict;
extern PyObject *pModule;
static void
do_script_hook_goto_url(struct session *ses, unsigned char **url)
static PyObject *
replace_with_python_string(unsigned char **dest, PyObject *object)
{
PyObject *pFunc = PyDict_GetItemString(pDict, "goto_url_hook");
unsigned char *str;
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pValue;
unsigned char *current_url;
if (object == Py_None) return object;
if (!ses || !have_location(ses)) {
current_url = NULL;
} else {
str = (unsigned char *) PyString_AsString(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)
{
unsigned char **url = va_arg(ap, unsigned char **);
struct session *ses = va_arg(ap, struct session *);
char *method = 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;
/*
* Historical note: The only reason the goto and follow hooks are
* treated differently is to maintain backwards compatibility for
* people who already have a goto_url_hook() function in hooks.py
* that expects a second argument. If we were starting over from
* scratch, we could treat the goto and follow hooks identically and
* simply pass @url as the sole argument in both cases; the Python
* code for the goto hook no longer needs its @current_url argument
* since it could instead determine the current URL by calling the
* Python interpreter's elinks.current_url() function.
*/
if (!strcmp(method, "goto_url_hook")) {
unsigned char *current_url = NULL;
if (python_ses && have_location(python_ses))
current_url = struri(cur_loc(ses)->vs.uri);
}
pValue = PyObject_CallFunction(pFunc, "ss", *url, current_url);
if (pValue) {
if (pValue != Py_None) {
const unsigned char *str;
unsigned char *new_url;
str = PyString_AsString(pValue);
if (str) {
new_url = stracpy((unsigned char *)str);
if (new_url) mem_free_set(url, new_url);
}
}
Py_DECREF(pValue);
} else {
alert_python_error(ses);
}
result = PyObject_CallMethod(python_hooks, method, "ss", *url,
current_url);
} else {
result = PyObject_CallMethod(python_hooks, method, "s", *url);
}
}
static enum evhook_status
script_hook_goto_url(va_list ap, void *data)
{
unsigned char **url = va_arg(ap, unsigned char **);
struct session *ses = va_arg(ap, struct session *);
if (!result || !replace_with_python_string(url, result))
alert_python_error();
if (pDict && *url)
do_script_hook_goto_url(ses, url);
Py_XDECREF(result);
python_ses = saved_python_ses;
return EVENT_HOOK_STATUS_NEXT;
}
static void
do_script_hook_follow_url(struct session *ses, unsigned char **url)
{
PyObject *pFunc = PyDict_GetItemString(pDict, "follow_url_hook");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pValue = PyObject_CallFunction(pFunc, "s", *url);
if (pValue) {
if (pValue != Py_None) {
const unsigned char *str;
unsigned char *new_url;
str = PyString_AsString(pValue);
if (str) {
new_url = stracpy((unsigned char *)str);
if (new_url) mem_free_set(url, new_url);
}
}
Py_DECREF(pValue);
} else {
alert_python_error(ses);
}
}
}
static enum evhook_status
script_hook_follow_url(va_list ap, void *data)
{
unsigned char **url = va_arg(ap, unsigned char **);
struct session *ses = va_arg(ap, struct session *);
if (pDict && *url)
do_script_hook_follow_url(ses, url);
return EVENT_HOOK_STATUS_NEXT;
}
static void
do_script_hook_pre_format_html(struct session *ses, unsigned char *url,
struct cache_entry *cached,
struct fragment *fragment)
{
PyObject *pFunc = PyDict_GetItemString(pDict, "pre_format_html_hook");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pValue = PyObject_CallFunction(pFunc, "ss#", url,
fragment->data,
fragment->length);
if (pValue) {
if (pValue != Py_None) {
const unsigned char *str;
int len;
str = PyString_AsString(pValue);
if (str) {
len = PyString_Size(pValue);
add_fragment(cached, 0, str, len);
normalize_cache_entry(cached, len);
}
}
Py_DECREF(pValue);
} else {
alert_python_error(ses);
}
}
}
/* Call a Python hook for a pre-format-html event. */
static enum evhook_status
script_hook_pre_format_html(va_list ap, void *data)
@ -146,78 +106,94 @@ script_hook_pre_format_html(va_list ap, void *data)
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
struct fragment *fragment = get_cache_fragment(cached);
unsigned char *url = struri(cached->uri);
char *method = "pre_format_html_hook";
struct session *saved_python_ses = python_ses;
PyObject *result;
int success = 0;
if (pDict && ses && url && cached->length && *fragment->data)
do_script_hook_pre_format_html(ses, url, cached, fragment);
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;
result = PyObject_CallMethod(python_hooks, method, "ss#", url,
fragment->data, fragment->length);
if (!result) goto error;
if (result != Py_None) {
unsigned char *str;
int len;
if (PyString_AsStringAndSize(result, (char **) &str, &len) != 0)
goto error;
(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;
}
static inline void
do_script_hook_get_proxy(unsigned char **new_proxy_url, unsigned char *url)
{
PyObject *pFunc = PyDict_GetItemString(pDict, "proxy_for_hook");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pValue = PyObject_CallFunction(pFunc, "s", url);
if (pValue) {
if (pValue != Py_None) {
const unsigned char *str;
unsigned char *new_url;
str = PyString_AsString(pValue);
if (str) {
new_url = stracpy((unsigned char *)str);
if (new_url) mem_free_set(new_proxy_url,
new_url);
}
}
Py_DECREF(pValue);
} else {
alert_python_error(NULL);
}
}
}
/* Call a Python hook for a get-proxy event. */
static enum evhook_status
script_hook_get_proxy(va_list ap, void *data)
{
unsigned char **new_proxy_url = va_arg(ap, unsigned char **);
unsigned char **proxy = va_arg(ap, unsigned char **);
unsigned char *url = va_arg(ap, unsigned char *);
char *method = "proxy_for_hook";
PyObject *result;
if (pDict && new_proxy_url && url)
do_script_hook_get_proxy(new_proxy_url, url);
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;
}
static void
do_script_hook_quit(void)
{
PyObject *pFunc = PyDict_GetItemString(pDict, "quit_hook");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pValue = PyObject_CallFunction(pFunc, NULL);
if (pValue) {
Py_DECREF(pValue);
} else {
alert_python_error(NULL);
}
}
}
/* Call a Python hook for a quit event. */
static enum evhook_status
script_hook_quit(va_list ap, void *data)
{
if (pDict) do_script_hook_quit();
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_goto_url, NULL },
{ "follow-url", 0, script_hook_follow_url, NULL },
{ "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 },

View File

@ -4,11 +4,12 @@
#include "config.h"
#endif
#include "scripting/python/core.h"
#include <Python.h>
#include "elinks.h"
#include "main/module.h"
#include "scripting/python/core.h"
#include "scripting/python/hooks.h"