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.
@@ -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
@@ -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 &quot;|maillinks -\n&quot; &quot;create link-list&quot;</p>
393
+ <p>macro pager M &quot;|maillinks -\n&quot; &quot;create link-list&quot;</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 &lt;<a class="reference external" href="mailto:michael.uplawski&#64;uplawski.eu">michael.uplawski&#64;uplawski.eu</a>&gt;</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
+
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+
@@ -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: []