pec_ruby 0.1.0 → 0.2.0
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 +4 -4
- data/.rspec +3 -0
- data/.rspec_status +99 -0
- data/CHANGELOG.md +28 -1
- data/Gemfile.lock +84 -0
- data/README.md +138 -16
- data/Rakefile +12 -0
- data/lib/pec_ruby/attachment.rb +139 -0
- data/lib/pec_ruby/client.rb +2 -1
- data/lib/pec_ruby/message.rb +111 -4
- data/lib/pec_ruby/version.rb +1 -1
- data/pec_ruby.gemspec +39 -0
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2065977ec4bb7287a8fb74b7a24a2f02a5f8491e0e9c7ee5900583430643324
|
4
|
+
data.tar.gz: 8b1c48a688a30b6a717526b00ccb605274e0c27587f81615c48e529496fc3349
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 401cef0a64fed51a473b4c0626007daaf699cb35ae9fdd244280bea5faccef08550f80600fa9dc53c9def9c469e4b08b105634e3aca277196bde8a53cbf228c4
|
7
|
+
data.tar.gz: 7aa36dddfb5e5d354d682069eed41ffd5a3de9d6242e86f33856c6e58a98f45c6cb8405e3d1df5249625d229e93db2f0bca0d0fc7b7a5de7aecc968ea19e0c99
|
data/.rspec
ADDED
data/.rspec_status
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
-------------------------------------------- | ------ | --------------- |
|
3
|
+
./spec/pec_ruby/attachment_spec.rb[1:1:1] | passed | 0.00043 seconds |
|
4
|
+
./spec/pec_ruby/attachment_spec.rb[1:2:1:1] | passed | 0.00521 seconds |
|
5
|
+
./spec/pec_ruby/attachment_spec.rb[1:2:2:1] | passed | 0.00009 seconds |
|
6
|
+
./spec/pec_ruby/attachment_spec.rb[1:3:1:1] | passed | 0.00008 seconds |
|
7
|
+
./spec/pec_ruby/attachment_spec.rb[1:3:2:1] | passed | 0.00007 seconds |
|
8
|
+
./spec/pec_ruby/attachment_spec.rb[1:4:1] | passed | 0.00007 seconds |
|
9
|
+
./spec/pec_ruby/attachment_spec.rb[1:5:1] | passed | 0.00007 seconds |
|
10
|
+
./spec/pec_ruby/attachment_spec.rb[1:6:1] | passed | 0.00008 seconds |
|
11
|
+
./spec/pec_ruby/attachment_spec.rb[1:7:1] | passed | 0.00033 seconds |
|
12
|
+
./spec/pec_ruby/attachment_spec.rb[1:8:1] | passed | 0.0023 seconds |
|
13
|
+
./spec/pec_ruby/attachment_spec.rb[1:8:2] | passed | 0.00063 seconds |
|
14
|
+
./spec/pec_ruby/attachment_spec.rb[1:9:1] | passed | 0.00056 seconds |
|
15
|
+
./spec/pec_ruby/attachment_spec.rb[1:10:1] | passed | 0.00051 seconds |
|
16
|
+
./spec/pec_ruby/attachment_spec.rb[1:11:1] | passed | 0.00014 seconds |
|
17
|
+
./spec/pec_ruby/attachment_spec.rb[1:12:1:1] | passed | 0.00055 seconds |
|
18
|
+
./spec/pec_ruby/attachment_spec.rb[1:12:2:1] | passed | 0.00013 seconds |
|
19
|
+
./spec/pec_ruby/attachment_spec.rb[1:12:3:1] | passed | 0.0001 seconds |
|
20
|
+
./spec/pec_ruby/attachment_spec.rb[1:13:1:1] | passed | 0.00076 seconds |
|
21
|
+
./spec/pec_ruby/attachment_spec.rb[1:13:2:1] | passed | 0.0002 seconds |
|
22
|
+
./spec/pec_ruby/attachment_spec.rb[1:13:3:1] | passed | 0.00076 seconds |
|
23
|
+
./spec/pec_ruby/attachment_spec.rb[2:1:1] | passed | 0.00004 seconds |
|
24
|
+
./spec/pec_ruby/attachment_spec.rb[2:2:1] | passed | 0.00007 seconds |
|
25
|
+
./spec/pec_ruby/attachment_spec.rb[2:3:1] | passed | 0.00007 seconds |
|
26
|
+
./spec/pec_ruby/attachment_spec.rb[2:4:1] | passed | 0.00007 seconds |
|
27
|
+
./spec/pec_ruby/attachment_spec.rb[2:5:1] | passed | 0.00008 seconds |
|
28
|
+
./spec/pec_ruby/attachment_spec.rb[2:6:1:1] | passed | 0.00007 seconds |
|
29
|
+
./spec/pec_ruby/attachment_spec.rb[2:6:2:1] | passed | 0.00124 seconds |
|
30
|
+
./spec/pec_ruby/attachment_spec.rb[2:7:1] | passed | 0.00022 seconds |
|
31
|
+
./spec/pec_ruby/attachment_spec.rb[2:8:1:1] | passed | 0.00008 seconds |
|
32
|
+
./spec/pec_ruby/attachment_spec.rb[2:8:2:1] | passed | 0.00008 seconds |
|
33
|
+
./spec/pec_ruby/client_spec.rb[1:1:1] | passed | 0.00004 seconds |
|
34
|
+
./spec/pec_ruby/client_spec.rb[1:1:2] | passed | 0.00003 seconds |
|
35
|
+
./spec/pec_ruby/client_spec.rb[1:1:3] | passed | 0.00028 seconds |
|
36
|
+
./spec/pec_ruby/client_spec.rb[1:2:1:1] | passed | 0.00003 seconds |
|
37
|
+
./spec/pec_ruby/client_spec.rb[1:2:2:1] | passed | 0.00008 seconds |
|
38
|
+
./spec/pec_ruby/client_spec.rb[1:2:3:1] | passed | 0.00007 seconds |
|
39
|
+
./spec/pec_ruby/client_spec.rb[1:3:1] | passed | 0.00021 seconds |
|
40
|
+
./spec/pec_ruby/client_spec.rb[1:3:2] | passed | 0.00036 seconds |
|
41
|
+
./spec/pec_ruby/client_spec.rb[1:3:3] | passed | 0.00028 seconds |
|
42
|
+
./spec/pec_ruby/client_spec.rb[1:3:4] | passed | 0.00018 seconds |
|
43
|
+
./spec/pec_ruby/client_spec.rb[1:3:5:1] | passed | 0.0002 seconds |
|
44
|
+
./spec/pec_ruby/client_spec.rb[1:3:6:1] | passed | 0.0003 seconds |
|
45
|
+
./spec/pec_ruby/client_spec.rb[1:4:1:1] | passed | 0.00015 seconds |
|
46
|
+
./spec/pec_ruby/client_spec.rb[1:4:1:2] | passed | 0.00013 seconds |
|
47
|
+
./spec/pec_ruby/client_spec.rb[1:4:2:1] | passed | 0.00004 seconds |
|
48
|
+
./spec/pec_ruby/client_spec.rb[1:4:3:1] | passed | 0.00015 seconds |
|
49
|
+
./spec/pec_ruby/client_spec.rb[1:5:1:1] | passed | 0.0001 seconds |
|
50
|
+
./spec/pec_ruby/client_spec.rb[1:5:2:1] | passed | 0.00032 seconds |
|
51
|
+
./spec/pec_ruby/client_spec.rb[1:5:2:2] | passed | 0.00022 seconds |
|
52
|
+
./spec/pec_ruby/client_spec.rb[1:5:2:3] | passed | 0.00021 seconds |
|
53
|
+
./spec/pec_ruby/client_spec.rb[1:5:2:4] | passed | 0.00108 seconds |
|
54
|
+
./spec/pec_ruby/client_spec.rb[1:6:1:1] | passed | 0.00013 seconds |
|
55
|
+
./spec/pec_ruby/client_spec.rb[1:6:2:1] | passed | 0.00181 seconds |
|
56
|
+
./spec/pec_ruby/client_spec.rb[1:6:2:2] | passed | 0.00269 seconds |
|
57
|
+
./spec/pec_ruby/client_spec.rb[1:6:3:1] | passed | 0.00018 seconds |
|
58
|
+
./spec/pec_ruby/message_spec.rb[1:1:1] | passed | 0.0001 seconds |
|
59
|
+
./spec/pec_ruby/message_spec.rb[1:2:1:1] | passed | 0.0001 seconds |
|
60
|
+
./spec/pec_ruby/message_spec.rb[1:2:2:1] | passed | 0.00029 seconds |
|
61
|
+
./spec/pec_ruby/message_spec.rb[1:2:2:2:1] | passed | 0.00016 seconds |
|
62
|
+
./spec/pec_ruby/message_spec.rb[1:3:1:1] | passed | 0.00009 seconds |
|
63
|
+
./spec/pec_ruby/message_spec.rb[1:3:2:1] | passed | 0.00025 seconds |
|
64
|
+
./spec/pec_ruby/message_spec.rb[1:3:2:2:1] | passed | 0.00012 seconds |
|
65
|
+
./spec/pec_ruby/message_spec.rb[1:4:1:1] | passed | 0.00009 seconds |
|
66
|
+
./spec/pec_ruby/message_spec.rb[1:4:2:1] | passed | 0.00013 seconds |
|
67
|
+
./spec/pec_ruby/message_spec.rb[1:5:1:1] | passed | 0.00018 seconds |
|
68
|
+
./spec/pec_ruby/message_spec.rb[1:5:2:1] | passed | 0.00029 seconds |
|
69
|
+
./spec/pec_ruby/message_spec.rb[1:6:1:1] | passed | 0.00011 seconds |
|
70
|
+
./spec/pec_ruby/message_spec.rb[1:6:2:1] | passed | 0.0001 seconds |
|
71
|
+
./spec/pec_ruby/message_spec.rb[1:7:1:1] | passed | 0.00019 seconds |
|
72
|
+
./spec/pec_ruby/message_spec.rb[1:7:1:2] | passed | 0.00013 seconds |
|
73
|
+
./spec/pec_ruby/message_spec.rb[1:7:2:1] | passed | 0.00026 seconds |
|
74
|
+
./spec/pec_ruby/message_spec.rb[1:7:2:2] | passed | 0.00024 seconds |
|
75
|
+
./spec/pec_ruby/message_spec.rb[1:7:3:1] | passed | 0.00017 seconds |
|
76
|
+
./spec/pec_ruby/message_spec.rb[1:8:1:1] | passed | 0.00011 seconds |
|
77
|
+
./spec/pec_ruby/message_spec.rb[1:8:2:1] | passed | 0.00012 seconds |
|
78
|
+
./spec/pec_ruby/message_spec.rb[1:9:1:1] | passed | 0.0001 seconds |
|
79
|
+
./spec/pec_ruby/message_spec.rb[1:9:2:1] | passed | 0.00017 seconds |
|
80
|
+
./spec/pec_ruby/message_spec.rb[1:10:1:1] | passed | 0.0001 seconds |
|
81
|
+
./spec/pec_ruby/message_spec.rb[1:10:2:1] | passed | 0.00129 seconds |
|
82
|
+
./spec/pec_ruby/message_spec.rb[1:10:3:1] | passed | 0.00108 seconds |
|
83
|
+
./spec/pec_ruby/message_spec.rb[1:11:1:1] | passed | 0.00026 seconds |
|
84
|
+
./spec/pec_ruby/message_spec.rb[1:11:2:1] | passed | 0.00015 seconds |
|
85
|
+
./spec/pec_ruby/message_spec.rb[1:12:1:1] | passed | 0.00024 seconds |
|
86
|
+
./spec/pec_ruby/message_spec.rb[1:12:2:1] | passed | 0.00014 seconds |
|
87
|
+
./spec/pec_ruby/message_spec.rb[1:13:1] | passed | 0.00016 seconds |
|
88
|
+
./spec/pec_ruby/message_spec.rb[1:14:1] | passed | 0.00015 seconds |
|
89
|
+
./spec/pec_ruby/message_spec.rb[1:15:1:1] | passed | 0.0001 seconds |
|
90
|
+
./spec/pec_ruby/message_spec.rb[1:15:2:1] | passed | 0.0001 seconds |
|
91
|
+
./spec/pec_ruby/message_spec.rb[1:16:1] | passed | 0.00015 seconds |
|
92
|
+
./spec/pec_ruby/message_spec.rb[1:17:1] | passed | 0.00018 seconds |
|
93
|
+
./spec/pec_ruby/message_spec.rb[1:18:1] | passed | 0.00057 seconds |
|
94
|
+
./spec/pec_ruby_spec.rb[1:1] | passed | 0.00004 seconds |
|
95
|
+
./spec/pec_ruby_spec.rb[1:2:1] | passed | 0.00005 seconds |
|
96
|
+
./spec/pec_ruby_spec.rb[1:2:2] | passed | 0.00003 seconds |
|
97
|
+
./spec/pec_ruby_spec.rb[1:2:3] | passed | 0.00003 seconds |
|
98
|
+
./spec/pec_ruby_spec.rb[1:2:4] | passed | 0.00002 seconds |
|
99
|
+
./spec/pec_ruby_spec.rb[1:2:5] | passed | 0.00002 seconds |
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.2.0] - 2025-07-13
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- **Breaking Change**: `original_body` now returns a hash with format information instead of plain text
|
14
|
+
- Hash contains: `content`, `content_type`, and `charset` keys
|
15
|
+
- Allows proper handling of HTML vs plain text content
|
16
|
+
- Preserves original formatting for correct display
|
17
|
+
|
18
|
+
### Added
|
19
|
+
- `original_body_text` method for getting plain text content only
|
20
|
+
- `original_body_html` method for getting HTML content only
|
21
|
+
- Enhanced body format detection and handling
|
22
|
+
- Better charset handling for international content
|
23
|
+
- **Nested postacert.eml support** for handling forwarded PECs
|
24
|
+
- `Attachment#postacert?` to detect postacert.eml attachments
|
25
|
+
- `Attachment#as_postacert_message` to parse nested PECs
|
26
|
+
- `Message#nested_postacerts` to get nested postacert attachments
|
27
|
+
- `Message#original_regular_attachments` to get non-postacert attachments
|
28
|
+
- `Message#has_nested_postacerts?` to check for nested PECs
|
29
|
+
- `Message#nested_postacert_messages` to get parsed nested messages
|
30
|
+
- `Message#all_postacert_messages` for hierarchical view of all messages
|
31
|
+
- `PecRuby::NestedPostacertMessage` class for nested postacert handling
|
32
|
+
- Full API compatibility with original message methods
|
33
|
+
- Support for multi-level nesting (postacert within postacert)
|
34
|
+
- Automatic detection and parsing of deeper nesting levels
|
35
|
+
|
10
36
|
## [0.1.0] - 2025-07-13
|
11
37
|
|
12
38
|
### Added
|
@@ -33,5 +59,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
33
59
|
- Aruba PEC (imaps.pec.aruba.it)
|
34
60
|
- Generic IMAP-compliant PEC providers
|
35
61
|
|
36
|
-
[Unreleased]: https://github.com/egio12/pec_ruby/compare/v0.
|
62
|
+
[Unreleased]: https://github.com/egio12/pec_ruby/compare/v0.2.0...HEAD
|
63
|
+
[0.2.0]: https://github.com/egio12/pec_ruby/compare/v0.1.0...v0.2.0
|
37
64
|
[0.1.0]: https://github.com/egio12/pec_ruby/releases/tag/v0.1.0
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pec_ruby (0.2.0)
|
5
|
+
mail (~> 2.7)
|
6
|
+
net-imap (~> 0.3)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
ast (2.4.3)
|
12
|
+
date (3.4.1)
|
13
|
+
diff-lcs (1.6.2)
|
14
|
+
json (2.12.2)
|
15
|
+
language_server-protocol (3.17.0.5)
|
16
|
+
lint_roller (1.1.0)
|
17
|
+
mail (2.8.1)
|
18
|
+
mini_mime (>= 0.1.1)
|
19
|
+
net-imap
|
20
|
+
net-pop
|
21
|
+
net-smtp
|
22
|
+
mini_mime (1.1.5)
|
23
|
+
net-imap (0.5.9)
|
24
|
+
date
|
25
|
+
net-protocol
|
26
|
+
net-pop (0.1.2)
|
27
|
+
net-protocol
|
28
|
+
net-protocol (0.2.2)
|
29
|
+
timeout
|
30
|
+
net-smtp (0.5.1)
|
31
|
+
net-protocol
|
32
|
+
parallel (1.27.0)
|
33
|
+
parser (3.3.8.0)
|
34
|
+
ast (~> 2.4.1)
|
35
|
+
racc
|
36
|
+
prism (1.4.0)
|
37
|
+
racc (1.8.1)
|
38
|
+
rainbow (3.1.1)
|
39
|
+
rake (13.3.0)
|
40
|
+
regexp_parser (2.10.0)
|
41
|
+
rspec (3.13.1)
|
42
|
+
rspec-core (~> 3.13.0)
|
43
|
+
rspec-expectations (~> 3.13.0)
|
44
|
+
rspec-mocks (~> 3.13.0)
|
45
|
+
rspec-core (3.13.5)
|
46
|
+
rspec-support (~> 3.13.0)
|
47
|
+
rspec-expectations (3.13.5)
|
48
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
+
rspec-support (~> 3.13.0)
|
50
|
+
rspec-mocks (3.13.5)
|
51
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
52
|
+
rspec-support (~> 3.13.0)
|
53
|
+
rspec-support (3.13.4)
|
54
|
+
rubocop (1.78.0)
|
55
|
+
json (~> 2.3)
|
56
|
+
language_server-protocol (~> 3.17.0.2)
|
57
|
+
lint_roller (~> 1.1.0)
|
58
|
+
parallel (~> 1.10)
|
59
|
+
parser (>= 3.3.0.2)
|
60
|
+
rainbow (>= 2.2.2, < 4.0)
|
61
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
62
|
+
rubocop-ast (>= 1.45.1, < 2.0)
|
63
|
+
ruby-progressbar (~> 1.7)
|
64
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
65
|
+
rubocop-ast (1.45.1)
|
66
|
+
parser (>= 3.3.7.2)
|
67
|
+
prism (~> 1.4)
|
68
|
+
ruby-progressbar (1.13.0)
|
69
|
+
timeout (0.4.3)
|
70
|
+
unicode-display_width (3.1.4)
|
71
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
72
|
+
unicode-emoji (4.0.4)
|
73
|
+
|
74
|
+
PLATFORMS
|
75
|
+
arm64-darwin-24
|
76
|
+
|
77
|
+
DEPENDENCIES
|
78
|
+
pec_ruby!
|
79
|
+
rake (~> 13.0)
|
80
|
+
rspec (~> 3.0)
|
81
|
+
rubocop (~> 1.21)
|
82
|
+
|
83
|
+
BUNDLED WITH
|
84
|
+
2.4.19
|
data/README.md
CHANGED
@@ -101,16 +101,33 @@ puts message.date # PEC message date
|
|
101
101
|
# Original message information
|
102
102
|
puts message.original_subject # Original subject
|
103
103
|
puts message.original_from # Original sender
|
104
|
-
|
104
|
+
body_info = message.original_body # Original message body with format info
|
105
105
|
|
106
106
|
# Attachments
|
107
107
|
message.original_attachments.each do |attachment|
|
108
108
|
puts "#{attachment.filename} (#{attachment.size_kb} KB)"
|
109
109
|
|
110
|
-
#
|
111
|
-
attachment.
|
112
|
-
|
113
|
-
|
110
|
+
# Check if attachment is a nested postacert.eml (forwarded PEC)
|
111
|
+
if attachment.postacert?
|
112
|
+
puts " -> This is a nested postacert.eml!"
|
113
|
+
nested_msg = attachment.as_postacert_message
|
114
|
+
puts " -> Original subject: #{nested_msg.subject}"
|
115
|
+
puts " -> Original from: #{nested_msg.from}"
|
116
|
+
else
|
117
|
+
# Save regular attachment
|
118
|
+
attachment.save_to("/path/to/file.pdf")
|
119
|
+
# or
|
120
|
+
attachment.save_to_dir("/downloads/")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Handle nested postacerts (forwarded PECs)
|
125
|
+
if message.has_nested_postacerts?
|
126
|
+
puts "This message contains #{message.nested_postacerts.size} forwarded PEC(s)"
|
127
|
+
|
128
|
+
message.nested_postacert_messages.each do |nested_msg|
|
129
|
+
puts "Nested PEC: #{nested_msg.subject} from #{nested_msg.from}"
|
130
|
+
end
|
114
131
|
end
|
115
132
|
```
|
116
133
|
|
@@ -215,14 +232,52 @@ message.original_subject # String: Original subject
|
|
215
232
|
message.original_from # String: Original sender
|
216
233
|
message.original_to # Array<String>: Original recipients
|
217
234
|
message.original_date # Time: Original message date
|
218
|
-
message.original_body #
|
235
|
+
message.original_body # Hash: Original message body with format info
|
236
|
+
message.original_body_text # String: Plain text body only
|
237
|
+
message.original_body_html # String: HTML body only
|
238
|
+
```
|
239
|
+
|
240
|
+
##### Original Message Body
|
241
|
+
|
242
|
+
The `original_body` method returns a hash with format information, allowing you to handle different content types appropriately:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
body_info = message.original_body
|
246
|
+
if body_info
|
247
|
+
puts "Content type: #{body_info[:content_type]}"
|
248
|
+
puts "Charset: #{body_info[:charset]}"
|
249
|
+
|
250
|
+
case body_info[:content_type]
|
251
|
+
when 'text/html'
|
252
|
+
# Handle HTML content - preserve formatting for web display
|
253
|
+
html_content = body_info[:content]
|
254
|
+
# You can now render this in a web browser or HTML viewer
|
255
|
+
when 'text/plain'
|
256
|
+
# Handle plain text content
|
257
|
+
text_content = body_info[:content]
|
258
|
+
puts text_content
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Or use convenience methods for specific formats
|
263
|
+
text_only = message.original_body_text # Returns nil if no text/plain part
|
264
|
+
html_only = message.original_body_html # Returns nil if no text/html part
|
219
265
|
```
|
220
266
|
|
221
267
|
##### Attachments
|
222
268
|
|
223
269
|
```ruby
|
224
270
|
# Get original message attachments
|
225
|
-
message.original_attachments
|
271
|
+
message.original_attachments # Array<PecRuby::Attachment> - All attachments
|
272
|
+
message.original_regular_attachments # Array<PecRuby::Attachment> - Non-postacert attachments only
|
273
|
+
message.nested_postacerts # Array<PecRuby::Attachment> - Nested postacert.eml files only
|
274
|
+
|
275
|
+
# Check for nested postacerts (forwarded PECs)
|
276
|
+
message.has_nested_postacerts? # Boolean
|
277
|
+
message.nested_postacert_messages # Array<PecRuby::NestedPostacertMessage>
|
278
|
+
|
279
|
+
# Get all postacert messages in a flattened structure
|
280
|
+
message.all_postacert_messages # Array<Hash> - Hierarchical view of all messages
|
226
281
|
```
|
227
282
|
|
228
283
|
##### Summary Information
|
@@ -254,11 +309,42 @@ attachment.content # String: Raw binary content
|
|
254
309
|
attachment.save_to(path) # Save to specific path
|
255
310
|
attachment.save_to_dir(directory) # Save to directory with original filename
|
256
311
|
|
312
|
+
# Nested postacert detection and parsing
|
313
|
+
attachment.postacert? # Boolean: Check if this is a postacert.eml
|
314
|
+
attachment.as_postacert_message # PecRuby::NestedPostacertMessage: Parse as nested PEC
|
315
|
+
|
257
316
|
# Summary
|
258
317
|
attachment.summary # Hash: Complete attachment information
|
259
318
|
attachment.to_s # String: Human-readable description
|
260
319
|
```
|
261
320
|
|
321
|
+
### PecRuby::NestedPostacertMessage
|
322
|
+
|
323
|
+
Represents a nested postacert.eml file (forwarded PEC) found within attachments.
|
324
|
+
|
325
|
+
#### Instance Methods
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
# Basic message information
|
329
|
+
nested_msg.subject # String: Subject of the nested message
|
330
|
+
nested_msg.from # String: Sender of the nested message
|
331
|
+
nested_msg.to # Array<String>: Recipients of the nested message
|
332
|
+
nested_msg.date # Time: Date of the nested message
|
333
|
+
|
334
|
+
# Body content (same API as original_body)
|
335
|
+
nested_msg.body # Hash: Body with content_type and charset info
|
336
|
+
nested_msg.body_text # String: Plain text body only
|
337
|
+
nested_msg.body_html # String: HTML body only
|
338
|
+
|
339
|
+
# Nested attachments
|
340
|
+
nested_msg.attachments # Array<PecRuby::Attachment>
|
341
|
+
nested_msg.nested_postacerts # Array<PecRuby::Attachment> - Even deeper nesting!
|
342
|
+
nested_msg.has_nested_postacerts? # Boolean: Check for deeper nesting
|
343
|
+
|
344
|
+
# Summary
|
345
|
+
nested_msg.summary # Hash: Complete nested message information
|
346
|
+
```
|
347
|
+
|
262
348
|
## Complete Example
|
263
349
|
|
264
350
|
```ruby
|
@@ -279,14 +365,56 @@ begin
|
|
279
365
|
pec_messages.each do |message|
|
280
366
|
puts "Subject: #{message.original_subject}"
|
281
367
|
puts "From: #{message.original_from}"
|
282
|
-
puts "
|
368
|
+
puts "Total attachments: #{message.original_attachments.size}"
|
369
|
+
puts "Regular attachments: #{message.original_regular_attachments.size}"
|
370
|
+
puts "Nested PECs: #{message.nested_postacerts.size}"
|
371
|
+
|
372
|
+
# Handle message body based on format
|
373
|
+
body_info = message.original_body
|
374
|
+
if body_info
|
375
|
+
puts "Body format: #{body_info[:content_type]}"
|
376
|
+
case body_info[:content_type]
|
377
|
+
when 'text/html'
|
378
|
+
puts "HTML content available for web display"
|
379
|
+
# Save HTML to file for viewing
|
380
|
+
File.write("./downloads/message_#{message.uid}.html", body_info[:content])
|
381
|
+
when 'text/plain'
|
382
|
+
puts "Text content:"
|
383
|
+
puts body_info[:content][0..100] + "..." # First 100 chars
|
384
|
+
end
|
385
|
+
end
|
283
386
|
|
284
|
-
# Download attachments
|
285
|
-
message.
|
387
|
+
# Download regular attachments
|
388
|
+
message.original_regular_attachments.each do |attachment|
|
286
389
|
attachment.save_to_dir('./downloads')
|
287
390
|
puts "Downloaded: #{attachment.filename}"
|
288
391
|
end
|
289
392
|
|
393
|
+
# Handle nested postacerts (forwarded PECs)
|
394
|
+
if message.has_nested_postacerts?
|
395
|
+
puts "Found #{message.nested_postacerts.size} forwarded PEC(s):"
|
396
|
+
|
397
|
+
message.nested_postacert_messages.each_with_index do |nested_msg, index|
|
398
|
+
puts " Nested PEC ##{index + 1}:"
|
399
|
+
puts " Subject: #{nested_msg.subject}"
|
400
|
+
puts " From: #{nested_msg.from}"
|
401
|
+
puts " Attachments: #{nested_msg.attachments.size}"
|
402
|
+
|
403
|
+
# Download nested PEC attachments
|
404
|
+
nested_msg.attachments.each do |nested_attachment|
|
405
|
+
unless nested_attachment.postacert? # Avoid infinite recursion
|
406
|
+
nested_attachment.save_to_dir('./downloads/nested')
|
407
|
+
puts " Downloaded nested: #{nested_attachment.filename}"
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# Check for even deeper nesting
|
412
|
+
if nested_msg.has_nested_postacerts?
|
413
|
+
puts " -> This nested PEC contains #{nested_msg.nested_postacerts.size} more nested PEC(s)!"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
290
418
|
puts "─" * 40
|
291
419
|
end
|
292
420
|
|
@@ -356,9 +484,3 @@ bundle exec rubocop # Check code style
|
|
356
484
|
## License
|
357
485
|
|
358
486
|
Distributed under the MIT License. See `LICENSE` for more information.
|
359
|
-
|
360
|
-
## Contact
|
361
|
-
|
362
|
-
Enrico Giordano - enricomaria.giordano@icloud.com
|
363
|
-
|
364
|
-
Project Link: [https://github.com/egio12/pec_ruby](https://github.com/egio12/pec_ruby)
|
data/Rakefile
ADDED
data/lib/pec_ruby/attachment.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'mail'
|
4
|
+
|
3
5
|
module PecRuby
|
4
6
|
class Attachment
|
5
7
|
attr_reader :mail_attachment
|
@@ -57,5 +59,142 @@ module PecRuby
|
|
57
59
|
def to_s
|
58
60
|
"#{filename} (#{mime_type}, #{size_kb} KB)"
|
59
61
|
end
|
62
|
+
|
63
|
+
# Check if this attachment is a postacert.eml file
|
64
|
+
def postacert?
|
65
|
+
filename&.downcase&.include?('postacert.eml') ||
|
66
|
+
(filename&.downcase&.end_with?('.eml') && mime_type&.include?('message'))
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parse this attachment as a postacert.eml if it is one
|
70
|
+
# Returns a PecRuby::Message-like object for the nested postacert
|
71
|
+
def as_postacert_message
|
72
|
+
return nil unless postacert?
|
73
|
+
|
74
|
+
begin
|
75
|
+
# Parse the attachment content as an email message
|
76
|
+
nested_mail = Mail.read_from_string(content)
|
77
|
+
|
78
|
+
# Create a simplified message object for the nested postacert
|
79
|
+
NestedPostacertMessage.new(nested_mail)
|
80
|
+
rescue => e
|
81
|
+
raise PecRuby::Error, "Failed to parse nested postacert.eml: #{e.message}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Simplified message class for nested postacert emails
|
87
|
+
class NestedPostacertMessage
|
88
|
+
attr_reader :mail
|
89
|
+
|
90
|
+
def initialize(mail)
|
91
|
+
@mail = mail
|
92
|
+
end
|
93
|
+
|
94
|
+
def subject
|
95
|
+
@mail.subject
|
96
|
+
end
|
97
|
+
|
98
|
+
def from
|
99
|
+
@mail.from&.first
|
100
|
+
end
|
101
|
+
|
102
|
+
def to
|
103
|
+
@mail.to || []
|
104
|
+
end
|
105
|
+
|
106
|
+
def date
|
107
|
+
@mail.date
|
108
|
+
end
|
109
|
+
|
110
|
+
def body
|
111
|
+
# Try to get text/plain first, then text/html
|
112
|
+
text_part = extract_text_part(@mail, "text/plain")
|
113
|
+
html_part = extract_text_part(@mail, "text/html")
|
114
|
+
selected_part = text_part || html_part
|
115
|
+
|
116
|
+
return nil unless selected_part
|
117
|
+
|
118
|
+
raw_body = selected_part.body.decoded
|
119
|
+
charset = selected_part.charset ||
|
120
|
+
selected_part.content_type_parameters&.[]("charset") ||
|
121
|
+
"UTF-8"
|
122
|
+
|
123
|
+
content = raw_body.dup.force_encoding(charset).encode("UTF-8")
|
124
|
+
|
125
|
+
{
|
126
|
+
content: content,
|
127
|
+
content_type: selected_part.mime_type,
|
128
|
+
charset: charset
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def body_text
|
133
|
+
text_part = extract_text_part(@mail, "text/plain")
|
134
|
+
return nil unless text_part
|
135
|
+
|
136
|
+
raw_body = text_part.body.decoded
|
137
|
+
charset = text_part.charset ||
|
138
|
+
text_part.content_type_parameters&.[]("charset") ||
|
139
|
+
"UTF-8"
|
140
|
+
|
141
|
+
raw_body.dup.force_encoding(charset).encode("UTF-8")
|
142
|
+
end
|
143
|
+
|
144
|
+
def body_html
|
145
|
+
html_part = extract_text_part(@mail, "text/html")
|
146
|
+
return nil unless html_part
|
147
|
+
|
148
|
+
raw_body = html_part.body.decoded
|
149
|
+
charset = html_part.charset ||
|
150
|
+
html_part.content_type_parameters&.[]("charset") ||
|
151
|
+
"UTF-8"
|
152
|
+
|
153
|
+
raw_body.dup.force_encoding(charset).encode("UTF-8")
|
154
|
+
end
|
155
|
+
|
156
|
+
def attachments
|
157
|
+
return [] unless @mail&.attachments
|
158
|
+
|
159
|
+
@mail.attachments.map { |att| Attachment.new(att) }
|
160
|
+
end
|
161
|
+
|
162
|
+
def summary
|
163
|
+
{
|
164
|
+
subject: subject,
|
165
|
+
from: from,
|
166
|
+
to: to,
|
167
|
+
date: date,
|
168
|
+
attachments_count: attachments.size,
|
169
|
+
nested_postacerts_count: nested_postacerts.size
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
# Find any nested postacert.eml files in this message's attachments
|
174
|
+
def nested_postacerts
|
175
|
+
attachments.select(&:postacert?)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Check if this nested message has any nested postacert.eml files
|
179
|
+
def has_nested_postacerts?
|
180
|
+
!nested_postacerts.empty?
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def extract_text_part(mail, preferred_type = "text/plain")
|
186
|
+
return mail unless mail.multipart?
|
187
|
+
|
188
|
+
mail.parts.each do |part|
|
189
|
+
if part.multipart?
|
190
|
+
found = extract_text_part(part, preferred_type)
|
191
|
+
return found if found
|
192
|
+
elsif part.mime_type == preferred_type
|
193
|
+
return part
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
nil
|
198
|
+
end
|
60
199
|
end
|
61
200
|
end
|
data/lib/pec_ruby/client.rb
CHANGED
data/lib/pec_ruby/message.rb
CHANGED
@@ -20,7 +20,7 @@ module PecRuby
|
|
20
20
|
return nil unless @envelope.subject
|
21
21
|
|
22
22
|
decoded = Mail::Encodings.value_decode(@envelope.subject)
|
23
|
-
decoded.gsub
|
23
|
+
decoded = decoded.gsub("POSTA CERTIFICATA:", "") if decoded.start_with?("POSTA CERTIFICATA:")
|
24
24
|
decoded.strip
|
25
25
|
end
|
26
26
|
|
@@ -89,13 +89,15 @@ module PecRuby
|
|
89
89
|
postacert_message&.date
|
90
90
|
end
|
91
91
|
|
92
|
-
# Get original message body
|
92
|
+
# Get original message body with format information
|
93
93
|
def original_body
|
94
94
|
mail = postacert_message
|
95
95
|
return nil unless mail
|
96
96
|
|
97
97
|
text_part = extract_text_part(mail, "text/plain")
|
98
98
|
html_part = extract_text_part(mail, "text/html")
|
99
|
+
|
100
|
+
# Prefer text/plain, but return HTML if that's all we have
|
99
101
|
selected_part = text_part || html_part
|
100
102
|
|
101
103
|
return nil unless selected_part
|
@@ -105,7 +107,45 @@ module PecRuby
|
|
105
107
|
selected_part.content_type_parameters&.[]("charset") ||
|
106
108
|
"UTF-8"
|
107
109
|
|
108
|
-
raw_body.force_encoding(charset).encode("UTF-8")
|
110
|
+
content = raw_body.dup.force_encoding(charset).encode("UTF-8")
|
111
|
+
|
112
|
+
{
|
113
|
+
content: content,
|
114
|
+
content_type: selected_part.mime_type,
|
115
|
+
charset: charset
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get original message body as plain text only
|
120
|
+
def original_body_text
|
121
|
+
mail = postacert_message
|
122
|
+
return nil unless mail
|
123
|
+
|
124
|
+
text_part = extract_text_part(mail, "text/plain")
|
125
|
+
return nil unless text_part
|
126
|
+
|
127
|
+
raw_body = text_part.body.decoded
|
128
|
+
charset = text_part.charset ||
|
129
|
+
text_part.content_type_parameters&.[]("charset") ||
|
130
|
+
"UTF-8"
|
131
|
+
|
132
|
+
raw_body.dup.force_encoding(charset).encode("UTF-8")
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get original message body as HTML only
|
136
|
+
def original_body_html
|
137
|
+
mail = postacert_message
|
138
|
+
return nil unless mail
|
139
|
+
|
140
|
+
html_part = extract_text_part(mail, "text/html")
|
141
|
+
return nil unless html_part
|
142
|
+
|
143
|
+
raw_body = html_part.body.decoded
|
144
|
+
charset = html_part.charset ||
|
145
|
+
html_part.content_type_parameters&.[]("charset") ||
|
146
|
+
"UTF-8"
|
147
|
+
|
148
|
+
raw_body.dup.force_encoding(charset).encode("UTF-8")
|
109
149
|
end
|
110
150
|
|
111
151
|
# Get original message attachments
|
@@ -116,6 +156,69 @@ module PecRuby
|
|
116
156
|
mail.attachments.map { |att| Attachment.new(att) }
|
117
157
|
end
|
118
158
|
|
159
|
+
# Get original message attachments that are NOT postacert.eml files
|
160
|
+
def original_regular_attachments
|
161
|
+
original_attachments.reject(&:postacert?)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Get nested postacert.eml files from original message attachments
|
165
|
+
def nested_postacerts
|
166
|
+
original_attachments.select(&:postacert?)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Check if original message has nested postacert.eml files
|
170
|
+
def has_nested_postacerts?
|
171
|
+
!nested_postacerts.empty?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Get all nested postacert messages parsed and ready to use
|
175
|
+
def nested_postacert_messages
|
176
|
+
nested_postacerts.map(&:as_postacert_message).compact
|
177
|
+
end
|
178
|
+
|
179
|
+
# Get a flattened view of all postacert messages (original + nested)
|
180
|
+
# Returns array with the original message first, followed by nested ones
|
181
|
+
def all_postacert_messages
|
182
|
+
messages = []
|
183
|
+
|
184
|
+
# Add the main postacert message (this message)
|
185
|
+
if has_postacert?
|
186
|
+
messages << {
|
187
|
+
level: 0,
|
188
|
+
message: self,
|
189
|
+
type: :main_postacert
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
# Add nested postacert messages
|
194
|
+
nested_postacert_messages.each_with_index do |nested_msg, index|
|
195
|
+
messages << {
|
196
|
+
level: 1,
|
197
|
+
message: nested_msg,
|
198
|
+
type: :nested_postacert,
|
199
|
+
index: index
|
200
|
+
}
|
201
|
+
|
202
|
+
# Check for deeper nesting (postacert within postacert within postacert)
|
203
|
+
if nested_msg.has_nested_postacerts?
|
204
|
+
nested_msg.nested_postacerts.each_with_index do |deep_nested, deep_index|
|
205
|
+
deep_nested_msg = deep_nested.as_postacert_message
|
206
|
+
if deep_nested_msg
|
207
|
+
messages << {
|
208
|
+
level: 2,
|
209
|
+
message: deep_nested_msg,
|
210
|
+
type: :deep_nested_postacert,
|
211
|
+
parent_index: index,
|
212
|
+
index: deep_index
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
messages
|
220
|
+
end
|
221
|
+
|
119
222
|
# Summary information
|
120
223
|
def summary
|
121
224
|
{
|
@@ -129,7 +232,11 @@ module PecRuby
|
|
129
232
|
original_from: original_from,
|
130
233
|
original_to: original_to,
|
131
234
|
original_date: original_date,
|
132
|
-
attachments_count: original_attachments.size
|
235
|
+
attachments_count: original_attachments.size,
|
236
|
+
regular_attachments_count: original_regular_attachments.size,
|
237
|
+
nested_postacerts_count: nested_postacerts.size,
|
238
|
+
has_nested_postacerts: has_nested_postacerts?,
|
239
|
+
total_postacert_messages: all_postacert_messages.size
|
133
240
|
}
|
134
241
|
end
|
135
242
|
|
data/lib/pec_ruby/version.rb
CHANGED
data/pec_ruby.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/pec_ruby/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "pec_ruby"
|
7
|
+
spec.version = PecRuby::VERSION
|
8
|
+
spec.authors = ["EMG"]
|
9
|
+
spec.email = ["enricomaria.giordano@icloud.com"]
|
10
|
+
|
11
|
+
spec.summary = "Ruby gem for decoding and reading Italian PEC (Posta Elettronica Certificata) emails"
|
12
|
+
spec.description = "A comprehensive Ruby library for handling Italian certified email (PEC) messages. Includes methods for extracting postacert.eml contents, decoding attachments, and a CLI for exploring PEC messages."
|
13
|
+
spec.homepage = "https://github.com/egio12/pec_ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/egio12/pec_ruby"
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/egio12/pec_ruby/blob/main/CHANGELOG.md"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
24
|
+
`git ls-files -z 2>/dev/null`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "bin"
|
29
|
+
spec.executables = ["pec_ruby"]
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
# Core dependencies
|
33
|
+
spec.add_dependency "mail", "~> 2.7"
|
34
|
+
spec.add_dependency "net-imap", "~> 0.3"
|
35
|
+
|
36
|
+
# Development dependencies
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
38
|
+
spec.add_development_dependency "rubocop", "~> 1.0"
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pec_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- EMG
|
@@ -76,10 +76,14 @@ executables:
|
|
76
76
|
extensions: []
|
77
77
|
extra_rdoc_files: []
|
78
78
|
files:
|
79
|
+
- ".rspec"
|
80
|
+
- ".rspec_status"
|
79
81
|
- CHANGELOG.md
|
80
82
|
- Gemfile
|
83
|
+
- Gemfile.lock
|
81
84
|
- LICENSE
|
82
85
|
- README.md
|
86
|
+
- Rakefile
|
83
87
|
- bin/pec_ruby
|
84
88
|
- lib/pec_ruby.rb
|
85
89
|
- lib/pec_ruby/attachment.rb
|
@@ -87,6 +91,7 @@ files:
|
|
87
91
|
- lib/pec_ruby/client.rb
|
88
92
|
- lib/pec_ruby/message.rb
|
89
93
|
- lib/pec_ruby/version.rb
|
94
|
+
- pec_ruby.gemspec
|
90
95
|
homepage: https://github.com/egio12/pec_ruby
|
91
96
|
licenses:
|
92
97
|
- MIT
|