1010
1111Uses the common mail iteration method from the lib file.
1212"""
13+ from functools import cache , cached_property
1314import re
15+
1416from intelmq .lib .utils import unzip
15- from intelmq .lib .exceptions import InvalidArgument
17+ from intelmq .lib .exceptions import InvalidArgument , MissingDependencyError
1618
1719from ._lib import MailCollectorBot
1820
1921
2022class MailAttachCollectorBot (MailCollectorBot ):
2123 """Monitor IMAP mailboxes and retrieve mail attachments"""
24+
2225 attach_regex : str = "csv.zip"
2326 extract_files : bool = True
2427 folder : str = "INBOX"
@@ -28,11 +31,16 @@ class MailAttachCollectorBot(MailCollectorBot):
2831 mail_user : str = "<user>"
2932 rate_limit : int = 60
3033 subject_regex : str = "<subject>"
34+ decrypt : bool = False # Decrypt the attachment with GPG
35+
36+ gpg_passphrase : str = "" # The private key passhrase
37+
38+ gpg_home : str = "" # Change the GPG home directory.
3139
3240 def init (self ):
3341 super ().init ()
3442 if self .attach_regex is None :
35- raise InvalidArgument (' attach_regex' , expected = ' string' )
43+ raise InvalidArgument (" attach_regex" , expected = " string" )
3644
3745 def process_message (self , uid , message ):
3846 seen = False
@@ -42,12 +50,14 @@ def process_message(self, uid, message):
4250 continue
4351
4452 try :
45- attach_filename = attach [' filename' ]
53+ attach_filename = attach [" filename" ]
4654 except KeyError :
4755 # https://github.com/certtools/intelmq/issues/1538
48- self .logger .debug (' Skipping attachment because of missing filename.' )
56+ self .logger .debug (" Skipping attachment because of missing filename." )
4957 continue
50- if attach_filename .startswith ('"' ): # for imbox versions older than 0.9.5, see also above
58+ if attach_filename .startswith (
59+ '"'
60+ ): # for imbox versions older than 0.9.5, see also above
5161 attach_filename = attach_filename [1 :- 1 ]
5262
5363 if re .search (self .attach_regex , attach_filename ):
@@ -57,18 +67,33 @@ def process_message(self, uid, message):
5767 report = self .new_report ()
5868
5969 if self .extract_files :
60- raw_reports = unzip (attach ['content' ].read (), self .extract_files ,
61- return_names = True , logger = self .logger )
70+ raw_reports = unzip (
71+ attach ["content" ].read (),
72+ self .extract_files ,
73+ return_names = True ,
74+ logger = self .logger ,
75+ )
6276 else :
63- raw_reports = ((attach_filename , attach [' content' ].read ()), )
77+ raw_reports = ((attach_filename , attach [" content" ].read ()),)
6478
6579 for file_name , raw_report in raw_reports :
80+ if self .decrypt :
81+ gpg = self ._gpg ().decrypt (
82+ raw_report , passphrase = self .gpg_passphrase
83+ )
84+ if gpg .ok :
85+ raw_report = gpg .data
86+ else :
87+ self .logger .error ('Could not decrypt attachment %s: %s.' , file_name , gpg .status )
88+ continue
6689 report = self .new_report ()
6790 report .add ("raw" , raw_report )
6891 if file_name :
6992 report .add ("extra.file_name" , file_name )
7093 report ["extra.email_subject" ] = message .subject
71- report ["extra.email_from" ] = ',' .join (x ['email' ] for x in message .sent_from )
94+ report ["extra.email_from" ] = "," .join (
95+ x ["email" ] for x in message .sent_from
96+ )
7297 report ["extra.email_message_id" ] = message .message_id
7398 report ["extra.email_date" ] = message .date
7499 self .send_message (report )
@@ -80,5 +105,13 @@ def process_message(self, uid, message):
80105 self .logger .info ("Email report read." )
81106 return seen
82107
108+ @cache
109+ def _gpg (self ):
110+ try :
111+ from gnupg import GPG
112+ except ImportError :
113+ raise MissingDependencyError ("python-gnupg" , ">=0.5" )
114+ return GPG (gnupghome = self .gpg_home )
115+
83116
84117BOT = MailAttachCollectorBot
0 commit comments