Additional functionality for Python backend.
[elinks.git] / src / scripting / python / hooks.c
blob23a07f07d88e58e803a246eac6539ba1c82a76b1
1 /* Python scripting hooks */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <Python.h>
9 #include <stdarg.h>
10 #include <string.h>
12 #include "elinks.h"
14 #include "cache/cache.h"
15 #include "main/event.h"
16 #include "protocol/uri.h"
17 #include "scripting/python/core.h"
18 #include "session/location.h"
19 #include "session/session.h"
20 #include "util/memory.h"
21 #include "util/string.h"
23 extern PyObject *python_hooks;
26 * A utility function for script_hook_url() and script_hook_get_proxy():
27 * Free a char * and replace it with the contents of a Python string.
28 * (Py_None is ignored.)
31 static PyObject *
32 replace_with_python_string(unsigned char **dest, PyObject *object)
34 unsigned char *str;
36 if (object == Py_None) return object;
38 str = (unsigned char *) PyString_AsString(object);
39 if (!str) return NULL;
41 str = stracpy(str);
42 if (!str) return PyErr_NoMemory();
44 mem_free_set(dest, str);
45 return object;
48 /* Call a Python hook for a goto-url or follow-url event. */
50 static enum evhook_status
51 script_hook_url(va_list ap, void *data)
53 unsigned char **url = va_arg(ap, unsigned char **);
54 struct session *ses = va_arg(ap, struct session *);
55 char *method = data;
56 struct session *saved_python_ses = python_ses;
57 PyObject *result;
59 evhook_use_params(url && ses);
61 if (!python_hooks || !url || !*url
62 || !PyObject_HasAttrString(python_hooks, method))
63 return EVENT_HOOK_STATUS_NEXT;
65 python_ses = ses;
68 * Historical note: The only reason the goto and follow hooks are
69 * treated differently is to maintain backwards compatibility for
70 * people who already have a goto_url_hook() function in hooks.py
71 * that expects a second argument. If we were starting over from
72 * scratch, we could treat the goto and follow hooks identically and
73 * simply pass @url as the sole argument in both cases; the Python
74 * code for the goto hook no longer needs its @current_url argument
75 * since it could instead determine the current URL by calling the
76 * Python interpreter's elinks.current_url() function.
78 if (!strcmp(method, "goto_url_hook")) {
79 unsigned char *current_url = NULL;
81 if (python_ses && have_location(python_ses))
82 current_url = struri(cur_loc(ses)->vs.uri);
84 result = PyObject_CallMethod(python_hooks, method, "ss", *url,
85 current_url);
86 } else {
87 result = PyObject_CallMethod(python_hooks, method, "s", *url);
90 if (!result || !replace_with_python_string(url, result))
91 alert_python_error();
93 Py_XDECREF(result);
95 python_ses = saved_python_ses;
97 return EVENT_HOOK_STATUS_NEXT;
100 /* Call a Python hook for a pre-format-html event. */
102 static enum evhook_status
103 script_hook_pre_format_html(va_list ap, void *data)
105 struct session *ses = va_arg(ap, struct session *);
106 struct cache_entry *cached = va_arg(ap, struct cache_entry *);
107 struct fragment *fragment = get_cache_fragment(cached);
108 unsigned char *url = struri(cached->uri);
109 char *method = "pre_format_html_hook";
110 struct session *saved_python_ses = python_ses;
111 PyObject *result;
112 int success = 0;
114 evhook_use_params(ses && cached);
116 if (!python_hooks || !cached->length || !*fragment->data
117 || !PyObject_HasAttrString(python_hooks, method))
118 return EVENT_HOOK_STATUS_NEXT;
120 python_ses = ses;
122 result = PyObject_CallMethod(python_hooks, method, "ss#", url,
123 fragment->data, fragment->length);
124 if (!result) goto error;
126 if (result != Py_None) {
127 unsigned char *str;
128 int len;
130 if (PyString_AsStringAndSize(result, (char **) &str, &len) != 0)
131 goto error;
133 (void) add_fragment(cached, 0, str, len);
134 normalize_cache_entry(cached, len);
137 success = 1;
139 error:
140 if (!success) alert_python_error();
142 Py_XDECREF(result);
144 python_ses = saved_python_ses;
146 return EVENT_HOOK_STATUS_NEXT;
149 /* Call a Python hook for a get-proxy event. */
151 static enum evhook_status
152 script_hook_get_proxy(va_list ap, void *data)
154 unsigned char **proxy = va_arg(ap, unsigned char **);
155 unsigned char *url = va_arg(ap, unsigned char *);
156 char *method = "proxy_for_hook";
157 PyObject *result;
159 evhook_use_params(proxy && url);
161 if (!python_hooks || !proxy || !url
162 || !PyObject_HasAttrString(python_hooks, method))
163 return EVENT_HOOK_STATUS_NEXT;
165 result = PyObject_CallMethod(python_hooks, method, "s", url);
167 if (!result || !replace_with_python_string(proxy, result))
168 alert_python_error();
170 Py_XDECREF(result);
172 return EVENT_HOOK_STATUS_NEXT;
175 /* Call a Python hook for a quit event. */
177 static enum evhook_status
178 script_hook_quit(va_list ap, void *data)
180 char *method = "quit_hook";
181 PyObject *result;
183 if (!python_hooks || !PyObject_HasAttrString(python_hooks, method))
184 return EVENT_HOOK_STATUS_NEXT;
186 result = PyObject_CallMethod(python_hooks, method, NULL);
187 if (!result) alert_python_error();
189 Py_XDECREF(result);
191 return EVENT_HOOK_STATUS_NEXT;
194 struct event_hook_info python_scripting_hooks[] = {
195 { "goto-url", 0, script_hook_url, "goto_url_hook" },
196 { "follow-url", 0, script_hook_url, "follow_url_hook" },
197 { "pre-format-html", 0, script_hook_pre_format_html, NULL },
198 { "get-proxy", 0, script_hook_get_proxy, NULL },
199 { "quit", 0, script_hook_quit, NULL },
200 NULL_EVENT_HOOK_INFO,