Exemplo n.º 1
0
class EmailParser(object):
	def __init__ (self):
		self.raw = None
		self.body = None
		self.subject = None
		self.sender = None
		self.recipient = None
		self.recipientbm = None
		self.pgpbody = None
		self.headers = {}
		self.signature = False
		self.encryption = False
		self.dkim = False
		self.status = False
		self.multipart = False
		self.maintype = None
		self.subtype = None
		self.charset = None
		self.attachment = None
		self.dsn = None
		self.parent = None
		self.out = None

	def parse(self):
		if self.parent == None:
			try:
				self.parse_headers()
			except:
				return False
		try:
			self.parse_body()
		except:
			return False

		self.subject = base64.b64encode(subject)
		self.body = base64.b64encode(body)
		if (sender[0:3] == "BM-"):
			senderbm = sender
		else:
			senderbm = BMAPI().get_address(sender)
		recipientbm = recipient
		userdata = lib.user.GWUser(bm = recipient)
		if userdata.check():
			recipient = userdata['email']
		try:
			ackData = BMAPI().conn().sendMessage(recipientbm, senderbm, self.subject, self.body, 2)
			logging.info("Sent BM from %s to %s", sender, recipient)
		except:
			logging.error("Failure sending BM from %s to %s", sender, recipient)
			return False
		return True

	def read_from_file(self, fname):
		try:
			fullpath = os.path.join(BMConfig().get("bmgateway", "bmgateway", "mail_folder"), fname)
			f = open(fullpath, 'r')
			self.raw = f.read()
			f.close()
		except IOError:
			logging.error('Could not read email from: ' + fullpath)

	def from_data(self, data):
		self.raw = data

	def parse_headers(self):
		self.headers = email.parser.Parser().parsestr(self.raw)
		if not self.headers:
			logging.error('Email missing headers')
			raise
		self.extract_sender()
		self.extract_recipient()
		if not self.sender or not self.recipient:
			logging.warn('Email missing sender or recipient')
			raise
		self.extract_subject()
		self.extract_dkim()

	def extract_sender(self):
		self.sender = self.headers["From"]

		## DSN or missing sender
		if self.sender == '<>' or not self.sender:
			self.sender = BMConfig().get("bmgateway", "bmgateway", "relay_address_label")
		else:
			self.sender = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', self.sender)[0]
		self.sender = self.sender.lower()

		self.body = email.message_from_string(self.raw)

		if self.body.get_content_maintype == "multipart":
			self.multipart = True
			self.maintype = self.body.get_content_maintype()
			self.subtype = self.body.get_content_subtype()
		self.charset = self.body.get_content_charset()

	def attachment(

		if self.body.has_key("Content-Disposition") and self.body.__getitem__("Content-Disposition")[:11] == "attachment":
			self.attachment = email.header.decode_header(part.get_filename())[0]
			
	def extract_recipient(self):
		## find email details
		userdata = None
		rcpts = ()
		for rcpthdr in ("To", "X-Original-To", "Cc"):
			rcpts.extend(re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', self.headers[rcpthdr]))
		for candidate in rcpts:
			## strip extension (user+foo@domain)
			self.recipient = re.sub(r'\+.*@', '@', candidate) 
			## lowercasse
			self.recipient = self.recipient.lower()
			## check if user exists
			userdata = lib.user.GWUser(email = self.recipient, unalias = True)
			if userdata.check():
				break

	def extract_subject(self):
		self.subject = email.header.decode_header(self.headers['Subject'])[0]
		if(self.subject[1]):
			self.subject = unicode(self.subject[0], self.subject[1])
		else:
			self.subject = self.subject[0]

	def extract_dkim(self):
		ar = self.body.get_param("dkim", "missing", "Authentication-Results")
		if ar == "missing":
			domain = self.sender.split("@")[-1]
			if lib.user.GWDomain(domain).check() and domain == self.body.get_param("d", "missing", "DKIM-Signature"):
				ar = "pass" # we trust MTA to reject fakes from domains that hare handled locally
		if ar[0:4] == "pass":
			self.dkim = True
	
	def parse_body(self)
		cipher = None
		signature = None
		if self.multipart:
			for part in self.body.walk():
				if part.get_content_type() == 'message/delivery-status':
					part_str = part.get_payload(decode = 0)
					for subpart in part_str:
						if subpart.get("Action", "") in ("relayed", "delivered", "expanded") and
							self.body.get_param("report-type", "") == "delivery-status" and self.body.get("Auto-Submitted", "") == "auto-replied":
							self.dsn = True
				elif part.get_content_type() == 'text/plain' and
					(self.subtype != "alternative" or self.out == None): # lower precedence if other content exists
					self.handle_text(part)
				elif part.get_content_type() == 'message/rfc822':
					self.handle_text(part)
				elif part.get_content_type() == 'text/html':
					self.handle_html(part)
				elif part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;":
					if has setting
						handle_attachment()
					
			elif self.subtype == "mixed":
			elif self.subtype == "digest":
			elif self.subtype == "alternative":
			elif self.subtype == "related":
			elif self.subtype == "signed":
			elif self.subtype == "encrypted":
		elif self.maintype == "message" and self.subtype == "rfc822":
			self.handle_text(self.body)
		elif self.maintype == "text" and self.subtype == "plain":
			self.handle_text(self.body)
		elif self.maintype == "text" and self.subtype == "html":
			self.handle_html(part)
		return True

	def handle_text(self, msg):
		text = msg.get_payload(decode = True)
		if (self.has_pgp(text)):
			self.out += self.handle_pgp(msg)
		else:
			self.out += text

	def handle_html(self, msg):
		text = msg.get_payload(decode = True)
		if (self.has_pgp(text)):
			text = self.handle_pgp(text)
		h = html2text.HTML2Text()
		h.inline_links = False
		if not msg.get_content_charset():
			charset = chardet.detect(text)
			if charset['encoding'] == None:
				charset = 'ascii'
			else:
				charset = charset['encoding']

		text = h.handle(text).decode(charset))
		self.out += text
Exemplo n.º 2
0
def handle_email(k):
	global address_list
	userdata = None

	## read email from file
	msg_raw = read_email(k)
	if not msg_raw:
		logging.error('Could not open email file: ' + k)
		return


	## extract header
	msg_headers = Parser().parsestr(msg_raw)

	## check if email was valid
	if not msg_headers:
		logging.error('Malformed email detected and purged')
		delete_email(k)
		return

	## find email source and dest addresses
	msg_sender    = msg_headers["From"]

	## failed delivery email
	if msg_sender == '<>' or not msg_sender:
		msg_sender = BMConfig().get("bmgateway", "bmgateway", "relay_address_label")
	else:
		try:
			msg_sender    = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_sender)[0]
		except:
			pass
	msg_sender = msg_sender.lower()

	msg_recipient = ""

	## find email details
	if msg_headers["To"]:
		rcpts = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_headers["To"])
		if len(rcpts) > 0:
			msg_recipient = rcpts[0]
			## strip extension (user+foo@domain)
			msg_recipient = re.sub(r'\+.*@', '@', msg_recipient) 
			msg_recipient = msg_recipient.lower()
			userdata = lib.user.GWUser(email = msg_recipient, unalias = True)

	## check if we have a recipient address for the receiving email
	if not userdata or not userdata.check():
		## look for X-Original-To instead
		rcpts = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_headers["X-Original-To"])
		if len(rcpts) > 0:
			msg_recipient = rcpts[0]
			msg_recipient = re.sub(r'\+.*@', '@', msg_recipient) 
			msg_recipient = msg_recipient.lower()
			userdata = lib.user.GWUser(email = msg_recipient, unalias = True)

	## no valid recipient
	#if not msg_recipient in addressbook:
	#	logging.warn('Purged email destined for unknown user ' + msg_recipient + ' from ' + msg_sender)
	#	logging.debug(msg_headers)
	#	delete_email(k)
	#	return

	## check if we have valid sender and recipient details
	if not msg_sender or not msg_recipient:
		logging.warn('Malformed email detected and purged')
		delete_email(k)
		return

	## set bitmessage destination address
	bm_to_address = userdata.bm

	## set from address
	## check to see if we need to generate a sending address for the source email address
	# if not msg_sender in address_list:
	# 	bm_from_address = generate_sender_address(msg_sender)
	# 	address_list[msg_sender] = bm_from_address
	# else:
	bm_from_address = BMAPI().get_address(BMConfig().get("bmgateway", "bmgateway", "relay_address_label"))

	## find message subject
	msg_subject = decode_header(msg_headers['subject'])[0]
	if(msg_subject[1]):
		msg_subject = unicode(msg_subject[0], msg_subject[1])
	else:
		msg_subject = lib.charset.safeDecode(msg_subject[0])

	## find message body contents in plaintext
	msg_tmp = email.message_from_string(msg_raw)

	# handle DSN
	if msg_tmp.get_content_type() == "multipart/report" and msg_tmp.get_param("report-type", "") == "delivery-status" and msg_tmp.get("Auto-Submitted", "") == "auto-replied":
		for part in msg_tmp.walk():
			if part and part.get_content_type() == 'message/delivery-status':
				for subpart in part.get_payload(decode = 0):
					if subpart.get("Action", "") in ("relayed", "delivered", "expanded"):
						logging.info ("Successful DSN from " + bm_to_address)
						lib.user.GWUser(bm = bm_to_address).setlastrelay(lastrelay = time.time())
						delete_email(k)
						return

	msg_body = u''
	body_raw = ''
	decrypt_ok = False
	sigverify_ok = False
	mega_fileids = []

	# DKIM
	ar = msg_tmp.get_param("dkim", "missing", "Authentication-Results")
	if ar == "missing":
		try:
			domain = msg_sender.split("@")[-1]
			if lib.user.GWDomain(domain).check() and domain == msg_tmp.get_param("d", "missing", "DKIM-Signature"):
				ar = "pass" # we trust MTA to reject fakes
		except:
			pass

	## inline PGP
	for part in msg_tmp.walk():
		if part and part.get_content_type() == 'text/plain' and not (part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;"):
			part_str = part.get_payload(decode=1)
			if userdata.pgp == 1:
				if userdata.flags & 1 == 1:
					pgpparts = part_str.split("-----")
					# hack for absent pgp
					if not pgpparts or len(pgpparts) < 4:
						msg_body += lib.charset.safeDecode(part_str, part.get_content_charset(None))
						continue
					state = 0
					pgp_body = ""
					for pgppart in pgpparts:
						if pgppart == "BEGIN PGP MESSAGE":
							pgp_body = "-----" + pgppart + "-----"
							state = 1
						elif pgppart == "END PGP MESSAGE":
							pgp_body += "-----" + pgppart + "-----"
							# import from sql if necessary
							lib.gpg.check_key(msg_recipient)
							decrypted, sigverify_ok = lib.gpg.decrypt_content(pgp_body, msg_sender, msg_recipient)
							if isinstance(decrypted, basestring):
								part_str = decrypted
								decrypt_ok = True
							#else:
								#part_str = part.get_payload(decode = 0)
							sigresult = "fail"
							if sigverify_ok:
								sigresult = "ok"
							logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient + ", signature: " + sigresult)
							state = 0
						elif pgppart == "BEGIN PGP SIGNED MESSAGE":
							pgp_body += "-----" + pgppart + "-----"
							state = 2
						elif pgppart == "BEGIN PGP SIGNATURE":
							pgp_body += "-----" + pgppart + "-----"
							state = 3
						elif pgppart == "END PGP SIGNATURE":
							pgp_body += "-----" + pgppart + "-----"
							# import from sql if necessary
							lib.gpg.check_key(msg_recipient)
							plain, sigverify_ok = lib.gpg.verify(pgp_body, msg_sender, msg_recipient)
							if isinstance(plain, basestring):
								part_str = plain
							#else:
								#part_str = part.get_payload(decode = 0)
							sigresult = "fail"
							if sigverify_ok:
								sigresult = "ok"
							logging.info("Verifying PGP signature from " + msg_sender + " to " + msg_recipient + ": " + sigresult)
							state = 0
						elif state == 0:
							msg_body += lib.charset.safeDecode(pgppart, part.get_content_charset(None))
						elif state > 0:
							pgp_body += lib.charset.safeDecode(pgppart, part.get_content_charset(None))
				else:
					if "BEGIN PGP MESSAGE" in part_str:
						# import from sql if necessary
						lib.gpg.check_key(msg_recipient)
						decrypted, sigverify_ok = lib.gpg.decrypt_content(part_str, msg_sender, msg_recipient)
						if isinstance(decrypted, basestring):
							part_str = decrypted
							decrypt_ok = True
						else:
							part_str = part.get_payload(decode = 0)
						logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient)
					elif "BEGIN PGP SIGNED MESSAGE" in part_str:
						# import from sql if necessary
						lib.gpg.check_key(msg_recipient)
						plain, sigverify_ok = lib.gpg.verify(part_str, msg_sender, msg_recipient)
						if isinstance(plain, basestring):
							part_str = plain
						else:
							part_str = part.get_payload(decode = 0)
			# PGP END
			
			body_raw += part.as_string(False)
			#print part.get_content_charset()
			#print msg_tmp.get_charset()
			part_str = lib.charset.safeDecode(part_str, part.get_content_charset(None))
			msg_body += part_str
	
	## if there's no plaintext content, convert the html
	if not msg_body or userdata.html == 2:
		for part in msg_tmp.walk():
			if part and part.get_content_type() == 'text/html' and not (part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;"):
				part_str = part.get_payload(decode=1)
				h = html2text.HTML2Text()
				h.inline_links = False
				if userdata.html == 1:
					msg_body += lib.charset.safeDecode(part_str, part.get_content_charset(None))
				elif userdata.html == 2:
					msg_body = lib.charset.safeDecode(part_str, part.get_content_charset(None))
				else:
					msg_body += h.handle(lib.charset.safeDecode(part_str, part.get_content_charset(None)))
				#msg_body = msg_body + html2text.html2text(unicode(part.get_payload(), part.get_content_charset()))		
	
	## if there's no plaintext or html, check if it's encrypted
	# PGP/MIME
	has_encrypted_parts = False
	if not msg_body:
		for part in msg_tmp.walk():
			if part.get_content_type() == 'application/pgp-encrypted':
				has_encrypted_parts = True
				# import from sql if necessary
				if userdata.pgp == 1:
					lib.gpg.check_key(msg_recipient)

			## look for encrypted attachment containing text
			if part.get_content_type() == 'application/octet-stream' and has_encrypted_parts:
				part_str = part.get_payload(decode=1)

				if userdata.pgp == 0:
					msg_body += part_str
					continue

				## if we see the pgp header, decrypt
				if 'BEGIN PGP MESSAGE' in part_str:
					decrypted_data, sigverify_ok = lib.gpg.decrypt_content(part_str, msg_sender, msg_recipient, True)

					## decrypt failed
					if not decrypted_data:
						logging.error("Decryption of email destined for " + msg_recipient + " failed")
						msg_body += part.get_payload(decode=0)
						continue

					logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient)
					msg_body += decrypted_data
					decrypt_ok = True
				elif "BEGIN PGP SIGNED MESSAGE" in part_str:
					plain, sigverify_ok = lib.gpg.verify(part_str, msg_sender, msg_recipient)
					if isinstance(plain, basestring):
						msg_body += plain
					else:
						msg_body += part.get_payload(decode = 0)
				
				## unknown attachment
				else:
					logging.debug("Received application/octet-stream type in inbound email, but did not see encryption header")

	if not sigverify_ok:
		for part in msg_tmp.walk():
			if part.get_content_type() == 'application/pgp-signature':

				if userdata.pgp == 0:
					msg_body = '-----BEGIN PGP SIGNED MESSAGE-----\n' + msg_body
					msg_body += '\n-----BEGIN PGP SIGNATURE-----\n'
					msg_body += part.get_payload(decode=0)
					msg_body += '\n-----END PGP SIGNATURE-----\n'
					continue

				# import from sql if necessary
				lib.gpg.check_key(msg_recipient)
				plain, sigverify_ok = lib.gpg.verify(body_raw, msg_sender, msg_recipient, part.get_payload(decode=1))

	if userdata.attachments == 1 and not userdata.expired():
		for part in msg_tmp.walk():
			if part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;":
				# fix encoding
				try:
					filename = email.header.decode_header(part.get_filename())
					encoding = filename[0][1]
					filename = filename[0][0]
				except:
					filename = part.get_filename()
					encoding = False

				fileid, link = lib.bmmega.mega_upload(userdata.bm, filename, part.get_payload(decode = 1))
				mega_fileids.append(fileid)
				if encoding:
					filename = unicode(filename, encoding)
				logging.info("Attachment \"%s\" (%s)", filename, part.get_content_type())
				msg_body = "Attachment \"" + filename + "\" (" + part.get_content_type() + "): " + link + "\n" + msg_body
	if userdata.pgp == 1:
		if not decrypt_ok:
			msg_body = "WARNING: PGP encryption missing or invalid. The message content could be exposed to third parties.\n" + msg_body
		if not sigverify_ok:
			msg_body = "WARNING: PGP signature missing or invalid. The authenticity of the message could not be verified.\n" + msg_body
	else:
		# msg_body = "WARNING: Server-side PGP is off, passing message as it is.\n" + msg_body
		pass
		
	if not ar[0:4] == "pass":
		msg_body = "WARNING: DKIM signature missing or invalid. The email may not have been sent through legitimate servers.\n" + msg_body

	logging.info('Incoming email from %s to %s', msg_sender, msg_recipient)

	sent = SendBM(bm_from_address, bm_to_address,
		'MAILCHUCK-FROM::' + msg_sender + ' | ' + msg_subject.encode('utf-8'),
		msg_body.encode('utf-8'))
	if sent.status:
		for fileid in mega_fileids:
			# cur.execute ("UPDATE mega SET ackdata = %s WHERE fileid = %s AND ackdata IS NULL", (ackdata.decode("hex"), fileid))
			pass
		## remove email file
		if userdata.archive == 1:
			#print msg_body
			save_email(k)
		else:
			delete_email(k)
def handle_email(k):
	global address_list
	userdata = None

	## read email from file
	msg_raw = read_email(k)
	if not msg_raw:
		logging.error('Could not open email file: ' + k)
		return


	## extract header
	msg_headers = Parser().parsestr(msg_raw)

	## check if email was valid
	if not msg_headers:
		logging.error('Malformed email detected and purged')
		delete_email(k)
		return

	## find email source and dest addresses
	msg_sender    = msg_headers["From"]

	## failed delivery email
	if msg_sender == '<>' or not msg_sender:
		msg_sender = BMConfig().get("bmgateway", "bmgateway", "relay_address_label")
	else:
		try:
			msg_sender    = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_sender)[0]
		except:
			pass
	msg_sender = msg_sender.lower()

	msg_recipient = ""

	## find email details
	if msg_headers["To"]:
		rcpts = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_headers["To"])
		if len(rcpts) > 0:
			msg_recipient = rcpts[0]
			## strip extension (user+foo@domain)
			msg_recipient = re.sub(r'\+.*@', '@', msg_recipient) 
			msg_recipient = msg_recipient.lower()
			userdata = lib.user.GWUser(email = msg_recipient, unalias = True)

	## check if we have a recipient address for the receiving email
	if not userdata.check():
		## look for X-Original-To instead
		rcpts = re.findall(r'[\w\.+-]+@[\w\.-]+.[\w]+', msg_headers["X-Original-To"])
		if len(rcpts) > 0:
			msg_recipient = rcpts[0]
			msg_recipient = re.sub(r'\+.*@', '@', msg_recipient) 
			msg_recipient = msg_recipient.lower()
			userdata = lib.user.GWUser(email = msg_recipient, unalias = True)

	## no valid recipient
	#if not msg_recipient in addressbook:
	#	logging.warn('Purged email destined for unknown user ' + msg_recipient + ' from ' + msg_sender)
	#	logging.debug(msg_headers)
	#	delete_email(k)
	#	return

	## check if we have valid sender and recipient details
	if not msg_sender or not msg_recipient:
		logging.warn('Malformed email detected and purged')
		delete_email(k)
		return

	## set bitmessage destination address
	bm_to_address = userdata.bm

	## set from address
	## check to see if we need to generate a sending address for the source email address
	# if not msg_sender in address_list:
	# 	bm_from_address = generate_sender_address(msg_sender)
	# 	address_list[msg_sender] = bm_from_address
	# else:
	bm_from_address = BMAPI().get_address(BMConfig().get("bmgateway", "bmgateway", "relay_address_label"))

	## find message subject
	msg_subject = decode_header(msg_headers['subject'])[0]
	if(msg_subject[1]):
		msg_subject = unicode(msg_subject[0], msg_subject[1])
	else:
		msg_subject = msg_subject[0]

	## find message body contents in plaintext
	msg_tmp = email.message_from_string(msg_raw)

	# handle DSN
	if msg_tmp.get_content_type() == "multipart/report" and msg_tmp.get_param("report-type", "") == "delivery-status" and msg_tmp.get("Auto-Submitted", "") == "auto-replied":
		for part in msg_tmp.walk():
			if part and part.get_content_type() == 'message/delivery-status':
				part_str = part.get_payload(decode = 0)
				for subpart in part_str:
					if subpart.get("Action", "") in ("relayed", "delivered", "expanded"):
						logging.info ("Successful DSN from " + bm_to_address)
						lib.user.GWUser(bm = bm_to_address).setlastrelay(lastrelay = time.time())
						delete_email(k)
						return

	msg_body = ''
	body_raw = ''
	decrypt_ok = False
	sigverify_ok = False

	# DKIM
	ar = msg_tmp.get_param("dkim", "missing", "Authentication-Results")
	if ar == "missing":
		try:
			domain = msg_sender.split("@")[-1]
			if lib.user.GWDomain(domain).check() and domain == msg_tmp.get_param("d", "missing", "DKIM-Signature"):
				ar = "pass" # we trust MTA to reject fakes
		except:
			pass

	## inline PGP
	for part in msg_tmp.walk():
		if part and part.get_content_type() == 'text/plain' and not (part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;"):
			part_str = part.get_payload(decode=1)
			if userdata.flags & 1 == 1:
				pgpparts = part_str.split("-----")
				state = 0
				pgp_body = ""
				for pgppart in pgpparts:
					if pgppart == "BEGIN PGP MESSAGE":
						pgp_body = "-----" + pgppart + "-----"
						state = 1
					elif pgppart == "END PGP MESSAGE":
						pgp_body += "-----" + pgppart + "-----"
						decrypted, sigverify_ok = lib.gpg.decrypt_content(pgp_body, msg_sender, msg_recipient)
						if isinstance(decrypted, basestring):
							part_str = decrypted
							decrypt_ok = True
						#else:
							#part_str = part.get_payload(decode = 0)
						sigresult = "fail"
						if sigverify_ok:
							sigresult = "ok"
						logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient + ", signature: " + sigresult)
						state = 0
					elif pgppart == "BEGIN PGP SIGNED MESSAGE":
						pgp_body += "-----" + pgppart + "-----"
						state = 2
					elif pgppart == "BEGIN PGP SIGNATURE":
						pgp_body += "-----" + pgppart + "-----"
						state = 3
					elif pgppart == "END PGP SIGNATURE":
						pgp_body += "-----" + pgppart + "-----"
						plain, sigverify_ok = lib.gpg.verify(pgp_body, msg_sender, msg_recipient)
						if isinstance(plain, basestring):
							part_str = plain
						#else:
							#part_str = part.get_payload(decode = 0)
						sigresult = "fail"
						if sigverify_ok:
							sigresult = "ok"
						logging.info("Verifying PGP signature from " + msg_sender + " to " + msg_recipient + ": " + sigresult)
						state = 0
					elif state == 0:
						if part.get_content_charset():
							msg_body += pgppart.decode(part.get_content_charset())
						else:
							charset = chardet.detect(pgppart)
							if charset['encoding']:
								msg_body += pgppart.decode(charset['encoding'])
							else:
								msg_body += pgppart.decode('ascii')
					elif state > 0:
						pgp_body += pgppart
			else:
				if "BEGIN PGP MESSAGE" in part_str:
					decrypted, sigverify_ok = lib.gpg.decrypt_content(part_str, msg_sender, msg_recipient)
					if isinstance(decrypted, basestring):
						part_str = decrypted
						decrypt_ok = True
					else:
						part_str = part.get_payload(decode = 0)
					logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient)
				elif "BEGIN PGP SIGNED MESSAGE" in part_str:
					plain, sigverify_ok = lib.gpg.verify(part_str, msg_sender, msg_recipient)
					if isinstance(plain, basestring):
						part_str = plain
					else:
						part_str = part.get_payload(decode = 0)
			body_raw += part.as_string(False)
			#print part.get_content_charset()
			#print msg_tmp.get_charset()
			if part.get_content_charset():
				try:
					part_str = part_str.decode(part.get_content_charset())
				except:
					charset = chardet.detect(part_str)
					part_str = part_str.decode(charset['encoding'])
			msg_body += part_str
	
	## if there's no plaintext content, convert the html
	if not msg_body:
		for part in msg_tmp.walk():
			if part and part.get_content_type() == 'text/html' and not (part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;"):
				part_str = part.get_payload(decode=1)
				h = html2text.HTML2Text()
				h.inline_links = False
				if part.get_content_charset():
					msg_body += h.handle(part_str.decode(part.get_content_charset()))
				else:
					charset = chardet.detect(part_str)
					msg_body += h.handle(part_str.decode(charset['encoding']))
				#msg_body = msg_body + html2text.html2text(unicode(part.get_payload(), part.get_content_charset()))		
	
	## if there's no plaintext or html, check if it's encrypted
	# PGP/MIME
	has_encrypted_parts = False
	if not msg_body:
		for part in msg_tmp.walk():
			if part.get_content_type() == 'application/pgp-encrypted':
				has_encrypted_parts = True

			## look for encrypted attachment containing text
			if part.get_content_type() == 'application/octet-stream' and has_encrypted_parts:
				part_str = part.get_payload(decode=1)

				## if we see the pgp header, decrypt
				if 'BEGIN PGP MESSAGE' in part_str:
					decrypted_data, sigverify_ok = lib.gpg.decrypt_content(part_str, msg_sender, msg_recipient, True)

					## decrypt failed
					if not decrypted_data:
						logging.error("Decryption of email destined for " + msg_recipient + " failed")
						msg_body += part.get_payload(decode=0)
						continue

					logging.info("Decrypted email from " + msg_sender + " to " + msg_recipient)
					msg_body += decrypted_data
					decrypt_ok = True
				elif "BEGIN PGP SIGNED MESSAGE" in part_str:
					plain, sigverify_ok = lib.gpg.verify(part_str, msg_sender, msg_recipient)
					if isinstance(plain, basestring):
						msg_body += plain
					else:
						msg_body += part.get_payload(decode = 0)
				
				## unknown attachment
				else:
					logging.debug("Received application/octet-stream type in inbound email, but did not see encryption header")

	if not sigverify_ok:
		for part in msg_tmp.walk():
			if part.get_content_type() == 'application/pgp-signature':
				plain, sigverify_ok = lib.gpg.verify(body_raw, msg_sender, msg_recipient, part.get_payload(decode=1))

	if userdata.attachments == 1 and not userdata.expired():
		for part in msg_tmp.walk():
			if part.has_key("Content-Disposition") and part.__getitem__("Content-Disposition")[:11] == "attachment;":
				# fix encoding
				try:
					filename = email.header.decode_header(part.get_filename())
					encoding = filename[0][1]
					filename = filename[0][0]
				except:
					filename = part.get_filename()
					encoding = False

				file_id, link = lib.bmmega.mega_upload(userdata.bm, filename, part.get_payload(decode = 1))
				if encoding:
					filename = unicode(filename, encoding)
				logging.info("Attachment \"%s\" (%s)", filename, part.get_content_type())
				msg_body = "Attachment \"" + filename + "\" (" + part.get_content_type() + "): " + link + "\n" + msg_body

	if not decrypt_ok:
		msg_body = "WARNING: PGP encryption missing or invalid. The message content could be exposed to third parties.\n" + msg_body
	if not sigverify_ok:
		msg_body = "WARNING: PGP signature missing or invalid. The authenticity of the message could not be verified.\n" + msg_body
	if not ar[0:4] == "pass":
		msg_body = "WARNING: DKIM signature missing or invalid. The email may not have been sent through legitimate servers.\n" + msg_body

	logging.info('Incoming email from %s to %s', msg_sender, msg_recipient)

	if SendBM(bm_from_address, bm_to_address,
		'MAILCHUCK-FROM::' + msg_sender + ' | ' + msg_subject.encode('utf-8'),
		msg_body.encode('utf-8')).status:
		## remove email file
		if userdata.archive == 1:
			#print msg_body
			save_email(k)
		else:
			delete_email(k)