1 /* Ruby scripting hooks */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
7 #include <ruby.h>
8
9 #include "elinks.h"
10
11 #include "cache/cache.h"
12 #include "main/event.h"
13 #include "protocol/uri.h"
14 #include "scripting/ruby/core.h"
15 #include "scripting/ruby/hooks.h"
16 #include "session/location.h"
17 #include "session/session.h"
18 #include "util/string.h"
19
20
21 /* The events that will trigger the functions below and what they are expected
22 * to do is explained in doc/events.txt */
23
24 /* We need to catch and handle errors because, otherwise, Ruby will kill us. */
25
26 struct erb_protect_info {
27 unsigned char *name;
28 int argc;
29 VALUE *args;
30 };
31
32 static VALUE
do_erb_protected_method_call(VALUE data)33 do_erb_protected_method_call(VALUE data)
34 {
35 struct erb_protect_info *info = (struct erb_protect_info *) data;
36 ID method_id;
37
38 assert(info);
39
40 method_id = rb_intern(info->name);
41
42 return rb_funcall3(erb_module, method_id, info->argc, info->args);
43 }
44
45 static VALUE
erb_protected_method_call(unsigned char * name,int argc,VALUE * args,int * error)46 erb_protected_method_call(unsigned char *name, int argc, VALUE *args, int *error)
47 {
48 struct erb_protect_info info = { name, argc, args };
49
50 return rb_protect(do_erb_protected_method_call, (VALUE) &info, error);
51 }
52
53
54
55 static enum evhook_status
script_hook_goto_url(va_list ap,void * data)56 script_hook_goto_url(va_list ap, void *data)
57 {
58 unsigned char **url = va_arg(ap, unsigned char **);
59 struct session *ses = va_arg(ap, struct session *);
60 int error;
61 VALUE args[2];
62 VALUE result;
63
64 if (*url == NULL)
65 return EVENT_HOOK_STATUS_NEXT;
66
67 args[0] = rb_str_new2(*url);
68
69 if (!ses || !have_location(ses)) {
70 args[1] = Qnil;
71 } else {
72 args[1] = rb_str_new2(struri(cur_loc(ses)->vs.uri));
73 }
74
75 result = erb_protected_method_call("goto_url_hook", 2, args, &error);
76 if (error) {
77 erb_report_error(ses, error);
78 return EVENT_HOOK_STATUS_NEXT;
79 }
80
81 switch (rb_type(result)) {
82 case T_STRING:
83 {
84 unsigned char *new_url;
85
86 new_url = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
87 if (new_url) {
88 mem_free_set(url, new_url);
89 }
90 break;
91 }
92 case T_NIL:
93 break;
94
95 default:
96 alert_ruby_error(ses, "goto_url_hook must return a string or nil");
97 }
98
99 return EVENT_HOOK_STATUS_NEXT;
100 }
101
102 static enum evhook_status
script_hook_follow_url(va_list ap,void * data)103 script_hook_follow_url(va_list ap, void *data)
104 {
105 unsigned char **url = va_arg(ap, unsigned char **);
106 struct session *ses = va_arg(ap, struct session *);
107 int error;
108 VALUE args[1];
109 VALUE result;
110
111 evhook_use_params(url && ses);
112
113 if (*url == NULL)
114 return EVENT_HOOK_STATUS_NEXT;
115
116 args[0] = rb_str_new2(*url);
117
118 result = erb_protected_method_call("follow_url_hook", 1, args, &error);
119 if (error) {
120 erb_report_error(ses, error);
121 return EVENT_HOOK_STATUS_NEXT;
122 }
123
124 switch (rb_type(result)) {
125 case T_STRING:
126 {
127 unsigned char *new_url;
128
129 new_url = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
130 if (new_url) {
131 mem_free_set(url, new_url);
132 }
133 break;
134 }
135 case T_NIL:
136 break;
137
138 default:
139 alert_ruby_error(ses, "follow_url_hook must return a string or nil");
140 }
141
142 return EVENT_HOOK_STATUS_NEXT;
143 }
144
145 static enum evhook_status
script_hook_pre_format_html(va_list ap,void * data)146 script_hook_pre_format_html(va_list ap, void *data)
147 {
148 struct session *ses = va_arg(ap, struct session *);
149 struct cache_entry *cached = va_arg(ap, struct cache_entry *);
150 struct fragment *fragment = get_cache_fragment(cached);
151 unsigned char *url = struri(cached->uri);
152 int error;
153 VALUE args[2];
154 VALUE result;
155
156 evhook_use_params(ses && cached);
157
158 if (!cached->length || !*fragment->data)
159 return EVENT_HOOK_STATUS_NEXT;
160
161 args[0] = rb_str_new2(url);
162 /* FIXME: Use html_len */
163 args[1] = rb_str_new2(fragment->data);
164
165 result = erb_protected_method_call("pre_format_html_hook", 2, args, &error);
166 if (error) {
167 erb_report_error(ses, error);
168 return EVENT_HOOK_STATUS_NEXT;
169 }
170
171 switch (rb_type(result)) {
172 case T_STRING:
173 {
174 int len = RSTRING(result)->len;
175
176 add_fragment(cached, 0, RSTRING(result)->ptr, len);
177 normalize_cache_entry(cached, len);
178
179 break;
180 }
181 case T_NIL:
182 break;
183
184 default:
185 alert_ruby_error(ses, "pre_format_html_hook must return a string or nil");
186 }
187
188 return EVENT_HOOK_STATUS_NEXT;
189 }
190
191 /* The Ruby function can return:
192 * - "PROXY:PORT" to use the specified proxy
193 * - "" to not use any proxy
194 * - nil to use the default proxies */
195 static enum evhook_status
script_hook_get_proxy(va_list ap,void * data)196 script_hook_get_proxy(va_list ap, void *data)
197 {
198 unsigned char **new_proxy_url = va_arg(ap, unsigned char **);
199 unsigned char *url = va_arg(ap, unsigned char *);
200 int error;
201 VALUE args[1];
202 VALUE result;
203
204 if (!new_proxy_url || !url)
205 return EVENT_HOOK_STATUS_NEXT;
206
207 args[0] = rb_str_new2(url);
208
209 result = erb_protected_method_call("proxy_hook", 1, args, &error);
210 if (error) {
211 erb_report_error(NULL, error);
212 return EVENT_HOOK_STATUS_NEXT;
213 }
214
215 switch (rb_type(result)) {
216 case T_STRING:
217 {
218 unsigned char *proxy;
219
220 proxy = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
221 if (proxy) {
222 mem_free_set(new_proxy_url, proxy);
223 }
224 break;
225 }
226 case T_NIL:
227 break;
228
229 default:
230 alert_ruby_error(NULL, "proxy_hook must return a string or nil");
231 }
232
233 return EVENT_HOOK_STATUS_NEXT;
234 }
235
236 static enum evhook_status
script_hook_quit(va_list ap,void * data)237 script_hook_quit(va_list ap, void *data)
238 {
239 VALUE args[1];
240 int error;
241
242 erb_protected_method_call("quit_hook", 0, args, &error);
243 if (error)
244 erb_report_error(NULL, error);
245
246 return EVENT_HOOK_STATUS_NEXT;
247 }
248
249 struct event_hook_info ruby_scripting_hooks[] = {
250 { "goto-url", 0, script_hook_goto_url, NULL },
251 { "follow-url", 0, script_hook_follow_url, NULL },
252 { "pre-format-html", 0, script_hook_pre_format_html, NULL },
253 { "get-proxy", 0, script_hook_get_proxy, NULL },
254 { "quit", 0, script_hook_quit, NULL },
255
256 NULL_EVENT_HOOK_INFO,
257 };
258