Почтовый робот для конвертации DOCX/XLSX в DOC/XLS
Компания Microsoft очень любит изменять форматы документов по-умолчанию в своём офисном пакете. В нашей компании в силу разных причин переходить на версию офиса 2007/2010 не стали. Но что делать, если поток входящих документов в новых форматах растёт с каждым днём? Можно установить средство от Microsoft, можно вообще перейти на LibreOffice, в котором есть поддержка новых форматов. Но ни тот, ни другой вариант нас полностью не устроил. Тогда и возникла идея сделать почтового робота.
Постановка задачи
Порядок работы с почтовым роботом должен быть предельно прост: пользователь отправляет письмо на некоторый адрес с вложенными документами, а в ответ получает письмо с конвертированными файлами.
Инструменты и приборы в наличии
В качестве почтового сервера у нас используется старый добрый Sendmail на RHEL5. Одной из его интересных фич является возможнось создания алиасов — виртуальных почтовых адресов, входящая почта на которые может быть обработана скриптом. Согласно настройкам безопасности, такие скрипты будут запущены внутри особого шелла smrsh, про настройки которого можно почитать в Интернете.
Для конвертации файлов можно использовать режим коммандной строки LibreOffice.
Алгоритм
Остаётся написать скрипт на любом языке, который будет делать следующее:
- получит письмо от sendmail
- выделит из него вложенные документы в формате MS Office 2007/2010
- запустит soffice с нужными параметрами
- подберёт получившиеся файлы
- и отправит их назад
Код
Я использовал python в силу того, что в RHEL5 он уже стоял (версии 2.4) вместе со всеми необходимыми библиотеками.
- conv.py
#!/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.