====== Почтовый робот для конвертации DOCX/XLSX в DOC/XLS ======
Компания Microsoft очень любит изменять форматы документов по-умолчанию в своём офисном пакете. В нашей компании в силу разных причин переходить на версию офиса 2007/2010 не стали. Но что делать, если поток входящих документов в новых форматах растёт с каждым днём? Можно установить [[http://www.microsoft.com/ru-ru/download/details.aspx?id=3|средство от Microsoft]], можно вообще перейти на [[http://www.libreoffice.org/|LibreOffice]], в котором есть поддержка новых форматов. Но ни тот, ни другой вариант нас полностью не устроил. Тогда и возникла идея сделать почтового робота.
===== Постановка задачи =====
Порядок работы с почтовым роботом должен быть предельно прост: пользователь отправляет письмо на некоторый адрес с вложенными документами, а в ответ получает письмо с конвертированными файлами.
===== Инструменты и приборы в наличии =====
В качестве почтового сервера у нас используется старый добрый [[http://www.sendmail.com/sm/open_source/|Sendmail]] на RHEL5. Одной из его интересных //фич// является возможнось создания алиасов --- виртуальных почтовых адресов, входящая почта на которые может быть обработана скриптом. Согласно настройкам безопасности, такие скрипты будут запущены внутри особого шелла smrsh, про настройки которого можно почитать в Интернете.
Для конвертации файлов можно использовать режим коммандной строки [[http://www.libreoffice.org/|LibreOffice]].
===== Алгоритм =====
Остаётся написать скрипт на любом языке, который будет делать следующее:
- получит письмо от sendmail
- выделит из него вложенные документы в формате MS Office 2007/2010
- запустит soffice с нужными параметрами
- подберёт получившиеся файлы
- и отправит их назад
===== Код =====
Я использовал [[http://python.org|python]] в силу того, что в RHEL5 он уже стоял (версии 2.4) вместе со всеми необходимыми библиотеками.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os, tempfile, string
# библиотека для работы с SMTP
import smtplib
# библиотеки для разбора email
import email.Message
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email.Header import make_header
from email.Utils import formatdate
# адрес и порт SMTP-сервера для отправки
server = '192.168.1.1'
port = 25
# кодировка обратного письма
icharset = 'koi8-r'
# кодировка для имен файлов
encodeto = 'koi8_r'
# путь к LibreOffice
soffice = "/opt/libreoffice3.5/program/soffice"
# форматы и фильтры для конвертации
# ограничимся вордом и экселем
convfilters = {'doc':'doc:"MS Word 97"','xls':'xls:"MS Excel 97"'}
# функция для отправки сообщения
def sendmail(to, subject, message, files):
from_address = 'conv'
msg = MIMEMultipart()
hdr = make_header([(subject, icharset)])
msg['From'] = from_address
msg['To'] = to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = hdr
msg.attach(MIMEText(message, _charset=icharset))
for filename in files:
path = filename
if os.path.isfile(path.encode(encodeto)):
filename = os.path.basename(path)
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
fp = open(path.encode(encodeto), 'rb')
fmsg = MIMEBase(maintype, subtype)
fmsg.set_payload(fp.read())
fp.close()
email.Encoders.encode_base64(fmsg)
# hd_fname = make_header([(filename,icharset)])
fname = filename.encode(encodeto)
fmsg.add_header('Content-Disposition', 'attachment', filename=fname)
msg.attach(fmsg)
srv = smtplib.SMTP(server, port)
srv.ehlo()
srv.sendmail(from_address, to, msg.as_string())
srv.close()
# основной код скрипта
tempfile.tempdir = "/tmp"
tempname=tempfile.mktemp(".conv.tmp")
# тут запомним все временные файлы, чтобы потом их удалить
allfiles = []
# тут запомним сконвертированные файлы, чтобы потом их отправить
convfiles = []
# сохраним исходное письмо во временный файл
f = open(tempname, "w+b")
f.write(sys.stdin.read())
f.close()
allfiles.append(tempname)
f = open(tempname)
# читаем сообщение из файла
message=email.message_from_file(f)
f.close()
to = message['From']
subject = message['Subject']
# разбираем части сообщения
for part in message.walk():
if part.is_multipart():
continue
#если часть сообщения — прикрепленный файл
if part.get_filename() != None:
(filename, fileext) = os.path.splitext(part.get_filename())
if (fileext == '.docx') or (fileext == '.xlsx'):
doctype = fileext[1:4]
cfilter = convfilters[doctype]
docfile = '/tmp/'+filename+fileext
# сохраним вложенный файл
f = open(docfile.encode(encodeto), "wb")
f.write(part.get_payload(decode=True))
f.close()
allfiles.append(docfile)
# строка запуска LibreOffice
command = soffice+" --headless --convert-to "+cfilter+" --outdir /tmp \""+docfile+"\""
os.system(command.encode(encodeto))
newdocfile="/tmp/"+filename+"."+doctype
allfiles.append(newdocfile)
convfiles.append(newdocfile)
# если есть что отправлять, отправляем
if len(convfiles):
sendmail(to,'Re:'+subject,'',convfiles)
# прибираем за собой
for filename in allfiles:
os.remove(filename.encode(encodeto))
===== Использование скрипта =====
Согласно инструкции к smrsh, скрипт необходимо сохранить в каталоге /usr/adm/sm.bin/. В этот же каталог надо сделать симлинк на soffice.
В файл /etc/mail/aliases необходимо добавить строку:
conv: |conv.py
и обновить базу алиасов с помощью команды newaliases.