maillinks 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/maillinks +26 -0
- data/doc/html/maillinks.html +428 -0
- data/doc/man/maillinks.1.gz +0 -0
- data/doc/pdf/maillinks.pdf +0 -0
- data/doc/rst/maillinks.rst +74 -0
- data/lib/constants.rb +30 -0
- data/lib/file_checking.rb +88 -0
- data/lib/log.conf +53 -0
- data/lib/logging.rb +104 -0
- data/lib/maillinks.rb +148 -0
- data/lib/translating.rb +89 -0
- data/lib/translations +24 -0
- data/maillinks.gemspec +18 -0
- metadata +120 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9fc8e9b6e7a578ac34c6c36e0b6322b577fd59f8928b80038a457d0ca0f4e009
|
|
4
|
+
data.tar.gz: 73b312948b9fa55511f8c2a8a90ea6db2ef3705004157a48669c81ebdab535e6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0f4a795b4ccf6657f1e858e2b15598b35d83982fc0e313ab4764aa5458c9f0a91a346de07eab4ba4f3e5033af3fc8dea5a83261912681c3d6c7a0109d6a7aeec
|
|
7
|
+
data.tar.gz: 84e524fa13f2d572c244710fbe4280f3ff748e1e25783557330c9b1a893986d97dbfd5daa8995c02ce76ad57178c2396d4894468118d6b5251a64493786fad08
|
data/bin/maillinks
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#encoding: UTF-8
|
|
3
|
+
=begin
|
|
4
|
+
/***************************************************************************
|
|
5
|
+
* ©2015-2015 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
6
|
+
* *
|
|
7
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
8
|
+
* it under the terms of the GNU General Public License as published by *
|
|
9
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
10
|
+
* (at your option) any later version. *
|
|
11
|
+
* *
|
|
12
|
+
* This program is distributed in the hope that it will be useful, *
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
15
|
+
* GNU General Public License for more details. *
|
|
16
|
+
* *
|
|
17
|
+
* You should have received a copy of the GNU General Public License *
|
|
18
|
+
* along with this program; if not, write to the *
|
|
19
|
+
* Free Software Foundation, Inc., *
|
|
20
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
21
|
+
***************************************************************************/
|
|
22
|
+
=end
|
|
23
|
+
|
|
24
|
+
require_relative '../lib/maillinks'
|
|
25
|
+
|
|
26
|
+
MailLinks.new(*ARGV)
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
6
|
+
<meta name="generator" content="Docutils 0.13.1: http://docutils.sourceforge.net/" />
|
|
7
|
+
<title>Maillinks</title>
|
|
8
|
+
<style type="text/css">
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
:Author: David Goodger (goodger@python.org)
|
|
12
|
+
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
|
13
|
+
:Copyright: This stylesheet has been placed in the public domain.
|
|
14
|
+
|
|
15
|
+
Default cascading style sheet for the HTML output of Docutils.
|
|
16
|
+
|
|
17
|
+
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
|
18
|
+
customize this style sheet.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/* used to remove borders from tables and images */
|
|
22
|
+
.borderless, table.borderless td, table.borderless th {
|
|
23
|
+
border: 0 }
|
|
24
|
+
|
|
25
|
+
table.borderless td, table.borderless th {
|
|
26
|
+
/* Override padding for "table.docutils td" with "! important".
|
|
27
|
+
The right padding separates the table cells. */
|
|
28
|
+
padding: 0 0.5em 0 0 ! important }
|
|
29
|
+
|
|
30
|
+
.first {
|
|
31
|
+
/* Override more specific margin styles with "! important". */
|
|
32
|
+
margin-top: 0 ! important }
|
|
33
|
+
|
|
34
|
+
.last, .with-subtitle {
|
|
35
|
+
margin-bottom: 0 ! important }
|
|
36
|
+
|
|
37
|
+
.hidden {
|
|
38
|
+
display: none }
|
|
39
|
+
|
|
40
|
+
.subscript {
|
|
41
|
+
vertical-align: sub;
|
|
42
|
+
font-size: smaller }
|
|
43
|
+
|
|
44
|
+
.superscript {
|
|
45
|
+
vertical-align: super;
|
|
46
|
+
font-size: smaller }
|
|
47
|
+
|
|
48
|
+
a.toc-backref {
|
|
49
|
+
text-decoration: none ;
|
|
50
|
+
color: black }
|
|
51
|
+
|
|
52
|
+
blockquote.epigraph {
|
|
53
|
+
margin: 2em 5em ; }
|
|
54
|
+
|
|
55
|
+
dl.docutils dd {
|
|
56
|
+
margin-bottom: 0.5em }
|
|
57
|
+
|
|
58
|
+
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
|
63
|
+
dl.docutils dt {
|
|
64
|
+
font-weight: bold }
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
div.abstract {
|
|
68
|
+
margin: 2em 5em }
|
|
69
|
+
|
|
70
|
+
div.abstract p.topic-title {
|
|
71
|
+
font-weight: bold ;
|
|
72
|
+
text-align: center }
|
|
73
|
+
|
|
74
|
+
div.admonition, div.attention, div.caution, div.danger, div.error,
|
|
75
|
+
div.hint, div.important, div.note, div.tip, div.warning {
|
|
76
|
+
margin: 2em ;
|
|
77
|
+
border: medium outset ;
|
|
78
|
+
padding: 1em }
|
|
79
|
+
|
|
80
|
+
div.admonition p.admonition-title, div.hint p.admonition-title,
|
|
81
|
+
div.important p.admonition-title, div.note p.admonition-title,
|
|
82
|
+
div.tip p.admonition-title {
|
|
83
|
+
font-weight: bold ;
|
|
84
|
+
font-family: sans-serif }
|
|
85
|
+
|
|
86
|
+
div.attention p.admonition-title, div.caution p.admonition-title,
|
|
87
|
+
div.danger p.admonition-title, div.error p.admonition-title,
|
|
88
|
+
div.warning p.admonition-title, .code .error {
|
|
89
|
+
color: red ;
|
|
90
|
+
font-weight: bold ;
|
|
91
|
+
font-family: sans-serif }
|
|
92
|
+
|
|
93
|
+
/* Uncomment (and remove this text!) to get reduced vertical space in
|
|
94
|
+
compound paragraphs.
|
|
95
|
+
div.compound .compound-first, div.compound .compound-middle {
|
|
96
|
+
margin-bottom: 0.5em }
|
|
97
|
+
|
|
98
|
+
div.compound .compound-last, div.compound .compound-middle {
|
|
99
|
+
margin-top: 0.5em }
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
div.dedication {
|
|
103
|
+
margin: 2em 5em ;
|
|
104
|
+
text-align: center ;
|
|
105
|
+
font-style: italic }
|
|
106
|
+
|
|
107
|
+
div.dedication p.topic-title {
|
|
108
|
+
font-weight: bold ;
|
|
109
|
+
font-style: normal }
|
|
110
|
+
|
|
111
|
+
div.figure {
|
|
112
|
+
margin-left: 2em ;
|
|
113
|
+
margin-right: 2em }
|
|
114
|
+
|
|
115
|
+
div.footer, div.header {
|
|
116
|
+
clear: both;
|
|
117
|
+
font-size: smaller }
|
|
118
|
+
|
|
119
|
+
div.line-block {
|
|
120
|
+
display: block ;
|
|
121
|
+
margin-top: 1em ;
|
|
122
|
+
margin-bottom: 1em }
|
|
123
|
+
|
|
124
|
+
div.line-block div.line-block {
|
|
125
|
+
margin-top: 0 ;
|
|
126
|
+
margin-bottom: 0 ;
|
|
127
|
+
margin-left: 1.5em }
|
|
128
|
+
|
|
129
|
+
div.sidebar {
|
|
130
|
+
margin: 0 0 0.5em 1em ;
|
|
131
|
+
border: medium outset ;
|
|
132
|
+
padding: 1em ;
|
|
133
|
+
background-color: #ffffee ;
|
|
134
|
+
width: 40% ;
|
|
135
|
+
float: right ;
|
|
136
|
+
clear: right }
|
|
137
|
+
|
|
138
|
+
div.sidebar p.rubric {
|
|
139
|
+
font-family: sans-serif ;
|
|
140
|
+
font-size: medium }
|
|
141
|
+
|
|
142
|
+
div.system-messages {
|
|
143
|
+
margin: 5em }
|
|
144
|
+
|
|
145
|
+
div.system-messages h1 {
|
|
146
|
+
color: red }
|
|
147
|
+
|
|
148
|
+
div.system-message {
|
|
149
|
+
border: medium outset ;
|
|
150
|
+
padding: 1em }
|
|
151
|
+
|
|
152
|
+
div.system-message p.system-message-title {
|
|
153
|
+
color: red ;
|
|
154
|
+
font-weight: bold }
|
|
155
|
+
|
|
156
|
+
div.topic {
|
|
157
|
+
margin: 2em }
|
|
158
|
+
|
|
159
|
+
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
|
160
|
+
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
|
161
|
+
margin-top: 0.4em }
|
|
162
|
+
|
|
163
|
+
h1.title {
|
|
164
|
+
text-align: center }
|
|
165
|
+
|
|
166
|
+
h2.subtitle {
|
|
167
|
+
text-align: center }
|
|
168
|
+
|
|
169
|
+
hr.docutils {
|
|
170
|
+
width: 75% }
|
|
171
|
+
|
|
172
|
+
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
|
173
|
+
clear: left ;
|
|
174
|
+
float: left ;
|
|
175
|
+
margin-right: 1em }
|
|
176
|
+
|
|
177
|
+
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
|
178
|
+
clear: right ;
|
|
179
|
+
float: right ;
|
|
180
|
+
margin-left: 1em }
|
|
181
|
+
|
|
182
|
+
img.align-center, .figure.align-center, object.align-center {
|
|
183
|
+
display: block;
|
|
184
|
+
margin-left: auto;
|
|
185
|
+
margin-right: auto;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
table.align-center {
|
|
189
|
+
margin-left: auto;
|
|
190
|
+
margin-right: auto;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.align-left {
|
|
194
|
+
text-align: left }
|
|
195
|
+
|
|
196
|
+
.align-center {
|
|
197
|
+
clear: both ;
|
|
198
|
+
text-align: center }
|
|
199
|
+
|
|
200
|
+
.align-right {
|
|
201
|
+
text-align: right }
|
|
202
|
+
|
|
203
|
+
/* reset inner alignment in figures */
|
|
204
|
+
div.align-right {
|
|
205
|
+
text-align: inherit }
|
|
206
|
+
|
|
207
|
+
/* div.align-center * { */
|
|
208
|
+
/* text-align: left } */
|
|
209
|
+
|
|
210
|
+
.align-top {
|
|
211
|
+
vertical-align: top }
|
|
212
|
+
|
|
213
|
+
.align-middle {
|
|
214
|
+
vertical-align: middle }
|
|
215
|
+
|
|
216
|
+
.align-bottom {
|
|
217
|
+
vertical-align: bottom }
|
|
218
|
+
|
|
219
|
+
ol.simple, ul.simple {
|
|
220
|
+
margin-bottom: 1em }
|
|
221
|
+
|
|
222
|
+
ol.arabic {
|
|
223
|
+
list-style: decimal }
|
|
224
|
+
|
|
225
|
+
ol.loweralpha {
|
|
226
|
+
list-style: lower-alpha }
|
|
227
|
+
|
|
228
|
+
ol.upperalpha {
|
|
229
|
+
list-style: upper-alpha }
|
|
230
|
+
|
|
231
|
+
ol.lowerroman {
|
|
232
|
+
list-style: lower-roman }
|
|
233
|
+
|
|
234
|
+
ol.upperroman {
|
|
235
|
+
list-style: upper-roman }
|
|
236
|
+
|
|
237
|
+
p.attribution {
|
|
238
|
+
text-align: right ;
|
|
239
|
+
margin-left: 50% }
|
|
240
|
+
|
|
241
|
+
p.caption {
|
|
242
|
+
font-style: italic }
|
|
243
|
+
|
|
244
|
+
p.credits {
|
|
245
|
+
font-style: italic ;
|
|
246
|
+
font-size: smaller }
|
|
247
|
+
|
|
248
|
+
p.label {
|
|
249
|
+
white-space: nowrap }
|
|
250
|
+
|
|
251
|
+
p.rubric {
|
|
252
|
+
font-weight: bold ;
|
|
253
|
+
font-size: larger ;
|
|
254
|
+
color: maroon ;
|
|
255
|
+
text-align: center }
|
|
256
|
+
|
|
257
|
+
p.sidebar-title {
|
|
258
|
+
font-family: sans-serif ;
|
|
259
|
+
font-weight: bold ;
|
|
260
|
+
font-size: larger }
|
|
261
|
+
|
|
262
|
+
p.sidebar-subtitle {
|
|
263
|
+
font-family: sans-serif ;
|
|
264
|
+
font-weight: bold }
|
|
265
|
+
|
|
266
|
+
p.topic-title {
|
|
267
|
+
font-weight: bold }
|
|
268
|
+
|
|
269
|
+
pre.address {
|
|
270
|
+
margin-bottom: 0 ;
|
|
271
|
+
margin-top: 0 ;
|
|
272
|
+
font: inherit }
|
|
273
|
+
|
|
274
|
+
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
|
275
|
+
margin-left: 2em ;
|
|
276
|
+
margin-right: 2em }
|
|
277
|
+
|
|
278
|
+
pre.code .ln { color: grey; } /* line numbers */
|
|
279
|
+
pre.code, code { background-color: #eeeeee }
|
|
280
|
+
pre.code .comment, code .comment { color: #5C6576 }
|
|
281
|
+
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
|
282
|
+
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
|
283
|
+
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
|
284
|
+
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
|
285
|
+
pre.code .inserted, code .inserted { background-color: #A3D289}
|
|
286
|
+
|
|
287
|
+
span.classifier {
|
|
288
|
+
font-family: sans-serif ;
|
|
289
|
+
font-style: oblique }
|
|
290
|
+
|
|
291
|
+
span.classifier-delimiter {
|
|
292
|
+
font-family: sans-serif ;
|
|
293
|
+
font-weight: bold }
|
|
294
|
+
|
|
295
|
+
span.interpreted {
|
|
296
|
+
font-family: sans-serif }
|
|
297
|
+
|
|
298
|
+
span.option {
|
|
299
|
+
white-space: nowrap }
|
|
300
|
+
|
|
301
|
+
span.pre {
|
|
302
|
+
white-space: pre }
|
|
303
|
+
|
|
304
|
+
span.problematic {
|
|
305
|
+
color: red }
|
|
306
|
+
|
|
307
|
+
span.section-subtitle {
|
|
308
|
+
/* font-size relative to parent (h1..h6 element) */
|
|
309
|
+
font-size: 80% }
|
|
310
|
+
|
|
311
|
+
table.citation {
|
|
312
|
+
border-left: solid 1px gray;
|
|
313
|
+
margin-left: 1px }
|
|
314
|
+
|
|
315
|
+
table.docinfo {
|
|
316
|
+
margin: 2em 4em }
|
|
317
|
+
|
|
318
|
+
table.docutils {
|
|
319
|
+
margin-top: 0.5em ;
|
|
320
|
+
margin-bottom: 0.5em }
|
|
321
|
+
|
|
322
|
+
table.footnote {
|
|
323
|
+
border-left: solid 1px black;
|
|
324
|
+
margin-left: 1px }
|
|
325
|
+
|
|
326
|
+
table.docutils td, table.docutils th,
|
|
327
|
+
table.docinfo td, table.docinfo th {
|
|
328
|
+
padding-left: 0.5em ;
|
|
329
|
+
padding-right: 0.5em ;
|
|
330
|
+
vertical-align: top }
|
|
331
|
+
|
|
332
|
+
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
|
333
|
+
font-weight: bold ;
|
|
334
|
+
text-align: left ;
|
|
335
|
+
white-space: nowrap ;
|
|
336
|
+
padding-left: 0 }
|
|
337
|
+
|
|
338
|
+
/* "booktabs" style (no vertical lines) */
|
|
339
|
+
table.docutils.booktabs {
|
|
340
|
+
border: 0px;
|
|
341
|
+
border-top: 2px solid;
|
|
342
|
+
border-bottom: 2px solid;
|
|
343
|
+
border-collapse: collapse;
|
|
344
|
+
}
|
|
345
|
+
table.docutils.booktabs * {
|
|
346
|
+
border: 0px;
|
|
347
|
+
}
|
|
348
|
+
table.docutils.booktabs th {
|
|
349
|
+
border-bottom: thin solid;
|
|
350
|
+
text-align: left;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
|
354
|
+
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
|
355
|
+
font-size: 100% }
|
|
356
|
+
|
|
357
|
+
ul.auto-toc {
|
|
358
|
+
list-style-type: none }
|
|
359
|
+
|
|
360
|
+
</style>
|
|
361
|
+
</head>
|
|
362
|
+
<body>
|
|
363
|
+
<div class="document" id="maillinks">
|
|
364
|
+
<h1 class="title">Maillinks</h1>
|
|
365
|
+
<h2 class="subtitle" id="print-out-the-urls-of-hyperlinks-in-html-mail">Print out the URLs of hyperlinks in HTML-mail.</h2>
|
|
366
|
+
|
|
367
|
+
<div class="section" id="synopsis">
|
|
368
|
+
<h1>Synopsis</h1>
|
|
369
|
+
<blockquote>
|
|
370
|
+
maillinks [mail-file]</blockquote>
|
|
371
|
+
<p>or</p>
|
|
372
|
+
<blockquote>
|
|
373
|
+
cat [mail-file] | maillinks</blockquote>
|
|
374
|
+
</div>
|
|
375
|
+
<div class="section" id="description">
|
|
376
|
+
<h1>Description</h1>
|
|
377
|
+
<p>In a plain text email message, you will recognize URLs easily. In HTML-mails
|
|
378
|
+
they are most of the time part of a link-tag and thus hidden by the caption
|
|
379
|
+
text which is shown instead. Not only because, nowadays, UBE and UCE are
|
|
380
|
+
composed with HTML, but in any case, before you take any risks, you should be
|
|
381
|
+
aware of where the click on such a link will lead you. Hyperlinks may include
|
|
382
|
+
directives which are likely to be addressed to a remotely running application.</p>
|
|
383
|
+
<p><em>Maillinks</em> will find the URLs “behind” any hyperlink and show it in a footnote
|
|
384
|
+
below the email-text. You can scrutinize the URL and find information about the
|
|
385
|
+
destination, before you engage your web-browser or other tool to follow a link.</p>
|
|
386
|
+
<div class="section" id="integration-in-mail-clients">
|
|
387
|
+
<h2>Integration in mail-clients</h2>
|
|
388
|
+
<p>You can pipe in an email-file to maillinks and thus create macros, where the
|
|
389
|
+
functionality is present in a mail-client. In the Mutt user-agent, to define a
|
|
390
|
+
shortcut, you note the following lines in your .muttrc file:</p>
|
|
391
|
+
<blockquote>
|
|
392
|
+
<p>macro index M "|maillinks -\n" "create link-list"</p>
|
|
393
|
+
<p>macro pager M "|maillinks -\n" "create link-list"</p>
|
|
394
|
+
</blockquote>
|
|
395
|
+
<p>Now you can push the <em>M</em> key either in the pager or in the mail index to pipe
|
|
396
|
+
in a selected mail to maillinks.</p>
|
|
397
|
+
<p>Consult the documentation of your own mail-client to find out if, and how
|
|
398
|
+
macros or similar options can be configured.</p>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="section" id="options">
|
|
402
|
+
<h1>Options</h1>
|
|
403
|
+
<p><em>Maillinks</em> does currently not interpret any command line arguments other than
|
|
404
|
+
the name of a mail-file to analyze.</p>
|
|
405
|
+
</div>
|
|
406
|
+
<div class="section" id="other-information">
|
|
407
|
+
<h1>Other Information</h1>
|
|
408
|
+
<dl class="docutils">
|
|
409
|
+
<dt>Source-code and Development</dt>
|
|
410
|
+
<dd><em>Maillinks</em> has been developed in Ruby and is published as a Ruby-Gem. If you
|
|
411
|
+
have installed the program from the Gem-file, then, to access the
|
|
412
|
+
source-code, find Maillinks in the Gem-directory for your Ruby-version.
|
|
413
|
+
All source-files found are in the sub-directories <em>/bin</em> and <em>/lib</em>.
|
|
414
|
+
Alternatively, you can unpack the Gem-file with <em>tar -xf</em>, then decompress
|
|
415
|
+
the data-archive with <em>tar -xzf</em>.</dd>
|
|
416
|
+
<dt>Version</dt>
|
|
417
|
+
<dd>0.1.2 (May 2017)</dd>
|
|
418
|
+
<dt>License</dt>
|
|
419
|
+
<dd><em>Maillinks</em> is published under the conditions of the GNU General Public
|
|
420
|
+
License, version 3.0 or later.</dd>
|
|
421
|
+
<dt>Author</dt>
|
|
422
|
+
<dd>Michael Uplawski <<a class="reference external" href="mailto:michael.uplawski@uplawski.eu">michael.uplawski@uplawski.eu</a>></dd>
|
|
423
|
+
</dl>
|
|
424
|
+
<p>Ω</p>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</body>
|
|
428
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
=========================
|
|
2
|
+
Maillinks
|
|
3
|
+
=========================
|
|
4
|
+
-------------------------------------------------
|
|
5
|
+
Print out the URLs of hyperlinks in HTML-mail.
|
|
6
|
+
-------------------------------------------------
|
|
7
|
+
|
|
8
|
+
Synopsis
|
|
9
|
+
========
|
|
10
|
+
maillinks [mail-file]
|
|
11
|
+
|
|
12
|
+
or
|
|
13
|
+
|
|
14
|
+
cat [mail-file] | maillinks
|
|
15
|
+
|
|
16
|
+
Description
|
|
17
|
+
===========
|
|
18
|
+
In a plain text email message, you will recognize URLs easily. In HTML-mails
|
|
19
|
+
they are most of the time part of a link-tag and thus hidden by the caption
|
|
20
|
+
text which is shown instead. Not only because, nowadays, UBE and UCE are
|
|
21
|
+
composed with HTML, but in any case, before you take any risks, you should be
|
|
22
|
+
aware of where the click on such a link will lead you. Hyperlinks may include
|
|
23
|
+
directives which are likely to be addressed to a remotely running application.
|
|
24
|
+
|
|
25
|
+
*Maillinks* will find the URLs “behind” any hyperlink and show it in a footnote
|
|
26
|
+
below the email-text. You can scrutinize the URL and find information about the
|
|
27
|
+
destination, before you engage your web-browser or other tool to follow a link.
|
|
28
|
+
|
|
29
|
+
Integration in mail-clients
|
|
30
|
+
----------------------------
|
|
31
|
+
|
|
32
|
+
You can pipe in an email-file to maillinks and thus create macros, where the
|
|
33
|
+
functionality is present in a mail-client. In the Mutt user-agent, to define a
|
|
34
|
+
shortcut, you note the following lines in your .muttrc file:
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
macro index M "\|maillinks -\\n" "create link-list"
|
|
38
|
+
|
|
39
|
+
macro pager M "\|maillinks -\\n" "create link-list"
|
|
40
|
+
|
|
41
|
+
Now you can push the *M* key either in the pager or in the mail index to pipe
|
|
42
|
+
in a selected mail to maillinks.
|
|
43
|
+
|
|
44
|
+
Consult the documentation of your own mail-client to find out if, and how
|
|
45
|
+
macros or similar options can be configured.
|
|
46
|
+
|
|
47
|
+
Options
|
|
48
|
+
=========
|
|
49
|
+
*Maillinks* does currently not interpret any command line arguments other than
|
|
50
|
+
the name of a mail-file to analyze.
|
|
51
|
+
|
|
52
|
+
Other Information
|
|
53
|
+
==================
|
|
54
|
+
|
|
55
|
+
Source-code and Development
|
|
56
|
+
*Maillinks* has been developed in Ruby and is published as a Ruby-Gem. If you
|
|
57
|
+
have installed the program from the Gem-file, then, to access the
|
|
58
|
+
source-code, find Maillinks in the Gem-directory for your Ruby-version.
|
|
59
|
+
All source-files found are in the sub-directories */bin* and */lib*.
|
|
60
|
+
Alternatively, you can unpack the Gem-file with *tar -xf*, then decompress
|
|
61
|
+
the data-archive with *tar -xzf*.
|
|
62
|
+
|
|
63
|
+
Version
|
|
64
|
+
0.1.2 (May 2017)
|
|
65
|
+
|
|
66
|
+
License
|
|
67
|
+
*Maillinks* is published under the conditions of the GNU General Public
|
|
68
|
+
License, version 3.0 or later.
|
|
69
|
+
|
|
70
|
+
Author
|
|
71
|
+
Michael Uplawski <michael.uplawski@uplawski.eu>
|
|
72
|
+
|
|
73
|
+
Ω
|
|
74
|
+
|
data/lib/constants.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2015-2015 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
9
|
+
* (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
|
17
|
+
* along with this program; if not, write to the *
|
|
18
|
+
* Free Software Foundation, Inc., *
|
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
20
|
+
***************************************************************************/
|
|
21
|
+
=end
|
|
22
|
+
|
|
23
|
+
require 'tmpdir'
|
|
24
|
+
|
|
25
|
+
# Idiots. Not only do they not know what email is, they also
|
|
26
|
+
# ignore the rules for HTML. Now they write HTML-mail.
|
|
27
|
+
Block_Tags = %w~body div p hr table img~
|
|
28
|
+
|
|
29
|
+
BROWSER = "/usr/bin/w3m"
|
|
30
|
+
OUT_FILE = "%s/mail_message.html" %Dir.tmpdir
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2011-2013 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
9
|
+
* (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
|
17
|
+
* along with this program; if not, write to the *
|
|
18
|
+
* Free Software Foundation, Inc., *
|
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
20
|
+
***************************************************************************/
|
|
21
|
+
=end
|
|
22
|
+
|
|
23
|
+
=begin
|
|
24
|
+
A module to facilitate frequently occuring checks on
|
|
25
|
+
file-system objects
|
|
26
|
+
=end
|
|
27
|
+
module File_Checking
|
|
28
|
+
|
|
29
|
+
@@text_messages = {
|
|
30
|
+
:exist? => "does not exist!",
|
|
31
|
+
:exist => "does not exist!",
|
|
32
|
+
:readable? => "is not readable!",
|
|
33
|
+
:readable => "is not readable!",
|
|
34
|
+
:executable? => "is not executable!",
|
|
35
|
+
:executable => "is not executable!",
|
|
36
|
+
:writable? => "is not writable!",
|
|
37
|
+
:writable => "is not writable!",
|
|
38
|
+
:directory? => "is not a directory!",
|
|
39
|
+
:directory => "is not a directory!",
|
|
40
|
+
:file? => "is not a file!",
|
|
41
|
+
:file => "is not a file!",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Checks if the file with the name from the first
|
|
45
|
+
# parameter has the properties, listed in the second.
|
|
46
|
+
# The messages parameter is an array of one or several
|
|
47
|
+
# of :exist?, :readable?, :writable?, :directory? or
|
|
48
|
+
# their string-representations, respectively.
|
|
49
|
+
# Returns nil in case of success, otherwise an
|
|
50
|
+
# informative message, describing the first negative
|
|
51
|
+
# test-result.
|
|
52
|
+
def file_check(file, *messages)
|
|
53
|
+
File_Checking.file_check(file, *messages)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Checks if the file with the name from the first
|
|
57
|
+
# parameter has the properties, listed in the second.
|
|
58
|
+
# The messages parameter is an array of one or all
|
|
59
|
+
# of :exist?, :readable?, :writable?, :directory? or
|
|
60
|
+
# their string-representations, respectively.
|
|
61
|
+
# Returns nil in case of success, otherwise an
|
|
62
|
+
# informative message, describing the first negative
|
|
63
|
+
# test-result.
|
|
64
|
+
def self.file_check(file, *messages)
|
|
65
|
+
msg = nil
|
|
66
|
+
if(file && messages.respond_to?(:to_ary) && !messages.empty?)
|
|
67
|
+
messages.each do |k|
|
|
68
|
+
if(! k.to_s.end_with?('?'))
|
|
69
|
+
k = (k.to_s << '?').to_sym
|
|
70
|
+
end
|
|
71
|
+
@log.debug ('checking ' << k.to_s) if @log
|
|
72
|
+
if(msg == nil && File.respond_to?(k) && ! File.send(k, file.to_s))
|
|
73
|
+
msg = "#{file} #{@@text_messages[k.to_sym]}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
msg
|
|
78
|
+
end
|
|
79
|
+
alias :check_file :file_check
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
=begin
|
|
83
|
+
# example
|
|
84
|
+
|
|
85
|
+
include File_Checking
|
|
86
|
+
msg = file_check('some_file.txt', [:exist?, :readable?, 'writable'])
|
|
87
|
+
puts msg if msg
|
|
88
|
+
=end
|
data/lib/log.conf
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2013 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
9
|
+
* (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
|
17
|
+
* along with this program; if not, write to the *
|
|
18
|
+
* Free Software Foundation, Inc., *
|
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
20
|
+
***************************************************************************/
|
|
21
|
+
|
|
22
|
+
A simplified logger configuration. Set the level for each individual logger
|
|
23
|
+
below. Choose a different log-device or log-file if you like. Keep the
|
|
24
|
+
formatting intact. Do not change other sections of this file.
|
|
25
|
+
=end
|
|
26
|
+
|
|
27
|
+
# Do not touch from here ----->
|
|
28
|
+
require 'logger'
|
|
29
|
+
|
|
30
|
+
debug = Logger::DEBUG
|
|
31
|
+
info = Logger::INFO
|
|
32
|
+
error = Logger::ERROR
|
|
33
|
+
fatal = Logger::FATAL
|
|
34
|
+
warn = Logger::WARN
|
|
35
|
+
unknown = Logger::UNKNOWN
|
|
36
|
+
{
|
|
37
|
+
# <---------------- to here !
|
|
38
|
+
|
|
39
|
+
# Enter your settings here, but take into consideration that not all
|
|
40
|
+
# the named classes will really produce readable output. Well, you can
|
|
41
|
+
# always try... Either name just the log-level or make the log-level
|
|
42
|
+
# precede the output-device or output-file like in the examples.
|
|
43
|
+
|
|
44
|
+
# Example: naming a log-file
|
|
45
|
+
# :HtmlBuilder => [info, 'C:\temp\htmlbuilder.log']
|
|
46
|
+
# :HtmlBuilder => [debug, '/tmp/htmlbuilder.log'],
|
|
47
|
+
|
|
48
|
+
:MailLinks => info,
|
|
49
|
+
|
|
50
|
+
# And ignore the remainder, too.
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#eof
|
data/lib/logging.rb
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2011-2014 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
9
|
+
* (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
|
17
|
+
* along with this program; if not, write to the *
|
|
18
|
+
* Free Software Foundation, Inc., *
|
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
20
|
+
***************************************************************************/
|
|
21
|
+
=end
|
|
22
|
+
require 'logger'
|
|
23
|
+
require_relative 'translating'
|
|
24
|
+
|
|
25
|
+
# Creates for each implementing object a member @log and precede its
|
|
26
|
+
# output with the name of the class of the object.
|
|
27
|
+
module Logging
|
|
28
|
+
include Translating
|
|
29
|
+
include File_Checking
|
|
30
|
+
|
|
31
|
+
@@have_log = false
|
|
32
|
+
@@LOG_CONF = File.dirname(File.absolute_path(__FILE__)) << File::Separator << 'log.conf'
|
|
33
|
+
|
|
34
|
+
def init_logger(target = STDOUT, level = Logger::INFO)
|
|
35
|
+
# allow to override the set log-levels with an
|
|
36
|
+
# external configuration (log.conf).
|
|
37
|
+
log_conf
|
|
38
|
+
# Or use the defaults as set here or elsewhere...
|
|
39
|
+
|
|
40
|
+
@level ||= level
|
|
41
|
+
@target ||= target
|
|
42
|
+
|
|
43
|
+
@log = Logger.new(@target)
|
|
44
|
+
@log.level = @level
|
|
45
|
+
|
|
46
|
+
@log.formatter = proc do |severity, datetime, progname, msg|
|
|
47
|
+
t = Time.now
|
|
48
|
+
"#{self.class.name}: #{severity} #{t.hour}-#{t.min}-#{t.sec}: #{msg}\n"
|
|
49
|
+
end
|
|
50
|
+
if ! @@have_log
|
|
51
|
+
@log.debug self.class.name.dup << ' reading logging-configuration from ' << @@LOG_CONF
|
|
52
|
+
@@have_log = true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def log_target=(target)
|
|
58
|
+
@target = target
|
|
59
|
+
@log = Logger.new(@@target)
|
|
60
|
+
@log.level = @level
|
|
61
|
+
end
|
|
62
|
+
def log_level=(level)
|
|
63
|
+
@level = level
|
|
64
|
+
@log.level = @level
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
# Override or set the log-level and target-device, as set in a file 'log.conf'.
|
|
69
|
+
# I do not like the look of this, but it works just the way I want it to.
|
|
70
|
+
# "HEAVANS! Isn't there a standard way to do this in Ruby, for Christ's sake?", you say.
|
|
71
|
+
# Heck, I don't care. <= Read that again, I say.
|
|
72
|
+
def log_conf
|
|
73
|
+
config = level = target = nil
|
|
74
|
+
if(File.exist?(@@LOG_CONF) )
|
|
75
|
+
begin
|
|
76
|
+
conf = File.read(@@LOG_CONF)
|
|
77
|
+
config = instance_eval(conf)
|
|
78
|
+
rescue Exception => ex
|
|
79
|
+
STDERR.puts trl("WARNING! Cannot evaluate the logger-configuration!") << ' ' << ex.message
|
|
80
|
+
STDERR.puts trl("Default log-levels apply.")
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
puts trl("Default log-levels apply.")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if(config && config.respond_to?(:to_hash) )
|
|
87
|
+
config.default = nil
|
|
88
|
+
config = config[self.class.name.to_sym]
|
|
89
|
+
if(config )
|
|
90
|
+
if(config.respond_to?(:to_ary) && config.size == 2)
|
|
91
|
+
@level, @target = config
|
|
92
|
+
logdir = File.dirname(@target)
|
|
93
|
+
msg = file_check(logdir, :exist?, :directory?, :writable?)
|
|
94
|
+
if(msg)
|
|
95
|
+
STDERR.puts trl("WARNING! A logfile for '%s' cannot be written to %s (%s)!") %[self.class.name, logdir, msg]
|
|
96
|
+
@target = nil
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
@level = config
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/maillinks.rb
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2015-2015 Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
9
|
+
* (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
|
17
|
+
* along with this program; if not, write to the *
|
|
18
|
+
* Free Software Foundation, Inc., *
|
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
20
|
+
***************************************************************************/
|
|
21
|
+
=end
|
|
22
|
+
|
|
23
|
+
require_relative 'file_checking'
|
|
24
|
+
require_relative 'logging'
|
|
25
|
+
require_relative 'constants'
|
|
26
|
+
require 'mail'
|
|
27
|
+
require 'nokogiri'
|
|
28
|
+
require 'escape'
|
|
29
|
+
|
|
30
|
+
# show me the urls.
|
|
31
|
+
class MailLinks
|
|
32
|
+
include Logging
|
|
33
|
+
def initialize(mail = nil)
|
|
34
|
+
|
|
35
|
+
init_logger(STDOUT)
|
|
36
|
+
# a file may be piped in from the command-line...
|
|
37
|
+
@log.debug('parameter is ' << (mail ? mail : ' N I L' ))
|
|
38
|
+
if(mail && '-' != mail.strip)
|
|
39
|
+
msg = File_Checking.file_check(mail, :exist, :file, :readable)
|
|
40
|
+
if(msg)
|
|
41
|
+
@log.error(msg)
|
|
42
|
+
usage
|
|
43
|
+
exit false
|
|
44
|
+
else
|
|
45
|
+
@log.debug('will work on mail-file ' << mail)
|
|
46
|
+
@mail = mail
|
|
47
|
+
|
|
48
|
+
File::open(OUT_FILE, 'w+') {|of| of << File::read(@mail) }
|
|
49
|
+
end
|
|
50
|
+
elsif(!$<.eof?)
|
|
51
|
+
@log.debug('Reading data from STDIN. Hit Ctrl+D to interrupt')
|
|
52
|
+
input = $<.read
|
|
53
|
+
@log.debug('input length is ' << input.length.to_s)
|
|
54
|
+
if(input.length < 10)
|
|
55
|
+
@log.error('insufficient input data')
|
|
56
|
+
@log.error('No mail text provided')
|
|
57
|
+
usage()
|
|
58
|
+
exit false
|
|
59
|
+
end
|
|
60
|
+
# ... provide a temporary file in that case,
|
|
61
|
+
# @mail = OUT_FILE
|
|
62
|
+
File.open(OUT_FILE, 'w+') {|of| of << input}
|
|
63
|
+
@mail = OUT_FILE.dup
|
|
64
|
+
@log.debug('Temporary eml-file is written.')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
procede
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
def procede
|
|
72
|
+
handle_mail
|
|
73
|
+
show_mail
|
|
74
|
+
end
|
|
75
|
+
def usage
|
|
76
|
+
msg = 'Usage: ' << $0 << ' ' << '<mail-file>'
|
|
77
|
+
msg << "\n or: " << 'cat <mail-file> |' << $0
|
|
78
|
+
puts msg
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def show_mail
|
|
82
|
+
cmd = Escape.shell_command([BROWSER, OUT_FILE])
|
|
83
|
+
system(cmd)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def handle_mail
|
|
87
|
+
begin
|
|
88
|
+
mail_content = File.read(OUT_FILE).encode(:invalid=>:replace, :undef=>:replace)
|
|
89
|
+
mail_body = mail_content.split("</head>")[1].split("</html>")[0]
|
|
90
|
+
mail_body = Mail::Encodings::QuotedPrintable.decode(mail_body)
|
|
91
|
+
rescue Exception => ex
|
|
92
|
+
@log.error('Cannot find the html-body in this mail. Maybe it is not a HTML-mail?')
|
|
93
|
+
@log.debug(ex.message)
|
|
94
|
+
end
|
|
95
|
+
if !mail_body
|
|
96
|
+
mail_body = mail_content
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
html_mail = Nokogiri::HTML::Document.new.fragment(mail_body)
|
|
100
|
+
|
|
101
|
+
a_nodes = html_mail.xpath(".//a")
|
|
102
|
+
if(! a_nodes || a_nodes.empty? )
|
|
103
|
+
@log.info('no links in this mail')
|
|
104
|
+
exit true
|
|
105
|
+
end
|
|
106
|
+
begin
|
|
107
|
+
# See idiots in constants.rb
|
|
108
|
+
body = nil
|
|
109
|
+
Block_Tags.each do |bt|
|
|
110
|
+
body = html_mail.xpath('.//' << bt )[0]
|
|
111
|
+
break if body
|
|
112
|
+
end
|
|
113
|
+
# abort, if not
|
|
114
|
+
body.add_child("<hr/>")
|
|
115
|
+
body.add_child("<h2>HTML-links in this mail</h2>")[0]
|
|
116
|
+
if(!body)
|
|
117
|
+
@log.error('cannot find a body-tag in this mail')
|
|
118
|
+
exit false
|
|
119
|
+
end
|
|
120
|
+
dl = body.add_child("<dl id='links'></dl>")[0]
|
|
121
|
+
if(!dl)
|
|
122
|
+
@log.error('cannot add a list-tag')
|
|
123
|
+
exit false
|
|
124
|
+
end
|
|
125
|
+
links_hash = Hash.new
|
|
126
|
+
a_nodes.each_with_index do |a, i|
|
|
127
|
+
@log.debug('a is ' << a.to_s.gsub("=\" ", ''))
|
|
128
|
+
caption = a.inner_text.to_s.strip
|
|
129
|
+
href = a.attribute('href')
|
|
130
|
+
if(href)
|
|
131
|
+
@log.debug('href is: ' << href.to_s)
|
|
132
|
+
link = href.content
|
|
133
|
+
@log.debug('link is ex href.content: ' << link.to_s)
|
|
134
|
+
a.add_next_sibling("<span>[" << (i.next).to_s << "]</span>")
|
|
135
|
+
dl.add_child("<dt>%i) %s</dt>"%[i.next, caption])
|
|
136
|
+
dl.add_child("<dd style='white-space:nowrap;'><a href='%s'>%s</a></dd>"%[link,link])
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
rescue Exception => ex
|
|
140
|
+
@log.error("cannot handle the HTML in this mail: " << ex.message)
|
|
141
|
+
exit false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
File.open(OUT_FILE, 'w') do |mf|
|
|
145
|
+
mf << html_mail
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/translating.rb
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#encoding: UTF-8
|
|
2
|
+
=begin
|
|
3
|
+
/***************************************************************************
|
|
4
|
+
* ©2011-2014, Michael Uplawski *
|
|
5
|
+
* <michael.uplawski@uplawski.eu> *
|
|
6
|
+
* *
|
|
7
|
+
* This program is free software; you can redistribute it and/or modify *
|
|
8
|
+
* it under the terms of the GNU General Public License as published by *
|
|
9
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
|
10
|
+
* (at your option) any later version. *
|
|
11
|
+
* *
|
|
12
|
+
* This program is distributed in the hope that it will be useful, *
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
15
|
+
* GNU General Public License for more details. *
|
|
16
|
+
* *
|
|
17
|
+
* You should have received a copy of the GNU General Public License *
|
|
18
|
+
* along with this program; if not, write to the *
|
|
19
|
+
* Free Software Foundation, Inc., *
|
|
20
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
21
|
+
***************************************************************************/
|
|
22
|
+
=end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
RD = File.expand_path(File.dirname(__FILE__) ) + File::SEPARATOR if !defined?(RD)
|
|
26
|
+
|
|
27
|
+
require 'yaml'
|
|
28
|
+
require_relative 'file_checking'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# A way to produce translated text in a ruby-program.
|
|
32
|
+
# Translations are read from a file "translations" in the program folder.
|
|
33
|
+
module Translating
|
|
34
|
+
# There are better ways to extend a translated
|
|
35
|
+
# string, but I keep the 'wild-card' for
|
|
36
|
+
# historical reasons.
|
|
37
|
+
@@awild = 'XX'
|
|
38
|
+
|
|
39
|
+
@@lang = nil
|
|
40
|
+
@@lang_file = format("%s%s", RD, 'LANG')
|
|
41
|
+
@@tr = YAML::load_file("#{RD}translations")
|
|
42
|
+
|
|
43
|
+
# find the current language-setting and return it.
|
|
44
|
+
def self.language()
|
|
45
|
+
if @@lang == nil
|
|
46
|
+
r = ENV['LANG']
|
|
47
|
+
if(r)
|
|
48
|
+
@@lang = r[0, 2]
|
|
49
|
+
elsif( !File_Checking::file_check(@@lang_file, [:exist?, :readable?]) && File::size(@@lang_file) >= 2)
|
|
50
|
+
File::open(@@lang_file, 'r') {|f| @@lang = f.readline}
|
|
51
|
+
@@lang.chomp!.downcase! if @@lang
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
@@lang = 'en' if !@@lang
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Translate a string to the currently set langage.
|
|
58
|
+
# The args parameter may contain replacement-text which
|
|
59
|
+
# will appear at the positions indicated by wildcard-characters
|
|
60
|
+
# in the original string.
|
|
61
|
+
def self.trl(t, *args)
|
|
62
|
+
Translating::language()
|
|
63
|
+
lt = @@tr[t]
|
|
64
|
+
if(lt)
|
|
65
|
+
lt = lt[@@lang]
|
|
66
|
+
else
|
|
67
|
+
# File.open('/tmp/mtf', 'a+') {|f| f << t << "\n"}
|
|
68
|
+
puts "\nTRANSLATION MISSING: \"" << t << "\""
|
|
69
|
+
end
|
|
70
|
+
lt ||= t
|
|
71
|
+
if(args && !args.empty?)
|
|
72
|
+
i = -1
|
|
73
|
+
lt = lt.gsub(@@awild) do |a|
|
|
74
|
+
i += 1
|
|
75
|
+
args.flatten[i]
|
|
76
|
+
end
|
|
77
|
+
lt += args[i + 1, args.length].join
|
|
78
|
+
end
|
|
79
|
+
return lt
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Translate a string to the currently set langage.
|
|
83
|
+
# The args parameter may contain replacement-text which
|
|
84
|
+
# will appear at the positions indicated by wildcard-characters
|
|
85
|
+
# in the original string.
|
|
86
|
+
def trl(t, *args )
|
|
87
|
+
Translating::trl(t, args)
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/translations
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copyright (c) 2011, Michael Uplawski <michael.uplawski@uplawski.eu>
|
|
2
|
+
# This program is free software; you can redistribute it and/or modify
|
|
3
|
+
# it under the terms of the GNU General Public License as published by
|
|
4
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
5
|
+
# (at your option) any later version.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, write to the
|
|
14
|
+
# Free Software Foundation, Inc.,
|
|
15
|
+
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
16
|
+
# =========================================================================
|
|
17
|
+
# These are the messages printed by Crème Fraîche. The first line of each
|
|
18
|
+
# block is the original english message, the following lines contain the
|
|
19
|
+
# translations for different locales.
|
|
20
|
+
|
|
21
|
+
# Keep this key 'MailLinks' as it is
|
|
22
|
+
|
|
23
|
+
MailLinks:
|
|
24
|
+
|
data/maillinks.gemspec
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'maillinks'
|
|
3
|
+
s.version = '0.1.4'
|
|
4
|
+
s.date = '2018-04-03'
|
|
5
|
+
s.summary = "runtime requirements, URLs better parsed, because the whole Quoted Printable decoding is done correctly, now."
|
|
6
|
+
s.description = "Marks in an email HTMl-links and creates a list of all links at the bottom of the mail-text. Best used with the Mutt MUA"
|
|
7
|
+
s.authors = ["Michael Uplawski"]
|
|
8
|
+
s.email = 'michael.uplawski@uplawski.eu'
|
|
9
|
+
s.files = %w~maillinks~.collect{|f| 'bin/' << f} + %w~constants.rb file_checking.rb log.conf logging.rb maillinks.rb translating.rb translations ~.collect{|f| 'lib/' << f} + %w~html/maillinks.html man/maillinks.1.gz pdf/maillinks.pdf rst/maillinks.rst~.collect{|f| 'doc/' << f} + %w~maillinks.gemspec~.collect{|f|f}
|
|
10
|
+
s.homepage = 'http://rubygems.org'
|
|
11
|
+
s.requirements = 'Linux, text-browser W3M'
|
|
12
|
+
s.add_runtime_dependency 'nokogiri', '~> 1.8', '>= 1.8.2'
|
|
13
|
+
s.add_runtime_dependency 'escape', '~> 0.0', '>= 0.0.4'
|
|
14
|
+
s.add_runtime_dependency 'mail', '~> 2.7', '>= 2.7.0'
|
|
15
|
+
s.executables = 'maillinks'
|
|
16
|
+
s.license = 'GPL-3'
|
|
17
|
+
s.required_ruby_version = '>= 2.1.2'
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: maillinks
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.4
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michael Uplawski
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-04-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: nokogiri
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.8'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 1.8.2
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.8'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.8.2
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: escape
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.0'
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 0.0.4
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - "~>"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0.0'
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: 0.0.4
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: mail
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '2.7'
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: 2.7.0
|
|
63
|
+
type: :runtime
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '2.7'
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 2.7.0
|
|
73
|
+
description: Marks in an email HTMl-links and creates a list of all links at the bottom
|
|
74
|
+
of the mail-text. Best used with the Mutt MUA
|
|
75
|
+
email: michael.uplawski@uplawski.eu
|
|
76
|
+
executables:
|
|
77
|
+
- maillinks
|
|
78
|
+
extensions: []
|
|
79
|
+
extra_rdoc_files: []
|
|
80
|
+
files:
|
|
81
|
+
- bin/maillinks
|
|
82
|
+
- doc/html/maillinks.html
|
|
83
|
+
- doc/man/maillinks.1.gz
|
|
84
|
+
- doc/pdf/maillinks.pdf
|
|
85
|
+
- doc/rst/maillinks.rst
|
|
86
|
+
- lib/constants.rb
|
|
87
|
+
- lib/file_checking.rb
|
|
88
|
+
- lib/log.conf
|
|
89
|
+
- lib/logging.rb
|
|
90
|
+
- lib/maillinks.rb
|
|
91
|
+
- lib/translating.rb
|
|
92
|
+
- lib/translations
|
|
93
|
+
- maillinks.gemspec
|
|
94
|
+
homepage: http://rubygems.org
|
|
95
|
+
licenses:
|
|
96
|
+
- GPL-3
|
|
97
|
+
metadata: {}
|
|
98
|
+
post_install_message:
|
|
99
|
+
rdoc_options: []
|
|
100
|
+
require_paths:
|
|
101
|
+
- lib
|
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: 2.1.2
|
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
requirements:
|
|
113
|
+
- Linux, text-browser W3M
|
|
114
|
+
rubyforge_project:
|
|
115
|
+
rubygems_version: 2.7.6
|
|
116
|
+
signing_key:
|
|
117
|
+
specification_version: 4
|
|
118
|
+
summary: runtime requirements, URLs better parsed, because the whole Quoted Printable
|
|
119
|
+
decoding is done correctly, now.
|
|
120
|
+
test_files: []
|