zendesk-rinku 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright (c) 2011, Vicent Marti
3
+ *
4
+ * Permission to use, copy, modify, and distribute this software for any
5
+ * purpose with or without fee is hereby granted, provided that the above
6
+ * copyright notice and this permission notice appear in all copies.
7
+ *
8
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ */
16
+
17
+ #ifndef UPSKIRT_AUTOLINK_H
18
+ #define UPSKIRT_AUTOLINK_H
19
+
20
+ #include "buffer.h"
21
+
22
+ #ifdef __cplusplus
23
+ extern "C" {
24
+ #endif
25
+
26
+ enum {
27
+ SD_AUTOLINK_SHORT_DOMAINS = (1 << 0),
28
+ };
29
+
30
+ int
31
+ sd_autolink_issafe(const uint8_t *link, size_t link_len);
32
+
33
+ size_t
34
+ sd_autolink__www(size_t *rewind_p, struct buf *link,
35
+ uint8_t *data, size_t offset, size_t size, unsigned int flags);
36
+
37
+ size_t
38
+ sd_autolink__email(size_t *rewind_p, struct buf *link,
39
+ uint8_t *data, size_t offset, size_t size, unsigned int flags);
40
+
41
+ size_t
42
+ sd_autolink__url(size_t *rewind_p, struct buf *link,
43
+ uint8_t *data, size_t offset, size_t size, unsigned int flags);
44
+
45
+ #ifdef __cplusplus
46
+ }
47
+ #endif
48
+
49
+ #endif
50
+
51
+ /* vim: set filetype=c: */
@@ -0,0 +1,225 @@
1
+ /*
2
+ * Copyright (c) 2008, Natacha Porté
3
+ * Copyright (c) 2011, Vicent Martí
4
+ *
5
+ * Permission to use, copy, modify, and distribute this software for any
6
+ * purpose with or without fee is hereby granted, provided that the above
7
+ * copyright notice and this permission notice appear in all copies.
8
+ *
9
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ */
17
+
18
+ #define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
19
+
20
+ #include "buffer.h"
21
+
22
+ #include <stdio.h>
23
+ #include <stdlib.h>
24
+ #include <string.h>
25
+ #include <assert.h>
26
+
27
+ /* MSVC compat */
28
+ #if defined(_MSC_VER)
29
+ # define _buf_vsnprintf _vsnprintf
30
+ #else
31
+ # define _buf_vsnprintf vsnprintf
32
+ #endif
33
+
34
+ int
35
+ bufprefix(const struct buf *buf, const char *prefix)
36
+ {
37
+ size_t i;
38
+ assert(buf && buf->unit);
39
+
40
+ for (i = 0; i < buf->size; ++i) {
41
+ if (prefix[i] == 0)
42
+ return 0;
43
+
44
+ if (buf->data[i] != prefix[i])
45
+ return buf->data[i] - prefix[i];
46
+ }
47
+
48
+ return 0;
49
+ }
50
+
51
+ /* bufgrow: increasing the allocated size to the given value */
52
+ int
53
+ bufgrow(struct buf *buf, size_t neosz)
54
+ {
55
+ size_t neoasz;
56
+ void *neodata;
57
+
58
+ assert(buf && buf->unit);
59
+
60
+ if (neosz > BUFFER_MAX_ALLOC_SIZE)
61
+ return BUF_ENOMEM;
62
+
63
+ if (buf->asize >= neosz)
64
+ return BUF_OK;
65
+
66
+ neoasz = buf->asize + buf->unit;
67
+ while (neoasz < neosz)
68
+ neoasz += buf->unit;
69
+
70
+ neodata = realloc(buf->data, neoasz);
71
+ if (!neodata)
72
+ return BUF_ENOMEM;
73
+
74
+ buf->data = neodata;
75
+ buf->asize = neoasz;
76
+ return BUF_OK;
77
+ }
78
+
79
+
80
+ /* bufnew: allocation of a new buffer */
81
+ struct buf *
82
+ bufnew(size_t unit)
83
+ {
84
+ struct buf *ret;
85
+ ret = malloc(sizeof (struct buf));
86
+
87
+ if (ret) {
88
+ ret->data = 0;
89
+ ret->size = ret->asize = 0;
90
+ ret->unit = unit;
91
+ }
92
+ return ret;
93
+ }
94
+
95
+ /* bufnullterm: NULL-termination of the string array */
96
+ const char *
97
+ bufcstr(struct buf *buf)
98
+ {
99
+ assert(buf && buf->unit);
100
+
101
+ if (buf->size < buf->asize && buf->data[buf->size] == 0)
102
+ return (char *)buf->data;
103
+
104
+ if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {
105
+ buf->data[buf->size] = 0;
106
+ return (char *)buf->data;
107
+ }
108
+
109
+ return NULL;
110
+ }
111
+
112
+ /* bufprintf: formatted printing to a buffer */
113
+ void
114
+ bufprintf(struct buf *buf, const char *fmt, ...)
115
+ {
116
+ va_list ap;
117
+ int n;
118
+
119
+ assert(buf && buf->unit);
120
+
121
+ if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0)
122
+ return;
123
+
124
+ va_start(ap, fmt);
125
+ n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
126
+ va_end(ap);
127
+
128
+ if (n < 0) {
129
+ #ifdef _MSC_VER
130
+ va_start(ap, fmt);
131
+ n = _vscprintf(fmt, ap);
132
+ va_end(ap);
133
+ #else
134
+ return;
135
+ #endif
136
+ }
137
+
138
+ if ((size_t)n >= buf->asize - buf->size) {
139
+ if (bufgrow(buf, buf->size + n + 1) < 0)
140
+ return;
141
+
142
+ va_start(ap, fmt);
143
+ n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
144
+ va_end(ap);
145
+ }
146
+
147
+ if (n < 0)
148
+ return;
149
+
150
+ buf->size += n;
151
+ }
152
+
153
+ /* bufput: appends raw data to a buffer */
154
+ void
155
+ bufput(struct buf *buf, const void *data, size_t len)
156
+ {
157
+ assert(buf && buf->unit);
158
+
159
+ if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)
160
+ return;
161
+
162
+ memcpy(buf->data + buf->size, data, len);
163
+ buf->size += len;
164
+ }
165
+
166
+ /* bufputs: appends a NUL-terminated string to a buffer */
167
+ void
168
+ bufputs(struct buf *buf, const char *str)
169
+ {
170
+ bufput(buf, str, strlen(str));
171
+ }
172
+
173
+
174
+ /* bufputc: appends a single uint8_t to a buffer */
175
+ void
176
+ bufputc(struct buf *buf, int c)
177
+ {
178
+ assert(buf && buf->unit);
179
+
180
+ if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)
181
+ return;
182
+
183
+ buf->data[buf->size] = c;
184
+ buf->size += 1;
185
+ }
186
+
187
+ /* bufrelease: decrease the reference count and free the buffer if needed */
188
+ void
189
+ bufrelease(struct buf *buf)
190
+ {
191
+ if (!buf)
192
+ return;
193
+
194
+ free(buf->data);
195
+ free(buf);
196
+ }
197
+
198
+
199
+ /* bufreset: frees internal data of the buffer */
200
+ void
201
+ bufreset(struct buf *buf)
202
+ {
203
+ if (!buf)
204
+ return;
205
+
206
+ free(buf->data);
207
+ buf->data = NULL;
208
+ buf->size = buf->asize = 0;
209
+ }
210
+
211
+ /* bufslurp: removes a given number of bytes from the head of the array */
212
+ void
213
+ bufslurp(struct buf *buf, size_t len)
214
+ {
215
+ assert(buf && buf->unit);
216
+
217
+ if (len >= buf->size) {
218
+ buf->size = 0;
219
+ return;
220
+ }
221
+
222
+ buf->size -= len;
223
+ memmove(buf->data, buf->data + len, buf->size);
224
+ }
225
+
@@ -0,0 +1,96 @@
1
+ /*
2
+ * Copyright (c) 2008, Natacha Porté
3
+ * Copyright (c) 2011, Vicent Martí
4
+ *
5
+ * Permission to use, copy, modify, and distribute this software for any
6
+ * purpose with or without fee is hereby granted, provided that the above
7
+ * copyright notice and this permission notice appear in all copies.
8
+ *
9
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ */
17
+
18
+ #ifndef BUFFER_H__
19
+ #define BUFFER_H__
20
+
21
+ #include <stddef.h>
22
+ #include <stdarg.h>
23
+ #include <stdint.h>
24
+
25
+ #ifdef __cplusplus
26
+ extern "C" {
27
+ #endif
28
+
29
+ #if defined(_MSC_VER)
30
+ #define __attribute__(x)
31
+ #define inline
32
+ #endif
33
+
34
+ typedef enum {
35
+ BUF_OK = 0,
36
+ BUF_ENOMEM = -1,
37
+ } buferror_t;
38
+
39
+ /* struct buf: character array buffer */
40
+ struct buf {
41
+ uint8_t *data; /* actual character data */
42
+ size_t size; /* size of the string */
43
+ size_t asize; /* allocated size (0 = volatile buffer) */
44
+ size_t unit; /* reallocation unit size (0 = read-only buffer) */
45
+ };
46
+
47
+ /* CONST_BUF: global buffer from a string litteral */
48
+ #define BUF_STATIC(string) \
49
+ { (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 }
50
+
51
+ /* VOLATILE_BUF: macro for creating a volatile buffer on the stack */
52
+ #define BUF_VOLATILE(strname) \
53
+ { (uint8_t *)strname, strlen(strname), 0, 0, 0 }
54
+
55
+ /* BUFPUTSL: optimized bufputs of a string litteral */
56
+ #define BUFPUTSL(output, literal) \
57
+ bufput(output, literal, sizeof literal - 1)
58
+
59
+ /* bufgrow: increasing the allocated size to the given value */
60
+ int bufgrow(struct buf *, size_t);
61
+
62
+ /* bufnew: allocation of a new buffer */
63
+ struct buf *bufnew(size_t) __attribute__ ((malloc));
64
+
65
+ /* bufnullterm: NUL-termination of the string array (making a C-string) */
66
+ const char *bufcstr(struct buf *);
67
+
68
+ /* bufprefix: compare the beginning of a buffer with a string */
69
+ int bufprefix(const struct buf *buf, const char *prefix);
70
+
71
+ /* bufput: appends raw data to a buffer */
72
+ void bufput(struct buf *, const void *, size_t);
73
+
74
+ /* bufputs: appends a NUL-terminated string to a buffer */
75
+ void bufputs(struct buf *, const char *);
76
+
77
+ /* bufputc: appends a single char to a buffer */
78
+ void bufputc(struct buf *, int);
79
+
80
+ /* bufrelease: decrease the reference count and free the buffer if needed */
81
+ void bufrelease(struct buf *);
82
+
83
+ /* bufreset: frees internal data of the buffer */
84
+ void bufreset(struct buf *);
85
+
86
+ /* bufslurp: removes a given number of bytes from the head of the array */
87
+ void bufslurp(struct buf *, size_t);
88
+
89
+ /* bufprintf: formatted printing to a buffer */
90
+ void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
91
+
92
+ #ifdef __cplusplus
93
+ }
94
+ #endif
95
+
96
+ #endif
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS += ' -fvisibility=hidden'
4
+
5
+ dir_config('rinku')
6
+ create_makefile('rinku')
data/ext/rinku/rinku.c ADDED
@@ -0,0 +1,468 @@
1
+ /*
2
+ * Copyright (c) 2011, Vicent Marti
3
+ *
4
+ * Permission to use, copy, modify, and distribute this software for any
5
+ * purpose with or without fee is hereby granted, provided that the above
6
+ * copyright notice and this permission notice appear in all copies.
7
+ *
8
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ */
16
+ #define RSTRING_NOT_MODIFIED
17
+
18
+ #include <stdio.h>
19
+ #include "ruby.h"
20
+
21
+ #define RUBY_EXPORT __attribute__ ((visibility ("default")))
22
+
23
+ #ifdef HAVE_RUBY_ENCODING_H
24
+ #include <ruby/encoding.h>
25
+ #else
26
+ #define rb_enc_copy(dst, src)
27
+ #endif
28
+
29
+ #include "autolink.h"
30
+ #include "buffer.h"
31
+
32
+ #include <string.h>
33
+ #include <stdlib.h>
34
+ #include <stdio.h>
35
+ #include <ctype.h>
36
+
37
+ static VALUE rb_mRinku;
38
+
39
+ typedef enum {
40
+ HTML_TAG_NONE = 0,
41
+ HTML_TAG_OPEN,
42
+ HTML_TAG_CLOSE,
43
+ } html_tag;
44
+
45
+ typedef enum {
46
+ AUTOLINK_URLS = (1 << 0),
47
+ AUTOLINK_EMAILS = (1 << 1),
48
+ AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS
49
+ } autolink_mode;
50
+
51
+ typedef size_t (*autolink_parse_cb)(
52
+ size_t *rewind, struct buf *, uint8_t *, size_t, size_t, unsigned int);
53
+
54
+ typedef enum {
55
+ AUTOLINK_ACTION_NONE = 0,
56
+ AUTOLINK_ACTION_WWW,
57
+ AUTOLINK_ACTION_EMAIL,
58
+ AUTOLINK_ACTION_URL,
59
+ AUTOLINK_ACTION_SKIP_TAG
60
+ } autolink_action;
61
+
62
+ static autolink_parse_cb g_callbacks[] = {
63
+ NULL,
64
+ sd_autolink__www, /* 1 */
65
+ sd_autolink__email,/* 2 */
66
+ sd_autolink__url, /* 3 */
67
+ };
68
+
69
+ static const char *g_hrefs[] = {
70
+ NULL,
71
+ "<a href=\"http://",
72
+ "<a href=\"mailto:",
73
+ "<a href=\"",
74
+ };
75
+
76
+ static void
77
+ autolink__print(struct buf *ob, const struct buf *link, void *payload)
78
+ {
79
+ bufput(ob, link->data, link->size);
80
+ }
81
+
82
+ /*
83
+ * Rinku assumes valid HTML encoding for all input, but there's still
84
+ * the case where a link can contain a double quote `"` that allows XSS.
85
+ *
86
+ * We need to properly escape the character we use for the `href` attribute
87
+ * declaration
88
+ */
89
+ static void print_link(struct buf *ob, const char *link, size_t size)
90
+ {
91
+ size_t i = 0, org;
92
+
93
+ while (i < size) {
94
+ org = i;
95
+
96
+ while (i < size && link[i] != '"')
97
+ i++;
98
+
99
+ if (i > org)
100
+ bufput(ob, link + org, i - org);
101
+
102
+ if (i >= size)
103
+ break;
104
+
105
+ BUFPUTSL(ob, "&quot;");
106
+ i++;
107
+ }
108
+ }
109
+
110
+ /* From sundown/html/html.c */
111
+ static int
112
+ html_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
113
+ {
114
+ size_t i;
115
+ int closed = 0;
116
+
117
+ if (tag_size < 3 || tag_data[0] != '<')
118
+ return HTML_TAG_NONE;
119
+
120
+ i = 1;
121
+
122
+ if (tag_data[i] == '/') {
123
+ closed = 1;
124
+ i++;
125
+ }
126
+
127
+ for (; i < tag_size; ++i, ++tagname) {
128
+ if (*tagname == 0)
129
+ break;
130
+
131
+ if (tag_data[i] != *tagname)
132
+ return HTML_TAG_NONE;
133
+ }
134
+
135
+ if (i == tag_size)
136
+ return HTML_TAG_NONE;
137
+
138
+ if (isspace(tag_data[i]) || tag_data[i] == '>')
139
+ return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
140
+
141
+ return HTML_TAG_NONE;
142
+ }
143
+
144
+ static size_t
145
+ autolink__skip_tag(
146
+ struct buf *ob,
147
+ const uint8_t *text,
148
+ size_t size,
149
+ const char **skip_tags)
150
+ {
151
+ size_t i = 0;
152
+
153
+ while (i < size && text[i] != '>')
154
+ i++;
155
+
156
+ while (*skip_tags != NULL) {
157
+ if (html_is_tag(text, size, *skip_tags) == HTML_TAG_OPEN)
158
+ break;
159
+
160
+ skip_tags++;
161
+ }
162
+
163
+ if (*skip_tags != NULL) {
164
+ for (;;) {
165
+ while (i < size && text[i] != '<')
166
+ i++;
167
+
168
+ if (i == size)
169
+ break;
170
+
171
+ if (html_is_tag(text + i, size - i, *skip_tags) == HTML_TAG_CLOSE)
172
+ break;
173
+
174
+ i++;
175
+ }
176
+
177
+ while (i < size && text[i] != '>')
178
+ i++;
179
+ }
180
+
181
+ // bufput(ob, text, i + 1);
182
+ return i;
183
+ }
184
+
185
+ int
186
+ rinku_autolink(
187
+ struct buf *ob,
188
+ const uint8_t *text,
189
+ size_t size,
190
+ autolink_mode mode,
191
+ unsigned int flags,
192
+ const char *link_attr,
193
+ const char **skip_tags,
194
+ void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload),
195
+ void *payload)
196
+ {
197
+ size_t i, end;
198
+ struct buf *link = bufnew(16);
199
+ char active_chars[256];
200
+ void (*link_url_cb)(struct buf *, const struct buf *, void *);
201
+ int link_count = 0;
202
+
203
+ if (!text || size == 0)
204
+ return 0;
205
+
206
+ memset(active_chars, 0x0, sizeof(active_chars));
207
+
208
+ active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG;
209
+
210
+ if (mode & AUTOLINK_EMAILS)
211
+ active_chars['@'] = AUTOLINK_ACTION_EMAIL;
212
+
213
+ if (mode & AUTOLINK_URLS) {
214
+ active_chars['w'] = AUTOLINK_ACTION_WWW;
215
+ active_chars['W'] = AUTOLINK_ACTION_WWW;
216
+ active_chars[':'] = AUTOLINK_ACTION_URL;
217
+ }
218
+
219
+ if (link_text_cb == NULL)
220
+ link_text_cb = &autolink__print;
221
+
222
+ if (link_attr != NULL) {
223
+ while (isspace(*link_attr))
224
+ link_attr++;
225
+ }
226
+
227
+ bufgrow(ob, size);
228
+
229
+ i = end = 0;
230
+
231
+ while (i < size) {
232
+ size_t rewind, link_end;
233
+ char action = 0;
234
+
235
+ while (end < size && (action = active_chars[text[end]]) == 0)
236
+ end++;
237
+
238
+ if (end == size) {
239
+ if (link_count > 0)
240
+ bufput(ob, text + i, end - i);
241
+ break;
242
+ }
243
+
244
+ if (action == AUTOLINK_ACTION_SKIP_TAG) {
245
+ end += autolink__skip_tag(ob,
246
+ text + end, size - end, skip_tags);
247
+
248
+ continue;
249
+ }
250
+
251
+ link->size = 0;
252
+ link_end = g_callbacks[(int)action](
253
+ &rewind, link, (uint8_t *)text + end, end, size - end, flags);
254
+
255
+ /* print the link */
256
+ if (link_end > 0) {
257
+ bufput(ob, text + i, end - i - rewind);
258
+
259
+ bufputs(ob, g_hrefs[(int)action]);
260
+ print_link(ob, link->data, link->size);
261
+
262
+ if (link_attr) {
263
+ BUFPUTSL(ob, "\" ");
264
+ bufputs(ob, link_attr);
265
+ bufputc(ob, '>');
266
+ } else {
267
+ BUFPUTSL(ob, "\">");
268
+ }
269
+
270
+ link_text_cb(ob, link, payload);
271
+ BUFPUTSL(ob, "</a>");
272
+
273
+ link_count++;
274
+ i = end + link_end;
275
+ end = i;
276
+ } else {
277
+ end = end + 1;
278
+ }
279
+ }
280
+
281
+ bufrelease(link);
282
+ return link_count;
283
+ }
284
+
285
+
286
+ /**
287
+ * Ruby code
288
+ */
289
+ static void
290
+ autolink_callback(struct buf *link_text, const struct buf *link, void *block)
291
+ {
292
+ VALUE rb_link, rb_link_text;
293
+ rb_link = rb_str_new(link->data, link->size);
294
+ rb_link_text = rb_funcall((VALUE)block, rb_intern("call"), 1, rb_link);
295
+ Check_Type(rb_link_text, T_STRING);
296
+ bufput(link_text, RSTRING_PTR(rb_link_text), RSTRING_LEN(rb_link_text));
297
+ }
298
+
299
+ const char **rinku_load_tags(VALUE rb_skip)
300
+ {
301
+ const char **skip_tags;
302
+ size_t i, count;
303
+
304
+ Check_Type(rb_skip, T_ARRAY);
305
+
306
+ count = RARRAY_LEN(rb_skip);
307
+ skip_tags = xmalloc(sizeof(void *) * (count + 1));
308
+
309
+ for (i = 0; i < count; ++i) {
310
+ VALUE tag = rb_ary_entry(rb_skip, i);
311
+ Check_Type(tag, T_STRING);
312
+ skip_tags[i] = StringValueCStr(tag);
313
+ }
314
+
315
+ skip_tags[count] = NULL;
316
+ return skip_tags;
317
+ }
318
+
319
+ /*
320
+ * Document-method: auto_link
321
+ *
322
+ * call-seq:
323
+ * auto_link(text, mode=:all, link_attr=nil, skip_tags=nil, flags=0)
324
+ * auto_link(text, mode=:all, link_attr=nil, skip_tags=nil, flags=0) { |link_text| ... }
325
+ *
326
+ * Parses a block of text looking for "safe" urls or email addresses,
327
+ * and turns them into HTML links with the given attributes.
328
+ *
329
+ * NOTE: The block of text may or may not be HTML; if the text is HTML,
330
+ * Rinku will skip the relevant tags to prevent double-linking and linking
331
+ * inside `pre` blocks by default.
332
+ *
333
+ * NOTE: If the input text is HTML, it's expected to be already escaped.
334
+ * Rinku will perform no escaping.
335
+ *
336
+ * NOTE: Currently the follow protocols are considered safe and are the
337
+ * only ones that will be autolinked.
338
+ *
339
+ * http:// https:// ftp:// mailto://
340
+ *
341
+ * Email addresses are also autolinked by default. URLs without a protocol
342
+ * specifier but starting with 'www.' will also be autolinked, defaulting to
343
+ * the 'http://' protocol.
344
+ *
345
+ * - `text` is a string in plain text or HTML markup. If the string is formatted in
346
+ * HTML, Rinku is smart enough to skip the links that are already enclosed in `<a>`
347
+ * tags.`
348
+ *
349
+ * - `mode` is a symbol, either `:all`, `:urls` or `:email_addresses`,
350
+ * which specifies which kind of links will be auto-linked.
351
+ *
352
+ * - `link_attr` is a string containing the link attributes for each link that
353
+ * will be generated. These attributes are not sanitized and will be include as-is
354
+ * in each generated link, e.g.
355
+ *
356
+ * ~~~~~ruby
357
+ * auto_link('http://www.pokemon.com', :all, 'target="_blank"')
358
+ * # => '<a href="http://www.pokemon.com" target="_blank">http://www.pokemon.com</a>'
359
+ * ~~~~~
360
+ *
361
+ * This string can be autogenerated from a hash using the Rails `tag_options` helper.
362
+ *
363
+ * - `skip_tags` is a list of strings with the names of HTML tags that will be skipped
364
+ * when autolinking. If `nil`, this defaults to the value of the global `Rinku.skip_tags`,
365
+ * which is initially `["a", "pre", "code", "kbd", "script"]`.
366
+ *
367
+ * - `flag` is an optional boolean value specifying whether to recognize
368
+ * 'http://foo' as a valid domain, or require at least one '.'. It defaults to false.
369
+ *
370
+ * - `&block` is an optional block argument. If a block is passed, it will
371
+ * be yielded for each found link in the text, and its return value will be used instead
372
+ * of the name of the link. E.g.
373
+ *
374
+ * ~~~~~ruby
375
+ * auto_link('Check it out at http://www.pokemon.com') do |url|
376
+ * "THE POKEMAN WEBSITEZ"
377
+ * end
378
+ * # => 'Check it out at <a href="http://www.pokemon.com">THE POKEMAN WEBSITEZ</a>'
379
+ * ~~~~~~
380
+ */
381
+ static VALUE
382
+ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
383
+ {
384
+ static const char *SKIP_TAGS[] = {"a", "pre", "code", "kbd", "script", NULL};
385
+
386
+ VALUE result, rb_text, rb_mode, rb_html, rb_skip, rb_flags, rb_block;
387
+ struct buf *output_buf;
388
+ int link_mode, count;
389
+ unsigned int link_flags = 0;
390
+ const char *link_attr = NULL;
391
+ const char **skip_tags = NULL;
392
+ ID mode_sym;
393
+
394
+ rb_scan_args(argc, argv, "14&", &rb_text, &rb_mode,
395
+ &rb_html, &rb_skip, &rb_flags, &rb_block);
396
+
397
+ Check_Type(rb_text, T_STRING);
398
+
399
+ if (!NIL_P(rb_mode)) {
400
+ Check_Type(rb_mode, T_SYMBOL);
401
+ mode_sym = SYM2ID(rb_mode);
402
+ } else {
403
+ mode_sym = rb_intern("all");
404
+ }
405
+
406
+ if (!NIL_P(rb_html)) {
407
+ Check_Type(rb_html, T_STRING);
408
+ link_attr = RSTRING_PTR(rb_html);
409
+ }
410
+
411
+ if (NIL_P(rb_skip))
412
+ rb_skip = rb_iv_get(self, "@skip_tags");
413
+
414
+ if (NIL_P(rb_skip)) {
415
+ skip_tags = SKIP_TAGS;
416
+ } else {
417
+ skip_tags = rinku_load_tags(rb_skip);
418
+ }
419
+
420
+ if (!NIL_P(rb_flags)) {
421
+ Check_Type(rb_flags, T_FIXNUM);
422
+ link_flags = FIX2INT(rb_flags);
423
+ }
424
+
425
+ output_buf = bufnew(32);
426
+
427
+ if (mode_sym == rb_intern("all"))
428
+ link_mode = AUTOLINK_ALL;
429
+ else if (mode_sym == rb_intern("email_addresses"))
430
+ link_mode = AUTOLINK_EMAILS;
431
+ else if (mode_sym == rb_intern("urls"))
432
+ link_mode = AUTOLINK_URLS;
433
+ else
434
+ rb_raise(rb_eTypeError,
435
+ "Invalid linking mode (possible values are :all, :urls, :email_addresses)");
436
+
437
+ count = rinku_autolink(
438
+ output_buf,
439
+ RSTRING_PTR(rb_text),
440
+ RSTRING_LEN(rb_text),
441
+ link_mode,
442
+ link_flags,
443
+ link_attr,
444
+ skip_tags,
445
+ RTEST(rb_block) ? &autolink_callback : NULL,
446
+ (void*)rb_block);
447
+
448
+ if (count == 0)
449
+ result = rb_text;
450
+ else {
451
+ result = rb_str_new(output_buf->data, output_buf->size);
452
+ rb_enc_copy(result, rb_text);
453
+ }
454
+
455
+ if (skip_tags != SKIP_TAGS)
456
+ xfree(skip_tags);
457
+
458
+ bufrelease(output_buf);
459
+ return result;
460
+ }
461
+
462
+ void RUBY_EXPORT Init_rinku()
463
+ {
464
+ rb_mRinku = rb_define_module("Rinku");
465
+ rb_define_method(rb_mRinku, "auto_link", rb_rinku_autolink, -1);
466
+ rb_define_const(rb_mRinku, "AUTOLINK_SHORT_DOMAINS", INT2FIX(SD_AUTOLINK_SHORT_DOMAINS));
467
+ }
468
+