summaryrefslogtreecommitdiff
path: root/fax
diff options
context:
space:
mode:
authorJinkun Jang <jinkun.jang@samsung.com>2013-03-13 01:42:35 +0900
committerJinkun Jang <jinkun.jang@samsung.com>2013-03-13 01:42:35 +0900
commit72835b3d805ac6c7cdaac7d3aff107567e938314 (patch)
tree0f2a04dc3d0672c0960a62804c6e7758673e393c /fax
parenteb5e5ee9adb02776056d1b4494f66150a2fc45f1 (diff)
downloadhplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.gz
hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.bz2
hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.zip
Tizen 2.1 base
Diffstat (limited to 'fax')
-rw-r--r--fax/__init__.py20
-rwxr-xr-xfax/backend/hpfax.py286
-rw-r--r--fax/coverpages.py588
-rw-r--r--fax/fax.py972
-rw-r--r--fax/faxdevice.py70
-rwxr-xr-xfax/filters/pstotiff40
-rw-r--r--fax/filters/pstotiff.convs27
-rw-r--r--fax/filters/pstotiff.types53
-rw-r--r--fax/ledmfax.py691
-rw-r--r--fax/ledmsoapfax.py106
-rw-r--r--fax/marvellfax.py872
-rw-r--r--fax/pmlfax.py1026
-rw-r--r--fax/ppd/HP-Fax-hpcups.ppd.gzbin0 -> 980 bytes
-rw-r--r--fax/ppd/HP-Fax-hpijs.ppd.gzbin0 -> 1932 bytes
-rw-r--r--fax/ppd/HP-Fax2-hpcups.ppd.gzbin0 -> 988 bytes
-rw-r--r--fax/ppd/HP-Fax2-hpijs.ppd.gzbin0 -> 1935 bytes
-rw-r--r--fax/ppd/HP-Fax3-hpcups.ppd.gzbin0 -> 933 bytes
-rw-r--r--fax/ppd/HP-Fax3-hpijs.ppd.gzbin0 -> 1914 bytes
-rw-r--r--fax/ppd/HP-Fax4-hpcups.ppd.gzbin0 -> 946 bytes
-rw-r--r--fax/ppd/HP-Fax4-hpijs.ppd.gzbin0 -> 1897 bytes
-rw-r--r--fax/soapfax.py719
21 files changed, 5470 insertions, 0 deletions
diff --git a/fax/__init__.py b/fax/__init__.py
new file mode 100644
index 0000000..97d3c89
--- /dev/null
+++ b/fax/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
diff --git a/fax/backend/hpfax.py b/fax/backend/hpfax.py
new file mode 100755
index 0000000..039af44
--- /dev/null
+++ b/fax/backend/hpfax.py
@@ -0,0 +1,286 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+__version__ = '4.1'
+__title__ = 'CUPS Fax Backend (hpfax:)'
+__doc__ = "CUPS backend for PC send fax. Generally this backend is run by CUPS, not directly by a user. To send a fax as a user, run hp-sendfax or print to the device's CUPS fax queue."
+
+# StdLib
+import sys
+import getopt
+import ConfigParser
+import os.path, os
+import syslog
+import time
+import operator
+import tempfile
+
+
+CUPS_BACKEND_OK = 0 # Job completed successfully
+CUPS_BACKEND_FAILED = 1 # Job failed, use error-policy
+CUPS_BACKEND_AUTH_REQUIRED = 2 # Job failed, authentication required
+CUPS_BACKEND_HOLD = 3 # Job failed, hold job
+CUPS_BACKEND_STOP = 4 # Job failed, stop queue
+CUPS_BACKEND_CANCEL = 5 # Job failed, cancel job
+
+PIPE_BUF = 4096
+
+job_id = 0
+pid = os.getpid()
+config_file = '/etc/hp/hplip.conf'
+home_dir = ''
+
+
+def bug(msg):
+ syslog.syslog("hpfax[%d]: error: %s\n" % (pid, msg))
+ log.stderr("ERROR: %s\n" % msg)
+
+
+if os.path.exists(config_file):
+ config = ConfigParser.ConfigParser()
+ config.read(config_file)
+
+ try:
+ home_dir = config.get('dirs', 'home')
+ except:
+ bug("Error setting home directory: home= under [dirs] not found.")
+ sys.exit(1)
+else:
+ bug("Error setting home directory: /etc/hp/hplip.conf not found")
+ sys.exit(1)
+
+if not home_dir or not os.path.exists(home_dir):
+ bug("Error setting home directory: Home directory %s not found." % home_dir)
+ sys.exit(1)
+
+sys.path.insert(0, home_dir)
+os.chdir(home_dir)
+
+# HPLIP
+try:
+ from base.g import *
+ from base.codes import *
+ from base import device
+ from base import utils
+ from prnt import cups
+except ImportError, e:
+ bug("Error importing HPLIP modules: %s\n" % (pid, e))
+ sys.exit(1)
+
+def handle_sigpipe():
+ syslog.syslog("SIGPIPE!")
+
+
+USAGE = [(__doc__, "", "para", True),
+ ("Usage: hpfax [job_id] [username] [title] [copies] [options]", "", "summary", True),
+ utils.USAGE_OPTIONS,
+ utils.USAGE_LOGGING1, utils.USAGE_LOGGING2, utils.USAGE_LOGGING3,
+ utils.USAGE_HELP,
+ ]
+
+def usage(typ='text'):
+ if typ == 'text':
+ utils.log_title(__title__, __version__)
+
+ utils.format_text(USAGE, typ, title=__title__, crumb='hpfax:')
+ sys.exit(CUPS_BACKEND_OK)
+
+# Send dbus event to hpssd on dbus system bus
+def send_message(device_uri, printer_name, event_code, username, job_id, title, pipe_name=''):
+ args = [device_uri, printer_name, event_code, username, job_id, title, pipe_name]
+ msg = lowlevel.SignalMessage('/', 'com.hplip.StatusService', 'Event')
+ msg.append(signature='ssisiss', *args)
+
+ SystemBus().send_message(msg)
+
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], 'l:hg', ['level=', 'help', 'help-rest', 'help-man'])
+
+except getopt.GetoptError:
+ usage()
+
+for o, a in opts:
+
+ if o in ('-l', '--logging'):
+ log_level = a.lower().strip()
+ log.set_level(log_level)
+
+ elif o == '-g':
+ log.set_level('debug')
+
+ elif o in ('-h', '--help'):
+ usage()
+
+ elif o == '--help-rest':
+ usage('rest')
+
+ elif o == '--help-man':
+ usage('man')
+
+
+if len( args ) == 0:
+ cups11 = utils.to_bool(sys_conf.get('configure', 'cups11', '0'))
+
+ try:
+ probed_devices = device.probeDevices(['usb', 'par'], filter={'fax-type': (operator.gt, 0)})
+ except Error:
+ sys.exit(CUPS_BACKEND_FAILED)
+
+ good_devices = 0
+ for uri in probed_devices:
+ try:
+ back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
+ device.parseDeviceURI(uri)
+ except Error:
+ continue
+
+ mq = device.queryModelByModel(model)
+
+ if mq.get('fax-type', FAX_TYPE_NONE) in (FAX_TYPE_MARVELL,):
+ # HP Fax 3
+ if bus == 'usb':
+ print 'direct %s "HP Fax 3" "%s USB %s HP Fax HPLIP" "MFG:HP;MDL:Fax 3;DES:HP Fax 3;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '), serial)
+
+ else: # par
+ print 'direct %s "HP Fax 3" "%s LPT HP Fax HPLIP" "MFG:HP;MDL:Fax 3;DES:HP Fax 3;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '))
+
+ elif mq.get('fax-type', FAX_TYPE_NONE) in (FAX_TYPE_SOAP,) or mq.get('fax-type', FAX_TYPE_NONE) in (FAX_TYPE_LEDMSOAP,):
+ # HP Fax 2
+ if bus == 'usb':
+ print 'direct %s "HP Fax 2" "%s USB %s HP Fax HPLIP" "MFG:HP;MDL:Fax 2;DES:HP Fax 2;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '), serial)
+
+ else: # par
+ print 'direct %s "HP Fax 2" "%s LPT HP Fax HPLIP" "MFG:HP;MDL:Fax 2;DES:HP Fax 2;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '))
+ elif mq.get('fax-type', FAX_TYPE_NONE) in (FAX_TYPE_LEDM,):
+ # HP Fax 4
+ if bus == 'usb':
+ print 'direct %s "HP Fax 4" "%s USB %s HP Fax HPLIP" "MFG:HP;MDL:Fax 4;DES:HP Fax 4;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '), serial)
+
+ else: # par
+ print 'direct %s "HP Fax 4" "%s LPT HP Fax HPLIP" "MFG:HP;MDL:Fax 4;DES:HP Fax 4;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '))
+
+ else:
+ # HP Fax
+ if bus == 'usb':
+ print 'direct %s "HP Fax" "%s USB %s HP Fax HPLIP" "MFG:HP;MDL:Fax;DES:HP Fax;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '), serial)
+
+ else: # par
+ print 'direct %s "HP Fax" "%s LPT HP Fax HPLIP" "MFG:HP;MDL:Fax;DES:HP Fax;"' % \
+ (uri.replace("hp:", "hpfax:"), model.replace('_', ' '))
+
+ good_devices += 1
+
+ if good_devices == 0:
+ if cups11:
+ print 'direct hpfax:/no_device_found "HP Fax" "no_device_found" ""'
+ else:
+ print 'direct hpfax "Unknown" "HP Fax (HPLIP)" ""'
+
+ sys.exit(CUPS_BACKEND_OK)
+
+else:
+ try:
+ # dBus
+ import dbus
+ from dbus import SystemBus, lowlevel
+ except ImportError:
+ bug("HPLIP pc send fax requires dbus and python-dbus")
+ sys.exit(CUPS_BACKEND_FAILED)
+
+ import warnings
+ # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
+ # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
+ warnings.simplefilter("ignore", DeprecationWarning)
+
+ # CUPS provided environment
+ try:
+ device_uri = os.environ['DEVICE_URI']
+ printer_name = os.environ['PRINTER']
+ except KeyError:
+ bug("Improper environment: Must be run by CUPS.")
+ sys.exit(CUPS_BACKEND_FAILED)
+
+ log.debug(args)
+
+ try:
+ job_id, username, title, copies, options = args[0:5]
+ job_id = int(job_id)
+ except IndexError:
+ bug("Invalid command line: invalid arguments.")
+ sys.exit(CUPS_BACKEND_FAILED)
+
+ send_message(device_uri, printer_name, EVENT_START_FAX_PRINT_JOB, username, job_id, title)
+
+ try:
+ input_fd = file(args[5], 'r')
+ except IndexError:
+ input_fd = 0
+
+ # REVISIT:
+ tmp_dir = '/tmp'
+ pipe_name = os.path.join(tmp_dir, "hpfax-pipe-%d" % job_id)
+
+ # Create the named pipe. Make sure it exists before sending
+ # message to hppsd.
+ os.umask(0111)
+ try:
+ os.mkfifo(pipe_name)
+ except OSError:
+ os.unlink(pipe_name)
+ os.mkfifo(pipe_name)
+
+ # Send dbus event to hpssd
+ send_message(device_uri, printer_name, EVENT_FAX_RENDER_COMPLETE, username, job_id, title, pipe_name)
+
+ # REVISIT:
+ pipe = os.open(pipe_name, os.O_WRONLY)
+
+ bytes_read = 0
+ while True:
+ data = os.read(input_fd, PIPE_BUF)
+
+ if not data:
+ break
+
+ os.write(pipe, data)
+ #syslog.syslog("Writing %d to pipe..." % len(data))
+ bytes_read += len(data)
+
+ if not bytes_read:
+ bug("No data on input file descriptor.")
+ sys.exit(CUPS_BACKEND_FAILED)
+
+ os.close(input_fd)
+ os.close(pipe)
+ os.unlink(pipe_name)
+
+ send_message(device_uri, printer_name, EVENT_END_FAX_PRINT_JOB, username, job_id, title)
+
+ sys.exit(CUPS_BACKEND_OK)
diff --git a/fax/coverpages.py b/fax/coverpages.py
new file mode 100644
index 0000000..eb40ff7
--- /dev/null
+++ b/fax/coverpages.py
@@ -0,0 +1,588 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+import warnings
+warnings.simplefilter("ignore", DeprecationWarning)
+warnings.simplefilter("ignore", SyntaxWarning)
+from reportlab.platypus.paragraph import Paragraph
+from reportlab.platypus.flowables import Preformatted, Image, HRFlowable
+from reportlab.platypus.doctemplate import *
+#from reportlab.rl_config import TTFSearchPath
+from reportlab.platypus import SimpleDocTemplate, Spacer
+from reportlab.platypus.tables import Table, TableStyle
+from reportlab.lib.pagesizes import letter, legal, A4
+from reportlab.lib.units import inch
+from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
+from reportlab.lib import colors
+#from reportlab.pdfbase import pdfmetrics
+#from reportlab.pdfbase.ttfonts import TTFont
+from time import localtime, strftime
+#import warnings
+warnings.simplefilter('default', DeprecationWarning)
+warnings.simplefilter("default", SyntaxWarning)
+
+if __name__ == "__main__":
+ import sys
+ sys.path.append("..")
+
+from base.g import *
+from base import utils
+
+PAGE_SIZE_LETTER = 'letter'
+PAGE_SIZE_LEGAL = 'legal'
+PAGE_SIZE_A4 = 'a4'
+
+
+def escape(s):
+ return s.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
+
+
+def createStandardCoverPage(page_size=PAGE_SIZE_LETTER,
+ total_pages=1,
+ recipient_name='',
+ recipient_phone='',
+ recipient_fax='',
+ sender_name='',
+ sender_phone='',
+ sender_fax='',
+ sender_email='',
+ regarding='',
+ message='',
+ preserve_formatting=False,
+ output=None):
+
+ s = getSampleStyleSheet()
+
+ story = []
+
+ #print prop.locale
+ #TTFSearchPath.append('/usr/share/fonts/truetype/arphic')
+ #pdfmetrics.registerFont(TTFont('UMing', 'uming.ttf'))
+
+ ps = ParagraphStyle(name="title",
+ parent=None,
+ fontName='helvetica-bold',
+ #fontName='STSong-Light',
+ #fontName = 'UMing',
+ fontSize=72,
+ )
+
+ story.append(Paragraph("FAX", ps))
+
+ story.append(Spacer(1, inch))
+
+ ps = ParagraphStyle(name='normal',
+ fontName='Times-Roman',
+ #fontName='STSong-Light',
+ #fontName='UMing',
+ fontSize=12)
+
+ recipient_name_label = Paragraph("To:", ps)
+ recipient_name_text = Paragraph(escape(recipient_name[:64]), ps)
+
+ recipient_fax_label = Paragraph("Fax:", ps)
+ recipient_fax_text = Paragraph(escape(recipient_fax[:64]), ps)
+
+ recipient_phone_label = Paragraph("Phone:", ps)
+ recipient_phone_text = Paragraph(escape(recipient_phone[:64]), ps)
+
+ sender_name_label = Paragraph("From:", ps)
+ sender_name_text = Paragraph(escape(sender_name[:64]), ps)
+
+ sender_phone_label = Paragraph("Phone:", ps)
+ sender_phone_text = Paragraph(escape(sender_phone[:64]), ps)
+
+ sender_email_label = Paragraph("Email:", ps)
+ sender_email_text = Paragraph(escape(sender_email[:64]), ps)
+
+ regarding_label = Paragraph("Regarding:", ps)
+ regarding_text = Paragraph(escape(regarding[:128]), ps)
+
+ date_time_label = Paragraph("Date:", ps)
+ date_time_text = Paragraph(strftime("%a, %d %b %Y %H:%M:%S (%Z)", localtime()), ps)
+
+ total_pages_label = Paragraph("Total Pages:", ps)
+ total_pages_text = Paragraph("%d" % total_pages, ps)
+
+ data = [[recipient_name_label, recipient_name_text, sender_name_label, sender_name_text],
+ [recipient_fax_label, recipient_fax_text, sender_phone_label, sender_phone_text],
+ [date_time_label, date_time_text, sender_email_label, sender_email_text],
+ [regarding_label, regarding_text, total_pages_label, total_pages_text]]
+
+ LIST_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ ])
+
+ story.append(HRFlowable(width='100%', color='black'))
+
+ story.append(Table(data, style=LIST_STYLE))
+
+ if message:
+ MSG_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ ('SPAN', (-2, 1), (-1, -1)),
+ ])
+
+ story.append(HRFlowable(width='100%', color='black'))
+ story.append(Spacer(1, 0.5*inch))
+
+ if preserve_formatting:
+ message = '\n'.join(message[:2048].splitlines()[:32])
+
+ data = [[Paragraph("Comments/Notes:", ps), ''],
+ [Preformatted(escape(message), ps), ''],]
+ else:
+ data = [[Paragraph("Comments/Notes:", ps), ''],
+ [Paragraph(escape(message[:2048]), ps), ''],]
+
+ story.append(HRFlowable(width='100%', color='black'))
+ story.append(Table(data, style=MSG_STYLE))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ if page_size == PAGE_SIZE_LETTER:
+ pgsz = letter
+ elif page_size == PAGE_SIZE_LEGAL:
+ pgsz = legal
+ else:
+ pgsz = A4
+
+ if output is None:
+ f_fd, f = utils.make_temp_file()
+ else:
+ f = output
+
+ doc = SimpleDocTemplate(f, pagesize=pgsz)
+ doc.build(story)
+
+ return f
+
+
+def createConfidentialCoverPage(page_size=PAGE_SIZE_LETTER,
+ total_pages=1,
+ recipient_name='',
+ recipient_phone='',
+ recipient_fax='',
+ sender_name='',
+ sender_phone='',
+ sender_fax='',
+ sender_email='',
+ regarding='',
+ message='',
+ preserve_formatting=False,
+ output=None):
+
+ s = getSampleStyleSheet()
+
+ story = []
+
+ story.append(Image(os.path.join(prop.image_dir, 'other', 'confidential_title.png')))
+ story.append(Spacer(1, inch))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ ps = ParagraphStyle(name='normal',
+ fontName='Times-Roman',
+ #fontName='STSong-Light',
+ #fontName='UMing',
+ fontSize=12)
+
+ recipient_name_label = Paragraph("To:", ps)
+ recipient_name_text = Paragraph(escape(recipient_name[:64]), ps)
+
+ recipient_fax_label = Paragraph("Fax:", ps)
+ recipient_fax_text = Paragraph(escape(recipient_fax[:64]), ps)
+
+ recipient_phone_label = Paragraph("Phone:", ps)
+ recipient_phone_text = Paragraph(escape(recipient_phone[:64]), ps)
+
+ sender_name_label = Paragraph("From:", ps)
+ sender_name_text = Paragraph(escape(sender_name[:64]), ps)
+
+ sender_phone_label = Paragraph("Phone:", ps)
+ sender_phone_text = Paragraph(escape(sender_phone[:64]), ps)
+
+ sender_email_label = Paragraph("Email:", ps)
+ sender_email_text = Paragraph(escape(sender_email[:64]), ps)
+
+ regarding_label = Paragraph("Regarding:", ps)
+ regarding_text = Paragraph(escape(regarding[:128]), ps)
+
+ date_time_label = Paragraph("Date:", ps)
+ date_time_text = Paragraph(strftime("%a, %d %b %Y %H:%M:%S (%Z)", localtime()), ps)
+
+ total_pages_label = Paragraph("Total Pages:", ps)
+ total_pages_text = Paragraph("%d" % total_pages, ps)
+
+ data = [[recipient_name_label, recipient_name_text],
+ [recipient_fax_label, recipient_fax_text],
+ ['', ''],
+ [sender_name_label, sender_name_text],
+ [sender_phone_label, sender_phone_text],
+ [sender_email_label, sender_email_text],
+ ['', ''],
+ [date_time_label, date_time_text],
+ [total_pages_label, total_pages_text],
+ [regarding_label, regarding_text],]
+
+ LIST_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ ])
+
+
+ story.append(Table(data, style=LIST_STYLE))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ if message:
+ MSG_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ #('SPAN', (-2, 1), (-1, -1)),
+ ])
+
+ #story.append(HRFlowable(width='100%', color='black'))
+ story.append(Spacer(1, 0.5*inch))
+
+# if preserve_formatting:
+# message = '\n'.join(message[:2048].splitlines()[:32])
+#
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Preformatted(escape(message), ps)],]
+# else:
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Paragraph(escape(message[:2048]), ps), ''],]
+#
+# #story.append(HRFlowable(width='100%', color='black'))
+# #story.append(Table(data, style=MSG_STYLE))
+
+ if preserve_formatting:
+ message = '\n'.join(message[:2048].splitlines()[:32])
+ story.append(Preformatted(escape(message), ps))
+ else:
+ story.append(Paragraph(escape(message), ps))
+
+
+ if page_size == PAGE_SIZE_LETTER:
+ pgsz = letter
+ elif page_size == PAGE_SIZE_LEGAL:
+ pgsz = legal
+ else:
+ pgsz = A4
+
+ if output is None:
+ f_fd, f = utils.make_temp_file()
+ else:
+ f = output
+
+ doc = SimpleDocTemplate(f, pagesize=pgsz)
+ doc.build(story)
+
+ return f
+
+
+def createGenericCoverPage(page_size=PAGE_SIZE_LETTER,
+ total_pages=1,
+ recipient_name='',
+ recipient_phone='',
+ recipient_fax='',
+ sender_name='',
+ sender_phone='',
+ sender_fax='',
+ sender_email='',
+ regarding='',
+ message='',
+ preserve_formatting=False,
+ output=None):
+
+ s = getSampleStyleSheet()
+
+ story = []
+
+ i = Image(os.path.join(prop.image_dir, 'other', 'generic_title.png'), width=250, height=147)
+ i.hAlign = 'LEFT'
+ story.append(i)
+ #story.append(Spacer(1, inch))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ ps = ParagraphStyle(name='normal',
+ fontName='Times-Roman',
+ #fontName='STSong-Light',
+ #fontName='UMing',
+ fontSize=12)
+
+ recipient_name_label = Paragraph("To:", ps)
+ recipient_name_text = Paragraph(escape(recipient_name[:64]), ps)
+
+ recipient_fax_label = Paragraph("Fax:", ps)
+ recipient_fax_text = Paragraph(escape(recipient_fax[:64]), ps)
+
+ recipient_phone_label = Paragraph("Phone:", ps)
+ recipient_phone_text = Paragraph(escape(recipient_phone[:64]), ps)
+
+ sender_name_label = Paragraph("From:", ps)
+ sender_name_text = Paragraph(escape(sender_name[:64]), ps)
+
+ sender_phone_label = Paragraph("Phone:", ps)
+ sender_phone_text = Paragraph(escape(sender_phone[:64]), ps)
+
+ sender_email_label = Paragraph("Email:", ps)
+ sender_email_text = Paragraph(escape(sender_email[:64]), ps)
+
+ regarding_label = Paragraph("Regarding:", ps)
+ regarding_text = Paragraph(escape(regarding[:128]), ps)
+
+ date_time_label = Paragraph("Date:", ps)
+ date_time_text = Paragraph(strftime("%a, %d %b %Y %H:%M:%S (%Z)", localtime()), ps)
+
+ total_pages_label = Paragraph("Total Pages:", ps)
+ total_pages_text = Paragraph("%d" % total_pages, ps)
+
+ data = [[recipient_name_label, recipient_name_text],
+ [recipient_fax_label, recipient_fax_text],
+ ['', ''],
+ [sender_name_label, sender_name_text],
+ [sender_phone_label, sender_phone_text],
+ [sender_email_label, sender_email_text],
+ ['', ''],
+ [date_time_label, date_time_text],
+ [total_pages_label, total_pages_text],
+ [regarding_label, regarding_text],]
+
+ LIST_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ ])
+
+
+ story.append(Table(data, style=LIST_STYLE))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ if message:
+ MSG_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ #('SPAN', (-2, 1), (-1, -1)),
+ ])
+
+ #story.append(HRFlowable(width='100%', color='black'))
+ story.append(Spacer(1, 0.5*inch))
+
+# if preserve_formatting:
+# message = '\n'.join(message[:2048].splitlines()[:32])
+#
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Preformatted(escape(message), ps)],]
+# else:
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Paragraph(escape(message[:2048]), ps), ''],]
+#
+# #story.append(HRFlowable(width='100%', color='black'))
+# #story.append(Table(data, style=MSG_STYLE))
+
+ if preserve_formatting:
+ message = '\n'.join(message[:2048].splitlines()[:32])
+ story.append(Preformatted(escape(message), ps))
+ else:
+ story.append(Paragraph(escape(message), ps))
+
+ #
+
+ if page_size == PAGE_SIZE_LETTER:
+ pgsz = letter
+ elif page_size == PAGE_SIZE_LEGAL:
+ pgsz = legal
+ else:
+ pgsz = A4
+
+ if output is None:
+ f_fd, f = utils.make_temp_file()
+ else:
+ f = output
+
+ doc = SimpleDocTemplate(f, pagesize=pgsz)
+ doc.build(story)
+
+ return f
+
+
+def createUrgentCoverPage(page_size=PAGE_SIZE_LETTER,
+ total_pages=1,
+ recipient_name='',
+ recipient_phone='',
+ recipient_fax='',
+ sender_name='',
+ sender_phone='',
+ sender_fax='',
+ sender_email='',
+ regarding='',
+ message='',
+ preserve_formatting=False,
+ output=None):
+
+ s = getSampleStyleSheet()
+
+ story = []
+ i = Image(os.path.join(prop.image_dir, 'other', 'urgent_title.png'), width=424, height=92)
+ i.hAlign = 'LEFT'
+ story.append(i)
+ story.append(Spacer(1, inch))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ ps = ParagraphStyle(name='normal',
+ fontName='Times-Roman',
+ #fontName='STSong-Light',
+ #fontName='UMing',
+ fontSize=12)
+
+ recipient_name_label = Paragraph("To:", ps)
+ recipient_name_text = Paragraph(escape(recipient_name[:64]), ps)
+
+ recipient_fax_label = Paragraph("Fax:", ps)
+ recipient_fax_text = Paragraph(escape(recipient_fax[:64]), ps)
+
+ recipient_phone_label = Paragraph("Phone:", ps)
+ recipient_phone_text = Paragraph(escape(recipient_phone[:64]), ps)
+
+ sender_name_label = Paragraph("From:", ps)
+ sender_name_text = Paragraph(escape(sender_name[:64]), ps)
+
+ sender_phone_label = Paragraph("Phone:", ps)
+ sender_phone_text = Paragraph(escape(sender_phone[:64]), ps)
+
+ sender_email_label = Paragraph("Email:", ps)
+ sender_email_text = Paragraph(escape(sender_email[:64]), ps)
+
+ regarding_label = Paragraph("Regarding:", ps)
+ regarding_text = Paragraph(escape(regarding[:128]), ps)
+
+ date_time_label = Paragraph("Date:", ps)
+ date_time_text = Paragraph(strftime("%a, %d %b %Y %H:%M:%S (%Z)", localtime()), ps)
+
+ total_pages_label = Paragraph("Total Pages:", ps)
+ total_pages_text = Paragraph("%d" % total_pages, ps)
+
+ data = [[recipient_name_label, recipient_name_text],
+ [recipient_fax_label, recipient_fax_text],
+ ['', ''],
+ [sender_name_label, sender_name_text],
+ [sender_phone_label, sender_phone_text],
+ [sender_email_label, sender_email_text],
+ ['', ''],
+ [date_time_label, date_time_text],
+ [total_pages_label, total_pages_text],
+ [regarding_label, regarding_text],]
+
+ LIST_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ ])
+
+
+ story.append(Table(data, style=LIST_STYLE))
+ story.append(HRFlowable(width='100%', color='black'))
+
+ if message:
+ MSG_STYLE = TableStyle([#('LINEABOVE', (0,0), (-1,0), 2, colors.black),
+ #('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ #('LINEBELOW', (0,-1), (-1,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
+ #('SPAN', (-2, 1), (-1, -1)),
+ ])
+
+ #story.append(HRFlowable(width='100%', color='black'))
+ story.append(Spacer(1, 0.5*inch))
+
+# if preserve_formatting:
+# message = '\n'.join(message[:2048].splitlines()[:32])
+#
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Preformatted(escape(message), ps)],]
+# else:
+# data = [#[Paragraph("Comments/Notes:", ps), ''],
+# [Paragraph(escape(message[:2048]), ps), ''],]
+#
+# #story.append(HRFlowable(width='100%', color='black'))
+# #story.append(Table(data, style=MSG_STYLE))
+
+ if preserve_formatting:
+ message = '\n'.join(message[:2048].splitlines()[:32])
+ story.append(Preformatted(escape(message), ps))
+ else:
+ story.append(Paragraph(escape(message), ps))
+
+
+ if page_size == PAGE_SIZE_LETTER:
+ pgsz = letter
+ elif page_size == PAGE_SIZE_LEGAL:
+ pgsz = legal
+ else:
+ pgsz = A4
+
+ if output is None:
+ f_fd, f = utils.make_temp_file()
+ else:
+ f = output
+
+ doc = SimpleDocTemplate(f, pagesize=pgsz)
+ doc.build(story)
+
+ return f
+
+
+# { "name" : (function, "thumbnail.png"), ... }
+COVERPAGES = { "basic": (createStandardCoverPage, 'standard_coverpage.png'),
+ "confidential": (createConfidentialCoverPage, 'confidential_coverpage.png'),
+ "generic": (createGenericCoverPage, "generic_coverpage.png"),
+ "urgent": (createUrgentCoverPage, "urgent_coverpage.png"),
+ }
+
+
+if __name__ == "__main__":
+ createUrgentCoverPage(page_size=PAGE_SIZE_LETTER,
+ total_pages=1,
+ recipient_name='Trex',
+ recipient_phone='+1 234-567-8912',
+ recipient_fax='+1 432 123 1234',
+ sender_name='Don',
+ sender_phone='+1 234 432 1234',
+ sender_fax='+1 567 876 5123 ',
+ sender_email='test@hplip.sf.net',
+ regarding='Some sorta stuff',
+ message="""Some HP printers require proprietary software technologies to allow full access to printer features and performance. These technologies cannot be open sourced. Because of this, HP uses a binary plug-in for these printers that work in conjunction with our Linux Open Source Printing Software to improve the printing experience for HP’s Linux Printing Customers. This binary plug-in requires the user to read and agree to a license agreement at the time of driver installation. There is a single plug-in file (for each HPLIP release) for all plug-in enabled devices.""",
+ preserve_formatting=False,
+ output="output.pdf")
+
+
diff --git a/fax/fax.py b/fax/fax.py
new file mode 100644
index 0000000..58a0e29
--- /dev/null
+++ b/fax/fax.py
@@ -0,0 +1,972 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2010 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+from __future__ import generators
+
+# Std Lib
+import sys
+import os
+import threading
+import cPickle
+import time
+from cStringIO import StringIO
+import struct
+
+# Local
+from base.g import *
+from base.codes import *
+from base.ldif import LDIFParser
+from base import device, utils, vcard
+from prnt import cups
+
+try:
+ import coverpages
+except ImportError:
+ pass
+
+try:
+ import dbus
+except ImportError:
+ log.error("dbus is required for PC send fax.")
+
+import warnings
+# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
+# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
+warnings.simplefilter("ignore", DeprecationWarning)
+
+
+# Update queue values (Send thread ==> UI)
+STATUS_IDLE = 0
+STATUS_PROCESSING_FILES = 1
+STATUS_SENDING_TO_RECIPIENT = 2
+STATUS_DIALING = 3
+STATUS_CONNECTING = 4
+STATUS_SENDING = 5
+STATUS_COMPLETED = 6
+STATUS_CREATING_COVER_PAGE = 7
+STATUS_ERROR = 8
+STATUS_BUSY = 9
+STATUS_CLEANUP = 10
+STATUS_ERROR_IN_CONNECTING = 11
+STATUS_ERROR_IN_TRANSMITTING = 12
+STATUS_ERROR_PROBLEM_IN_FAXLINE = 13
+STATUS_JOB_CANCEL = 14
+
+# Event queue values (UI ==> Send thread)
+EVENT_FAX_SEND_CANCELED = 1
+# Other values in queue are:
+#EVENT_FAX_RENDER_COMPLETE_BEGIN = 8010
+#EVENT_FAX_RENDER_COMPLETE_SENDDATA = 8011
+#EVENT_FAX_RENDER_COMPLETE_END = 8012
+
+# **************************************************************************** #
+# HPLIP G3 Fax File Format (big endian)
+#
+# #==============================================#
+# # File Header: Total 28 bytes #
+# #..............................................#
+# # Magic bytes: 8 bytes ("hplip_g3") #
+# # Format version: 8 bits (1) #
+# # Total pages in file(=p): 32 bits #
+# # Hort DPI: 16 bits (200 or 300) #
+# # Vert DPI: 16 bits (100, 200, or 300) #
+# # Page Size: 8 bits (0=Unk, 1=Letter, 2=A4, #
+# # 3=Legal) #
+# # Resolution: 8 bits (0=Unk, 1=Std, 2=Fine, #
+# # 3=300DPI) #
+# # Encoding: 8 bits (2=MH, 4=MMR, 7=JPEG) #
+# # Reserved1: 32 bits (0) #
+# # Reserved2: 32 bits (0) #
+# #----------------------------------------------#
+# # Page 1 Header: Total 24 bytes #
+# #..............................................#
+# # Page number: 32 bits (1 based) #
+# # Pixels per row: 32 bits #
+# # Rows this page: 32 bits #
+# # Image bytes this page(=x): 32 bits #
+# # Thumbnail bytes this page(=y): 32 bits #
+# # (thumbnail not present if y == 0) #
+# # (encoding?) #
+# # letter: 134 px wide x 173 px high #
+# # legal: 134 px wide x 221 px high #
+# # a4 : 134 px wide x 190 px high #
+# # Reserved3: 32 bits (0) #
+# #..............................................#
+# # Image data: x bytes #
+# #..............................................#
+# # Thumbnail data: y bytes (if present) #
+# #----------------------------------------------#
+# # Page 2 Header: Total 24 bytes #
+# #..............................................#
+# # Image Data #
+# #..............................................#
+# # Thumbnail data (if present) #
+# #----------------------------------------------#
+# # ... Pages 3 - (p-1) ... #
+# #----------------------------------------------#
+# # Page p Header: Total 24 bytes #
+# #..............................................#
+# # Image Data #
+# #..............................................#
+# # Thumbnail data (if present) #
+# #==============================================#
+#
+
+RESOLUTION_STD = 1
+RESOLUTION_FINE = 2
+RESOLUTION_300DPI = 3
+
+FILE_HEADER_SIZE = 28
+PAGE_HEADER_SIZE = 24
+# **************************************************************************** #
+
+##skip_dn = ["uid=foo,ou=People,dc=example,dc=com",
+## "uid=bar,ou=People,dc=example,dc=com", "dc=example,dc=com"]
+
+class FaxLDIFParser(LDIFParser):
+ def __init__(self, input, db):
+ LDIFParser.__init__(self, input)
+ self.db = db
+
+ def handle(self, dn, entry):
+ if dn:
+ try:
+ firstname = entry['givenName'][0]
+ except KeyError:
+ try:
+ firstname = entry['givenname'][0]
+ except KeyError:
+ firstname = ''
+
+ try:
+ lastname = entry['sn'][0]
+ except KeyError:
+ lastname = ''
+
+ try:
+ nickname = entry['cn'][0]
+ except KeyError:
+ nickname = firstname + ' ' + lastname
+
+ try:
+ fax = entry['facsimiletelephonenumber'][0] # fax
+ except KeyError:
+ try:
+ fax = entry['fax'][0]
+ except KeyError:
+ fax = ''
+
+ grps = []
+ try:
+ grps = entry['ou']
+ except KeyError:
+ pass
+
+ grps.append(u'All')
+ groups = [g for g in grps if g]
+
+ if nickname:
+ log.debug("Import: name=%s, fax=%s, group(s)=%s, notes=%s" % ( nickname, fax, ','.join(groups), dn))
+ self.db.set(nickname, title, firstname, lastname, fax, groups, dn)
+
+
+
+# **************************************************************************** #
+class FaxAddressBook(object): # Pickle based address book
+ def __init__(self):
+ self._data = {}
+ #
+ # { 'name' : {'name': u'',
+ # 'firstname' : u'', # NOT USED STARTING IN 2.8.9
+ # 'lastname': u', # NOT USED STARTING IN 2.8.9
+ # 'title' : u'', # NOT USED STARTING IN 2.8.9
+ # 'fax': u'',
+ # 'groups' : [u'', u'', ...],
+ # 'notes' : u'', } ...
+ # }
+ #
+ self.load()
+
+ def load(self):
+ self._fab = "/dev/null"
+ if prop.user_dir != None:
+ self._fab = os.path.join(prop.user_dir, "fab.pickle")
+ #old_fab = os.path.join(prop.user_dir, "fab.db")
+
+ # Load the existing pickle if present
+ if os.path.exists(self._fab):
+ pickle_file = open(self._fab, "r")
+ self._data = cPickle.load(pickle_file)
+ pickle_file.close()
+ else:
+ self.save() # save the empty file to create the file
+
+
+ def set(self, name, title, firstname, lastname, fax, groups, notes):
+ try:
+ grps = [unicode(s) for s in groups]
+ except UnicodeDecodeError:
+ grps = [unicode(s.decode('utf-8')) for s in groups]
+
+ self._data[unicode(name)] = {'name' : unicode(name),
+ 'title': unicode(title), # NOT USED STARTING IN 2.8.9
+ 'firstname': unicode(firstname), # NOT USED STARTING IN 2.8.9
+ 'lastname': unicode(lastname), # NOT USED STARTING IN 2.8.9
+ 'fax': unicode(fax),
+ 'notes': unicode(notes),
+ 'groups': grps}
+
+ self.save()
+
+ insert = set
+
+
+ def set_key_value(self, name, key, value):
+ self._data[unicode(name)][key] = value
+ self.save()
+
+
+ def get(self, name):
+ return self._data.get(name, None)
+
+ select = get
+
+ def rename(self, old_name, new_name):
+ try:
+ self._data[old_name]
+ except KeyError:
+ return
+ else:
+ try:
+ self._data[new_name]
+ except KeyError:
+ self._data[new_name] = self._data[old_name].copy()
+ self._data[new_name]['name'] = new_name
+ del self._data[old_name]
+ self.save()
+
+
+ def get_all_groups(self):
+ all_groups = []
+ for e, v in self._data.items():
+ for g in v['groups']:
+ if g not in all_groups:
+ all_groups.append(g)
+ return all_groups
+
+
+ def get_all_records(self):
+ return self._data
+
+
+ def get_all_names(self):
+ return self._data.keys()
+
+
+ def save(self):
+ try:
+ pickle_file = open(self._fab, "w")
+ cPickle.dump(self._data, pickle_file, cPickle.HIGHEST_PROTOCOL)
+ pickle_file.close()
+ except IOError:
+ log.error("I/O error saving fab file.")
+
+
+ def clear(self):
+ self._data = {}
+ self.save()
+
+
+ def delete(self, name):
+ if name in self._data:
+ del self._data[name]
+ self.save()
+ return True
+
+ return False
+
+
+ def last_modification_time(self):
+ try:
+ return os.stat(self._fab).st_mtime
+ except OSError:
+ return 0
+
+
+ def update_groups(self, group, members):
+ for e, v in self._data.items():
+ if v['name'] in members: # membership indicated
+ if not group in v['groups']:
+ v['groups'].append(unicode(group))
+ else:
+ if group in v['groups']:
+ v['groups'].remove(unicode(group))
+ self.save()
+
+
+ def delete_group(self, group):
+ for e, v in self._data.items():
+ if group in v['groups']:
+ v['groups'].remove(unicode(group))
+ self.save()
+
+
+ def group_members(self, group):
+ members = []
+ for e, v in self._data.items():
+ if group in v['groups']:
+ members.append(e)
+ return members
+
+
+ def add_to_group(self, group, members):
+ group_members = self.group_members(group)
+ new_group_members = []
+ for m in members:
+ if m not in group_members:
+ new_group_members.append(m)
+
+ self.update_groups(group, group_members + new_group_members)
+
+
+ def remove_from_group(self, group, remove_members):
+ group_members = self.group_members(group)
+ new_group_members = []
+ for m in group_members:
+ if m not in remove_members:
+ new_group_members.append(m)
+
+ self.update_groups(group, new_group_members)
+
+
+ def rename_group(self, old_group, new_group):
+ members = self.group_members(old_group)
+ self.update_groups(old_group, [])
+ self.update_groups(new_group, members)
+
+
+ def import_ldif(self, filename):
+ try:
+ data = open(filename, 'r').read()
+ log.debug_block(filename, data)
+ parser = FaxLDIFParser(open(filename, 'r'), self)
+ parser.parse()
+ self.save()
+ return True, ''
+ except ValueError, e:
+ return False, e.message
+
+
+ def import_vcard(self, filename):
+ data = file(filename, 'r').read()
+ log.debug_block(filename, data)
+
+ for card in vcard.VCards(vcard.VFile(vcard.opentextfile(filename))):
+ log.debug(card)
+
+ if card['name']:
+ fax = ''
+ for x in range(1, 9999):
+ if x == 1:
+ s = 'phone'
+ else:
+ s = 'phone%d' % x
+
+ try:
+ card[s]
+ except KeyError:
+ break
+ else:
+ if 'fax' in card[s]['type']:
+ fax = card[s]['number']
+ break
+
+ org = card.get('organisation', '')
+ if org:
+ org = [org]
+ else:
+ org = card.get('categories', '').split(';')
+ if not org:
+ org = []
+
+ org.append(u'All')
+ groups = [o for o in org if o]
+
+ name = card['name']
+ notes = card.get('notes', u'')
+ log.debug("Import: name=%s, fax=%s group(s)=%s notes=%s" % (name, fax, ','.join(groups), notes))
+ self.set(name, u'', u'', u'', fax, groups, notes)
+
+ return True, ''
+
+
+# **************************************************************************** #
+class FaxDevice(device.Device):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ device.Device.__init__(self, device_uri, printer_name,
+ None, callback, disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+ self.fax_type = fax_type
+
+ if not disable_dbus:
+ session_bus = dbus.SessionBus()
+ self.service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
+ else:
+ self.service = None
+
+
+ def setPhoneNum(self, num):
+ raise AttributeError
+
+ def getPhoneNum(self):
+ raise AttributeError
+
+ phone_num = property(getPhoneNum, setPhoneNum)
+
+
+ def setStationName(self, name):
+ raise AttributeError
+
+ def getStationName(self):
+ raise AttributeError
+
+ station_name = property(getStationName, setStationName)
+
+ def setDateAndTime(self):
+ raise AttributeError
+
+ def uploadLog(self):
+ raise AttributeError
+
+ def isUploadLogActive(self):
+ raise AttributeError
+
+ def waitForUploadLogThread(self):
+ raise AttributeError
+
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ raise AttributeError
+
+ def isSendFaxActive(self):
+ if self.send_fax_thread is not None:
+ return self.send_fax_thread.isAlive()
+ else:
+ return False
+
+ def waitForSendFaxThread(self):
+ if self.send_fax_thread is not None and \
+ self.send_fax_thread.isAlive():
+
+ try:
+ self.send_fax_thread.join()
+ except KeyboardInterrupt:
+ pass
+
+
+# **************************************************************************** #
+
+
+def getFaxDevice(device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ if fax_type == FAX_TYPE_NONE:
+ if device_uri is None and printer_name is not None:
+ printers = cups.getPrinters()
+
+ for p in printers:
+ if p.name.lower() == printer_name.lower():
+ device_uri = p.device_uri
+ break
+ else:
+ raise Error(ERROR_DEVICE_NOT_FOUND)
+
+ if device_uri is not None:
+ mq = device.queryModelByURI(device_uri)
+ fax_type = mq['fax-type']
+
+ log.debug("fax-type=%d" % fax_type)
+
+ if fax_type in (FAX_TYPE_BLACK_SEND_EARLY_OPEN, FAX_TYPE_BLACK_SEND_LATE_OPEN):
+ from pmlfax import PMLFaxDevice
+ return PMLFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_SOAP:
+ from soapfax import SOAPFaxDevice
+ return SOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_LEDMSOAP:
+ from ledmsoapfax import LEDMSOAPFaxDevice
+ return LEDMSOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_MARVELL:
+ from marvellfax import MarvellFaxDevice
+ return MarvellFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_LEDM:
+ from ledmfax import LEDMFaxDevice
+ return LEDMFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ else:
+ raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
+
+# **************************************************************************** #
+
+
+
+
+# TODO: Define these in only 1 place!
+STATE_DONE = 0
+STATE_ABORTED = 10
+STATE_SUCCESS = 20
+STATE_BUSY = 25
+STATE_READ_SENDER_INFO = 30
+STATE_PRERENDER = 40
+STATE_COUNT_PAGES = 50
+STATE_NEXT_RECIPIENT = 60
+STATE_COVER_PAGE = 70
+STATE_SINGLE_FILE = 80
+STATE_MERGE_FILES = 90
+STATE_SINGLE_FILE = 100
+STATE_SEND_FAX = 110
+STATE_CLEANUP = 120
+STATE_ERROR = 130
+
+class FaxSendThread(threading.Thread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ threading.Thread.__init__(self)
+
+ self.dev = dev # device.Device
+ self.service = service # dbus proxy to status server object
+ self.phone_num_list = phone_num_list
+ self.fax_file_list = fax_file_list
+ self.update_queue = update_queue
+ self.event_queue = event_queue
+ self.cover_message = cover_message
+ self.cover_re = cover_re
+ self.cover_func = cover_func
+ self.current_printer = printer_name
+ self.stream = StringIO()
+ self.prev_update = ''
+ self.remove_temp_file = False
+ self.preserve_formatting = preserve_formatting
+ self.results = {} # {'file' : error_code,...}
+ self.cover_page_present = False
+ self.recipient_file_list = []
+ self.f = None # final file of fax data to send (pages merged)
+ self.job_hort_dpi = 0
+ self.job_hort_dpi = 0
+ self.job_vert_dpi = 0
+ self.job_page_size = 0
+ self.job_resolution = 0
+ self.job_encoding = 0
+
+
+ def pre_render(self, state):
+ # pre-render each page that needs rendering
+ # except for the cover page
+ self.cover_page_present = False
+ log.debug(self.fax_file_list)
+
+ for fax_file in self.fax_file_list: # (file, type, desc, title)
+ fax_file_name, fax_file_type, fax_file_desc, \
+ fax_file_title, fax_file_pages = fax_file
+
+ if fax_file_type == "application/hplip-fax-coverpage": # render later
+ self.cover_page_present = True
+ log.debug("Skipping coverpage")
+
+ #if fax_file_type == "application/hplip-fax": # already rendered
+ else:
+ self.rendered_file_list.append((fax_file_name, "application/hplip-fax",
+ "HP Fax", fax_file_title))
+
+ log.debug("Processing pre-rendered file: %s (%d pages)" %
+ (fax_file_name, fax_file_pages))
+
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ log.debug(self.rendered_file_list)
+
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ return state
+
+
+ def count_pages(self, state):
+ self.recipient_file_list = self.rendered_file_list[:]
+ log.debug("Counting total pages...")
+ self.job_total_pages = 0
+ log.debug(self.recipient_file_list)
+
+ i = 0
+ for fax_file in self.recipient_file_list: # (file, type, desc, title)
+ fax_file_name = fax_file[0]
+ log.debug("Processing file (counting pages): %s..." % fax_file_name)
+
+ #self.write_queue((STATUS_PROCESSING_FILES, self.job_total_pages, ''))
+
+ if os.path.exists(fax_file_name):
+ self.results[fax_file_name] = ERROR_SUCCESS
+ fax_file_fd = file(fax_file_name, 'r')
+ header = fax_file_fd.read(FILE_HEADER_SIZE)
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = \
+ self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ self.results[fax_file_name] = ERROR_FAX_INVALID_FAX_FILE
+ state = STATE_ERROR
+ continue
+
+ if not i:
+ self.job_hort_dpi, self.job_vert_dpi, self.job_page_size, \
+ self.job_resolution, self.job_encoding = \
+ hort_dpi, vert_dpi, page_size, resolution, encoding
+
+ i += 1
+ else:
+ if self.job_hort_dpi != hort_dpi or \
+ self.job_vert_dpi != vert_dpi or \
+ self.job_page_size != page_size or \
+ self.job_resolution != resolution or \
+ self.job_encoding != encoding:
+
+ log.error("Incompatible options for file: %s" % fax_file_name)
+ self.results[fax_file_name] = ERROR_FAX_INCOMPATIBLE_OPTIONS
+ state = STATE_ERROR
+
+
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi,
+ vert_dpi, page_size, resolution, encoding))
+
+ self.job_total_pages += total_pages
+
+ fax_file_fd.close()
+
+ else:
+ log.error("Unable to find HP Fax file: %s" % fax_file_name)
+ self.results[fax_file_name] = ERROR_FAX_FILE_NOT_FOUND
+ state = STATE_ERROR
+ break
+
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+ break
+
+
+ if self.cover_page_present:
+ self.job_total_pages += 1 # Cover pages are truncated to 1 page
+
+ log.debug("Total fax pages=%d" % self.job_total_pages)
+
+ return state
+
+ def decode_fax_header(self, header):
+ try:
+ return struct.unpack(">8sBIHHBBBII", header)
+ except struct.error:
+ return -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+
+ def decode_page_header(self, header):
+ try:
+ return struct.unpack(">IIIIII", header)
+ except struct.error:
+ return -1, -1, -1, -1, -1, -1
+
+ def cover_page(self, recipient):
+ if self.job_total_pages > 1:
+ state = STATE_MERGE_FILES
+ else:
+ state = STATE_SINGLE_FILE
+
+ if self.cover_page_present:
+ log.debug("Creating cover page for recipient: %s" % recipient['name'])
+ fax_file, canceled = self.render_cover_page(recipient)
+
+ if canceled:
+ state = STATE_ABORTED
+ elif not fax_file:
+ state = STATE_ERROR # timeout
+ else:
+ self.recipient_file_list.insert(0, (fax_file, "application/hplip-fax",
+ "HP Fax", 'Cover Page'))
+
+ log.debug("Cover page G3 file: %s" % fax_file)
+
+ self.results[fax_file] = ERROR_SUCCESS
+
+ return state
+
+ def single_file(self, state):
+ state = STATE_SEND_FAX
+
+ log.debug("Processing single file...")
+ self.f = self.recipient_file_list[0][0]
+
+ try:
+ f_fd = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to open fax file: %s" % self.f)
+ state = STATE_ERROR
+ else:
+ header = f_fd.read(FILE_HEADER_SIZE)
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ self.results[self.f] = ERROR_SUCCESS
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ self.results[self.f] = ERROR_FAX_INVALID_FAX_FILE
+ state = STATE_ERROR
+
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi,
+ page_size, resolution, encoding))
+
+ f_fd.close()
+
+ return state
+
+
+ def merge_files(self, state):
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ log.debug(self.recipient_file_list)
+ log.debug("Merging g3 files...")
+ self.remove_temp_file = True
+
+ if self.job_total_pages:
+ f_fd, self.f = utils.make_temp_file()
+ log.debug("Temp file=%s" % self.f)
+
+ data = struct.pack(">8sBIHHBBBII", "hplip_g3", 1L, self.job_total_pages,
+ self.job_hort_dpi, self.job_vert_dpi, self.job_page_size,
+ self.job_resolution, self.job_encoding,
+ 0L, 0L)
+
+ os.write(f_fd, data)
+
+ job_page_num = 1
+
+ for fax_file in self.recipient_file_list:
+ fax_file_name = fax_file[0]
+ log.debug("Processing file: %s..." % fax_file_name)
+
+ if self.results[fax_file_name] == ERROR_SUCCESS:
+ fax_file_fd = file(fax_file_name, 'r')
+ header = fax_file_fd.read(FILE_HEADER_SIZE)
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ state = STATE_ERROR
+ break
+
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
+
+ for p in range(total_pages):
+ header = fax_file_fd.read(PAGE_HEADER_SIZE)
+
+ page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ if page_num == -1:
+ log.error("Page header error")
+ state - STATE_ERROR
+ break
+
+ header = struct.pack(">IIIIII", job_page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, 0L)
+ os.write(f_fd, header)
+
+ self.write_queue((STATUS_PROCESSING_FILES, job_page_num, ''))
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%s" %
+ (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
+
+ os.write(f_fd, fax_file_fd.read(bytes_to_read))
+ job_page_num += 1
+
+ fax_file_fd.close()
+
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+ break
+
+ else:
+ log.error("Skipping file: %s" % fax_file_name)
+ continue
+
+ os.close(f_fd)
+ log.debug("Total pages=%d" % self.job_total_pages)
+
+ return state
+
+
+ def next_recipient_gen(self):
+ for a in self.phone_num_list:
+ yield a
+
+ def next_file_gen(self):
+ for a in self.recipient_file_list:
+ yield a
+
+
+ def render_file(self, path, title, mime_type, force_single_page=False):
+ all_pages = True
+ page_range = ''
+ page_set = 0
+ nup = 1
+
+ cups.resetOptions()
+
+ if mime_type in ["application/x-cshell",
+ "application/x-perl",
+ "application/x-python",
+ "application/x-shell",
+ "application/x-sh",
+ "text/plain",]:
+
+ cups.addOption('prettyprint')
+
+ if nup > 1:
+ cups.addOption('number-up=%d' % nup)
+
+ if force_single_page:
+ cups.addOption('page-ranges=1') # Force coverpage to 1 page
+
+ sent_job_id = cups.printFile(self.current_printer, path, title)
+ cups.resetOptions()
+
+ log.debug("Job ID=%d" % sent_job_id)
+ job_id = 0
+
+ time.sleep(1)
+
+ fax_file = ''
+ complete = False
+
+ end_time = time.time() + 300.0 # wait for 5 min. max
+ while time.time() < end_time:
+ log.debug("Waiting for fax...")
+
+ result = list(self.service.CheckForWaitingFax(self.dev.device_uri, prop.username, sent_job_id))
+
+ fax_file = str(result[7])
+ log.debug("Fax file=%s" % fax_file)
+
+ if fax_file:
+ break
+
+ if self.check_for_cancel():
+ log.error("Render canceled. Canceling job #%d..." % sent_job_id)
+ cups.cancelJob(sent_job_id)
+ return '', True
+
+ time.sleep(1)
+
+ else:
+ log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
+ cups.cancelJob(sent_job_id)
+ return '', False
+
+ return fax_file, False
+
+
+ def check_for_cancel(self):
+ canceled = False
+ while self.event_queue.qsize():
+ try:
+ event = self.event_queue.get(0)
+ if event[0] == EVENT_FAX_SEND_CANCELED:
+ canceled = True
+ log.debug("Cancel pressed!")
+ except Queue.Empty:
+ break
+
+ return canceled
+
+ def render_cover_page(self, a):
+ log.debug("Creating cover page...")
+
+ pdf = self.cover_func(page_size=coverpages.PAGE_SIZE_LETTER,
+ total_pages=self.job_total_pages,
+
+ recipient_name=a['name'],
+ recipient_phone='', # ???
+ recipient_fax=a['fax'],
+
+ sender_name=self.sender_name,
+ sender_phone=user_conf.get('fax', 'voice_phone'),
+ sender_fax=self.sender_fax,
+ sender_email=user_conf.get('fax', 'email_address'),
+
+ regarding=self.cover_re,
+ message=self.cover_message,
+ preserve_formatting=self.preserve_formatting)
+
+ log.debug("PDF File=%s" % pdf)
+ fax_file, canceled = self.render_file(pdf, 'Cover Page', "application/pdf",
+ force_single_page=True)
+
+ try:
+ os.remove(pdf)
+ except IOError:
+ pass
+
+ return fax_file, canceled
+
+
+ def write_queue(self, message):
+ if self.update_queue is not None and message != self.prev_update:
+ self.update_queue.put(message)
+ time.sleep(0)
+ self.prev_update = message
+
+
+ def run(self):
+ pass
+
+
+
diff --git a/fax/faxdevice.py b/fax/faxdevice.py
new file mode 100644
index 0000000..b832d35
--- /dev/null
+++ b/fax/faxdevice.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2010 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+# Local
+from base.g import *
+from prnt import cups
+from base import device, codes
+from soapfax import SOAPFaxDevice
+from pmlfax import PMLFaxDevice
+from marvellfax import MarvellFaxDevice
+
+def FaxDevice(device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ if fax_type == FAX_TYPE_NONE:
+ if device_uri is None and printer_name is not None:
+ printers = cups.getPrinters()
+
+ for p in printers:
+ if p.name.lower() == printer_name.lower():
+ device_uri = p.device_uri
+ break
+ else:
+ raise Error(ERROR_DEVICE_NOT_FOUND)
+
+ if device_uri is not None:
+ mq = device.queryModelByURI(device_uri)
+ fax_type = mq['fax-type']
+
+ log.debug("fax-type=%d" % fax_type)
+
+ if fax_type in (FAX_TYPE_BLACK_SEND_EARLY_OPEN, FAX_TYPE_BLACK_SEND_LATE_OPEN):
+ return PMLFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_SOAP:
+ return SOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_LEDMSOAP:
+ return LEDMSOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_MARVELL:
+ return MarvellFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+ elif fax_type == FAX_TYPE_LEDM:
+ from ledmfax import LEDMFaxDevice
+ return LEDMFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
+
+
+ else:
+ raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
diff --git a/fax/filters/pstotiff b/fax/filters/pstotiff
new file mode 100755
index 0000000..2b0571e
--- /dev/null
+++ b/fax/filters/pstotiff
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+import os
+import os.path
+import time
+import sys
+import tempfile
+
+READ_SIZE = 8192
+
+total_bytes_read = 0
+temp_in_file = "-"
+
+if (len(sys.argv) > 6):
+ temp_in_file = sys.argv[6]
+
+temp_out_handle, temp_out_fname = tempfile.mkstemp()
+
+font = "-I/usr/share/cups/fonts"
+device = "-sDEVICE=tiffg4 -dMaxStripSize=0 -r204x196 -dNOPAUSE -dBATCH -dSAFER -dPARANOIDSAFER -dSHORTERRORS -dWRITESYSTEMDICT -dGHOSTSCRIPT -sstdout=%stderr -sOutputFile=" + temp_out_fname + " " + temp_in_file
+
+gs_command = "/usr/bin/gs" + " " + font + " " + device
+
+exit_code = os.system(gs_command)
+
+file_len = os.stat(temp_out_fname).st_size
+if (file_len < READ_SIZE):
+ READ_SIZE = file_len
+
+os.close(temp_out_handle)
+
+out_handle = open(temp_out_fname, mode='rb')
+while (total_bytes_read < file_len):
+ data = out_handle.read(READ_SIZE)
+ sys.stdout.write(data)
+ total_bytes_read += READ_SIZE
+out_handle.close()
+
+os.remove(temp_out_fname)
+sys.exit(0)
diff --git a/fax/filters/pstotiff.convs b/fax/filters/pstotiff.convs
new file mode 100644
index 0000000..83a265c
--- /dev/null
+++ b/fax/filters/pstotiff.convs
@@ -0,0 +1,27 @@
+# Copyright 2010 by HP.
+#
+########################################################################
+#
+# Format of Lines:
+#
+# source/type destination/type cost filter
+#
+# General Notes:
+#
+# The "cost" field is used to find the least costly filters to run
+# when converting a job file to a printable format.
+#
+# All filters *must* accept the standard command-line arguments
+# (job-id, user, title, copies, options, [filename or stdin]) to
+# work with CUPS.
+#
+
+########################################################################
+#
+# TIFF filters...
+#
+
+application/postscript image/tiff 80 pstotiff
+application/pdf image/tiff 80 pstotiff
+application/vnd.cups-pdf image/tiff 80 pstotiff
+application/vnd.cups-postscript image/tiff 80 pstotiff
diff --git a/fax/filters/pstotiff.types b/fax/filters/pstotiff.types
new file mode 100644
index 0000000..6e2f5be
--- /dev/null
+++ b/fax/filters/pstotiff.types
@@ -0,0 +1,53 @@
+# Copyright 2010-2011 by HP.
+#
+########################################################################
+#
+# Format of Lines:
+#
+# super/type rules
+#
+# "rules" can be any combination of:
+#
+# ( expr ) Parenthesis for expression grouping
+# + Logical AND
+# , or whitespace Logical OR
+# ! Logical NOT
+# match("pattern") Pattern match on filename
+# extension Pattern match on "*.extension"
+# ascii(offset,length) True if bytes are valid printable ASCII
+# (CR, NL, TAB, BS, 32-126)
+# printable(offset,length) True if bytes are printable 8-bit chars
+# (CR, NL, TAB, BS, 32-126, 128-254)
+# string(offset,"string") True if bytes are identical to string
+# istring(offset,"string") True if bytes are identical to
+# case-insensitive string
+# char(offset,value) True if byte is identical
+# short(offset,value) True if 16-bit integer is identical
+# int(offset,value) True if 32-bit integer is identical
+# locale("string") True if current locale matches string
+# contains(offset,range,"string") True if the range contains the string
+#
+# General Notes:
+#
+# MIME type names are case-insensitive. Internally they are converted
+# to lowercase. Multiple occurrences of a type will cause the provided
+# rules to be appended to the existing definition. Type names are sorted
+# in ascending order, so if two types use the same rules to resolve a type
+# (e.g. doc extension for two types), the returned type will be the first
+# type in the sorted list.
+#
+# The "printable" rule differs from the "ascii" rule in that it also
+# accepts 8-bit characters in the range 128-255.
+#
+# String constants must be surrounded by "" if they contain whitespace.
+# To insert binary data into a string, use the <hex> notation.
+#
+
+########################################################################
+#
+# Application-generated files...
+#
+
+#application/vnd.hplip-tiff tiff tif string(0,MM<002A>) string(0,II<2A00>)
+image/tiff tiff tif string(0,MM<002A>) string(0,II<2A00>)
+
diff --git a/fax/ledmfax.py b/fax/ledmfax.py
new file mode 100644
index 0000000..7a79d6e
--- /dev/null
+++ b/fax/ledmfax.py
@@ -0,0 +1,691 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: k,shunmugaraj
+# Date Created: 10/10/2010
+
+from __future__ import division
+
+# Std Lib
+import sys
+import os
+import time
+import cStringIO
+import urllib # TODO: Replace with urllib2 (urllib is deprecated in Python 3.0)
+import re
+import threading
+import struct
+import time
+import xml.parsers.expat as expat
+from stat import *
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, codes, dime, status
+from fax import *
+
+
+# **************************************************************************** #
+
+http_result_pat = re.compile("""HTTP/\d.\d\s(\d+)""", re.I)
+
+HTTP_OK = 200
+HTTP_ACCEPTED = 202
+HTTP_CREATED = 201
+HTTP_ERROR = 500
+
+PIXELS_PER_LINE = 1728
+
+# **************************************************************************** #
+setPhoneNumXML = """<?xml version=\"1.0\" encoding=\"UTF-8\"?><!--Sample XML file generated by XMLSPY v5 rel. 4 U (http://www.xmlspy.com)--><faxcfgdyn:FaxConfigDyn xmlns:faxcfgdyn=\"http://www.hp.com/schemas/imaging/con/ledm/faxconfigdyn/2009/03/03\" xmlns:dd=\"http://www.hp.com/schemas/imaging/con/dictionaries/1.0/\" xmlns:fax=\"http://www.hp.com/schemas/imaging/con/fax/2008/06/13\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.hp.com/schemas/imaging/con/ledm/faxconfigdyn/2009/03/03 ../schemas/FaxConfigDyn.xsd\"><faxcfgdyn:SystemSettings><dd:PhoneNumber>%s</dd:PhoneNumber></faxcfgdyn:SystemSettings></faxcfgdyn:FaxConfigDyn>"""
+
+setStationNameXML = """<?xml version=\"1.0\" encoding=\"UTF-8\"?><!--Sample XML file generated by XMLSPY v5 rel. 4 U (http://www.xmlspy.com)--><faxcfgdyn:FaxConfigDyn xmlns:faxcfgdyn=\"http://www.hp.com/schemas/imaging/con/ledm/faxconfigdyn/2009/03/03\" xmlns:dd=\"http://www.hp.com/schemas/imaging/con/dictionaries/1.0/\" xmlns:fax=\"http://www.hp.com/schemas/imaging/con/fax/2008/06/13\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.hp.com/schemas/imaging/con/ledm/faxconfigdyn/2009/03/03 ../schemas/FaxConfigDyn.xsd\"><faxcfgdyn:SystemSettings><dd:CompanyName>%s</dd:CompanyName></faxcfgdyn:SystemSettings></faxcfgdyn:FaxConfigDyn>"""
+
+createJobXML = """<?xml version=\"1.0\" encoding=\"UTF-8\"?><!--THIS DATA SUBJECT TO DISCLAIMER(S)INCLUDED WITH THE PRODUCT OF ORIGIN.--><fpsdyn:FaxPCSendDyn xmlns:fpsdyn=\"http://www.hp.com/schemas/imaging/con/ledm/printtofaxdyn/2008/11/24\" xmlns:dd=\"http://www.hp.com/schemas/imaging/con/dictionaries/1.0/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.hp.com/schemas/imaging/con/ledm/printtofaxdyn/2008/11/24 ../schemas/FaxPCSendDyn.xsd\"><dd:Version><dd:Revision>1.0</dd:Revision></dd:Version><fpsdyn:FaxPCSendConfig><fpsdyn:FaxTxPhoneNumber>%s</fpsdyn:FaxTxPhoneNumber><fpsdyn:NumPages>%d</fpsdyn:NumPages><fpsdyn:TTI_Control>TTI_Off</fpsdyn:TTI_Control></fpsdyn:FaxPCSendConfig></fpsdyn:FaxPCSendDyn>"""
+
+pageConfigXML = """<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!-- THIS DATA SUBJECT TO DISCLAIMER(S)INCLUDED WITH THE PRODUCT OF ORIGIN.--><fpsdyn:FaxPCSendDyn xmlns:fpsdyn=\"http://www.hp.com/schemas/imaging/con/ledm/printtofaxdyn/2008/11/24\" xmlns:dd=\"http://www.hp.com/schemas/imaging/con/dictionaries/1.0/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.hp.com/schemas/imaging/con/ledm/printtofaxdyn/2008/11/24 ../schemas/FaxPCSendDyn.xsd\"><dd:Version><dd:Revision>1.0</dd:Revision></dd:Version><fpsdyn:PageConfig><fpsdyn:PageNum>%d</fpsdyn:PageNum><fpsdyn:Width>1728</fpsdyn:Width><fpsdyn:Height>2200</fpsdyn:Height><fpsdyn:ImageType>BW</fpsdyn:ImageType><fpsdyn:Compression>mh</fpsdyn:Compression><fpsdyn:HorizontalDPI>%d</fpsdyn:HorizontalDPI><fpsdyn:VerticalDPI>%d</fpsdyn:VerticalDPI></fpsdyn:PageConfig></fpsdyn:FaxPCSendDyn>"""
+
+cancelJobXML = """<?xml version=\"1.0\" encoding=\"UTF-8\"?><!-- THIS DATA SUBJECT TO DISCLAIMER(S) INCLUDED WITH THE PRODUCT OF ORIGIN.--><j:Job xmlns:j=\"http://www.hp.com/schemas/imaging/con/ledm/jobs/2009/04/30\" xmlns:dd=\"http://www.hp.com/schemas/imaging/con/dictionaries/1.0/\" xmlns:fax=\"http://www.hp.com/schemas/imaging/con/fax/2008/06/13\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.hp.com/schemas/imaging/con/ledm/jobs/2009/04/30 ../schemas/Jobs.xsd\"><j:JobUrl>%s</j:JobUrl><j:JobState>Canceled</j:JobState></j:Job>"""
+
+# **************************************************************************** #
+class LEDMFaxDevice(FaxDevice):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ FaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+
+ if self.bus == 'net':
+ self.http_host = self.host
+ else:
+ self.http_host = 'localhost'
+
+
+ def put(self, url, post):
+ data = """PUT %s HTTP/1.1\r
+Connection: Keep-alive\r
+User-agent: hplip/2.0\r
+Host: %s\r
+Content-length: %d\r
+\r
+%s""" % (url, self.http_host, len(post), post)
+ log.log_data(data)
+ self.writeLEDM(data)
+ response = cStringIO.StringIO()
+
+ while self.readLEDM(512, response, timeout=5):
+ pass
+
+ response = response.getvalue()
+ log.log_data(response)
+ self.closeLEDM()
+
+ match = http_result_pat.match(response)
+ if match is None: return HTTP_OK
+ try:
+ code = int(match.group(1))
+ except (ValueError, TypeError):
+ code = HTTP_ERROR
+
+ return code == HTTP_OK
+
+
+ def setPhoneNum(self, num):
+ xml = setPhoneNumXML %(num)
+ log.debug("SetPhoneNum:xml Value:%s" %xml)
+ return self.put("/DevMgmt/FaxConfigDyn.xml", xml)
+
+
+ def getPhoneNum(self):
+ return self.readAttributeFromXml("/DevMgmt/FaxConfigDyn.xml",'faxcfgdyn:faxconfigdyn-faxcfgdyn:systemsettings-dd:phonenumber')
+
+ phone_num = property(getPhoneNum, setPhoneNum)
+
+
+ def setStationName(self, name):
+ xml = setStationNameXML %(name)
+ return self.put("/DevMgmt/FaxConfigDyn.xml", xml)
+
+
+ def getStationName(self):
+ return self.readAttributeFromXml("/DevMgmt/FaxConfigDyn.xml",'faxcfgdyn:faxconfigdyn-faxcfgdyn:systemsettings-dd:companyname')
+
+ station_name = property(getStationName, setStationName)
+
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ if not self.isSendFaxActive():
+
+ self.send_fax_thread = LEDMFaxSendThread(self, self.service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func,
+ preserve_formatting,
+ printer_name, update_queue,
+ event_queue)
+
+ self.send_fax_thread.start()
+ return True
+ else:
+ return False
+
+
+# **************************************************************************** #
+class LEDMFaxSendThread(FaxSendThread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ FaxSendThread.__init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func, preserve_formatting,
+ printer_name, update_queue, event_queue)
+
+ if dev.bus == 'net':
+ self.http_host = "%s:8080" % self.dev.host
+ else:
+ self.http_host = 'localhost:8080'
+
+
+ def run(self):
+
+ STATE_DONE = 0
+ STATE_ABORTED = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_READ_SENDER_INFO = 30
+ STATE_PRERENDER = 40
+ STATE_COUNT_PAGES = 50
+ STATE_NEXT_RECIPIENT = 60
+ STATE_COVER_PAGE = 70
+ STATE_SINGLE_FILE = 80
+ STATE_MERGE_FILES = 90
+ STATE_SINGLE_FILE = 100
+ STATE_SEND_FAX = 110
+ STATE_CLEANUP = 120
+ STATE_ERROR = 130
+
+ next_recipient = self.next_recipient_gen()
+
+ state = STATE_READ_SENDER_INFO
+ error_state = STATUS_ERROR
+ self.rendered_file_list = []
+
+ while state != STATE_DONE: # --------------------------------- Fax state machine
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ log.debug("STATE=(%d, 0, 0)" % state)
+
+ if state == STATE_ABORTED: # ----------------------------- Aborted (10, 0, 0)
+ log.error("Aborted by user.")
+ self.write_queue((STATUS_IDLE, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_SUCCESS: # --------------------------- Success (20, 0, 0)
+ log.debug("Success.")
+ self.write_queue((STATUS_COMPLETED, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_ERROR: # ----------------------------- Error (130, 0, 0)
+ log.error("Error, aborting.")
+ self.write_queue((error_state, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_BUSY: # ------------------------------ Busy (25, 0, 0)
+ log.error("Device busy, aborting.")
+ self.write_queue((STATUS_BUSY, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_READ_SENDER_INFO: # ------------------ Get sender info (30, 0, 0)
+ log.debug("%s State: Get sender info" % ("*"*20))
+ state = STATE_PRERENDER
+ try:
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ self.sender_name = self.dev.station_name
+ log.debug("Sender name=%s" % self.sender_name)
+ self.sender_fax = self.dev.phone_num
+ log.debug("Sender fax=%s" % self.sender_fax)
+ except Error:
+ log.error("LEDM GET failed!")
+ state = STATE_ERROR
+
+ finally:
+ self.dev.close()
+
+
+ elif state == STATE_PRERENDER: # --------------------------------- Pre-render non-G4 files (40, 0, 0)
+ log.debug("%s State: Pre-render non-G4 files" % ("*"*20))
+ state = self.pre_render(STATE_COUNT_PAGES)
+
+ elif state == STATE_COUNT_PAGES: # -------------------------------- Get total page count (50, 0, 0)
+ log.debug("%s State: Get total page count" % ("*"*20))
+ state = self.count_pages(STATE_NEXT_RECIPIENT)
+
+ elif state == STATE_NEXT_RECIPIENT: # ----------------------------- Loop for multiple recipients (60, 0, 0)
+ log.debug("%s State: Next recipient" % ("*"*20))
+ state = STATE_COVER_PAGE
+
+ try:
+ recipient = next_recipient.next()
+ log.debug("Processing for recipient %s" % recipient['name'])
+ self.write_queue((STATUS_SENDING_TO_RECIPIENT, 0, recipient['name']))
+ except StopIteration:
+ state = STATE_SUCCESS
+ log.debug("Last recipient.")
+ continue
+
+ recipient_file_list = self.rendered_file_list[:]
+
+
+ elif state == STATE_COVER_PAGE: # ---------------------------------- Create cover page (70, 0, 0)
+ log.debug("%s State: Render cover page" % ("*"*20))
+ state = self.cover_page(recipient)
+
+
+ elif state == STATE_SINGLE_FILE: # --------------------------------- Special case for single file (no merge) (80, 0, 0)
+ log.debug("%s State: Handle single file" % ("*"*20))
+ state = self.single_file(STATE_SEND_FAX)
+
+ elif state == STATE_MERGE_FILES: # --------------------------------- Merge multiple G4 files (90, 0, 0)
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ state = self.merge_files(STATE_SEND_FAX)
+
+ elif state == STATE_SEND_FAX: # ------------------------------------ Send fax state machine (110, 0, 0)
+ log.debug("%s State: Send fax" % ("*"*20))
+ state = STATE_NEXT_RECIPIENT
+
+ FAX_SEND_STATE_DONE = 0
+ FAX_SEND_STATE_ABORT = 10
+ FAX_SEND_STATE_ERROR = 20
+ FAX_SEND_STATE_BUSY = 25
+ FAX_SEND_STATE_SUCCESS = 30
+ FAX_SEND_STATE_DEVICE_OPEN = 40
+ FAX_SEND_STATE_BEGINJOB = 50
+ FAX_SEND_STATE_DOWNLOADPAGES = 60
+ FAX_SEND_STATE_ENDJOB = 70
+ FAX_SEND_STATE_CANCELJOB = 80
+ FAX_SEND_STATE_CLOSE_SESSION = 170
+
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_DEVICE_OPEN
+
+ while fax_send_state != FAX_SEND_STATE_DONE:
+
+ if self.check_for_cancel():
+ log.error("Fax send aborted.")
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if monitor_state:
+ fax_state = self.getFaxDownloadState()
+ if not fax_state in (pml.UPDN_STATE_XFERACTIVE, pml.UPDN_STATE_XFERDONE):
+ log.error("D/L error state=%d" % fax_state)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ state = STATE_ERROR
+
+ log.debug("STATE=(%d, %d, 0)" % (STATE_SEND_FAX, fax_send_state))
+
+ if fax_send_state == FAX_SEND_STATE_ABORT: # ----------------- Abort (110, 10, 0)
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CANCELJOB
+ state = STATE_ABORTED
+
+ elif fax_send_state == FAX_SEND_STATE_ERROR: # --------------- Error (110, 20, 0)
+ log.error("Fax send error.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BUSY: # ---------------- Busy (110, 25, 0)
+ log.error("Fax device busy.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_SUCCESS: # ------------- Success (110, 30, 0)
+ log.debug("Fax send success.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_NEXT_RECIPIENT
+
+ elif fax_send_state == FAX_SEND_STATE_DEVICE_OPEN: # --------- Device open (110, 40, 0)
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_BEGINJOB
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BEGINJOB: # -------------- BeginJob (110, 50, 0)
+ log.debug("%s State: BeginJob" % ("*"*20))
+ try:
+ ff = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ try:
+ header = ff.read(FILE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi, page_size,
+ resolution, encoding))
+
+ faxnum = recipient['fax'].encode('ascii')
+
+ createJob = createJobXML %(faxnum, total_pages)
+ data = self.format_http_post("/FaxPCSend/Job",len(createJob),createJob)
+ log.log_data(data)
+
+ self.dev.openLEDM()
+ self.dev.writeLEDM(data)
+ response = cStringIO.StringIO()
+ try:
+ while self.dev.readLEDM(512, response, timeout=5):
+ pass
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+ self.dev.closeLEDM()
+
+ response = response.getvalue()
+ log.log_data(response)
+ if self.get_error_code(response) == HTTP_CREATED:
+ fax_send_state = FAX_SEND_STATE_DOWNLOADPAGES
+ else:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ log.error("Create Job request failed")
+ break
+
+ responsestr = str(response)
+ pos = responsestr.find("/Jobs/JobList/",0,len(responsestr))
+ pos1 = responsestr.find("Content-Length",0,len(responsestr))
+ jobListURI = responsestr[pos:pos1].strip()
+ log.debug("jobListURI = [%s]" %(jobListURI))
+
+ elif fax_send_state == FAX_SEND_STATE_DOWNLOADPAGES: # -------------- DownloadPages (110, 60, 0)
+ log.debug("%s State: DownloadPages" % ("*"*20))
+ page = StringIO()
+ log.debug("Total Number of pages are:%d" %total_pages)
+ for p in range(total_pages):
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if fax_send_state == FAX_SEND_STATE_ABORT:
+ break
+
+ try:
+ header = ff.read(PAGE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%d" %
+ (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
+
+ if ppr != PIXELS_PER_LINE:
+ log.error("Pixels per line (width) must be %d!" % PIXELS_PER_LINE)
+
+ page.write(ff.read(bytes_to_read))
+ thumbnail = ff.read(thumbnail_bytes) # thrown away for now (should be 0 read)
+ page.seek(0)
+
+ try:
+ data = page.read(bytes_to_read)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if data == '':
+ log.error("No data!")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ pageConfigURI = self.dev.readAttributeFromXml(jobListURI,"j:job-faxpcsendstatus-resourceuri")
+ log.debug("pageConfigURI:[%s]" %pageConfigURI)
+
+ pageConfig = pageConfigXML %(page_num,hort_dpi,vert_dpi)
+ xmldata = self.format_http_post(pageConfigURI,len(pageConfig),pageConfig)
+ log.log_data(xmldata)
+
+ self.dev.openLEDM()
+ try:
+ self.dev.writeLEDM(xmldata)
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+
+ response = cStringIO.StringIO()
+ try:
+ while self.dev.readLEDM(512, response, timeout=5):
+ pass
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+
+ self.dev.closeLEDM()
+ response = (response.getvalue())
+ log.log_data(response)
+ if self.get_error_code(response) != HTTP_ACCEPTED:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ log.error("Page config data is not accepted by the device")
+ break
+
+ pageImageURI = self.dev.readAttributeFromXml(jobListURI,"j:job-faxpcsendstatus-resourceuri")
+ while(True):
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ Status, Fax_State = self.checkForError(jobListURI)
+ if Status == FAX_SEND_STATE_ERROR and (Fax_State == STATUS_ERROR_IN_TRANSMITTING or
+ Fax_State == STATUS_ERROR_IN_CONNECTING or Fax_State == STATUS_ERROR_PROBLEM_IN_FAXLINE or
+ Fax_State == STATUS_JOB_CANCEL):
+ log.debug("setting state to FAX_SEND_STATE_ERROR")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ error_state = Fax_State
+ break
+ elif Status == FAX_SEND_STATE_SUCCESS:
+ break
+
+ if fax_send_state == FAX_SEND_STATE_ABORT or fax_send_state == FAX_SEND_STATE_ERROR:
+ break
+
+
+ xmldata = self.format_http_post(pageImageURI,len(data),"","application/octet-stream")
+ log.debug("Sending Page Image XML Data [%s] to the device" %str(xmldata))
+ self.dev.openLEDM()
+ self.dev.writeLEDM(xmldata)
+ log.debug("Sending Raw Data to printer............")
+ try:
+ self.dev.writeLEDM(data)
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+
+ response = cStringIO.StringIO()
+ try:
+ while self.dev.readLEDM(512, response, timeout=10):
+ pass
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+
+ self.dev.closeLEDM()
+ response = response.getvalue()
+ log.log_data(response)
+
+ if self.get_error_code(response) != HTTP_ACCEPTED:
+ log.error("Image Data is not accepted by the device")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ page.truncate(0)
+ page.seek(0)
+
+ else:
+ fax_send_state = FAX_SEND_STATE_ENDJOB
+
+
+ elif fax_send_state == FAX_SEND_STATE_ENDJOB: # -------------- EndJob (110, 70, 0)
+ fax_send_state = FAX_SEND_STATE_SUCCESS
+
+
+ elif fax_send_state == FAX_SEND_STATE_CANCELJOB: # -------------- CancelJob (110, 80, 0)
+ log.debug("%s State: CancelJob" % ("*"*20))
+
+ xmldata = cancelJobXML %(jobListURI)
+ data = self.format_http_put(jobListURI,len(xmldata),xmldata)
+ log.log_data(data)
+
+ self.dev.openLEDM()
+ self.dev.writeLEDM(data)
+
+ response = cStringIO.StringIO()
+ try:
+ while self.dev.readLEDM(512, response, timeout=10):
+ pass
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ self.dev.closeLEDM()
+ break
+ self.dev.closeLEDM()
+ response = response.getvalue()
+ log.log_data(response)
+
+ if self.get_error_code(response) == HTTP_OK:
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ else:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ log.error("Job Cancel Request Failed")
+
+
+ elif fax_send_state == FAX_SEND_STATE_CLOSE_SESSION: # -------------- Close session (110, 170, 0)
+ log.debug("%s State: Close session" % ("*"*20))
+ log.debug("Closing session...")
+
+ try:
+ ff.close()
+ except NameError:
+ pass
+
+ #time.sleep(1)
+
+ self.dev.closeLEDM()
+ self.dev.close()
+
+ fax_send_state = FAX_SEND_STATE_DONE # Exit inner state machine
+
+
+ elif state == STATE_CLEANUP: # --------------------------------- Cleanup (120, 0, 0)
+ log.debug("%s State: Cleanup" % ("*"*20))
+
+ if self.remove_temp_file:
+ log.debug("Removing merged file: %s" % self.f)
+ try:
+ os.remove(self.f)
+ log.debug("Removed")
+ except OSError:
+ log.debug("Not found")
+
+ state = STATE_DONE # Exit outer state machine
+
+
+ def get_error_code(self, ret):
+ if not ret: return HTTP_ERROR
+
+ match = http_result_pat.match(ret)
+ if match is None: return HTTP_OK
+ try:
+ code = int(match.group(1))
+ except (ValueError, TypeError):
+ code = HTTP_ERROR
+ return code
+
+ def checkForError(self,uri):
+ stream = cStringIO.StringIO()
+ data = self.dev.FetchLEDMUrl(uri)
+ if not data:
+ log.error("Unable To read the XML data from device")
+ return ""
+
+ xmlDict = utils.XMLToDictParser().parseXML(data)
+ log.debug("Read Attribute:%s and it is value:%s" %(uri,data))
+
+ FAX_SEND_STATE_ERROR = 20
+ FAX_SEND_STATE_SUCCESS = 30
+ state = FAX_SEND_STATE_ERROR
+ Fax_send_state = STATUS_ERROR
+
+ if cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxmachinestatus']),"Transmitting")==0 \
+ and cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxerrorstatus']),"CommunicationError")== 0:
+ state = FAX_SEND_STATE_ERROR
+ Fax_send_state = STATUS_ERROR_IN_TRANSMITTING
+ elif(cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxmachinestatus']),"Connecting")==0 \
+ and cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxerrorstatus']),"NoAnswer")== 0):
+ state = FAX_SEND_STATE_ERROR
+ Fax_send_state = STATUS_ERROR_IN_CONNECTING
+ elif(cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxerrorstatus']),"PcDisconnect")==0 \
+ and cmp(str(xmlDict['j:job-faxpcsendstatus-pagestatus-state']),"Error")== 0):
+ state = FAX_SEND_STATE_ERROR
+ Fax_send_state = STATUS_ERROR_PROBLEM_IN_FAXLINE
+ elif(cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxerrorstatus']),"Stop")==0 \
+ and cmp(str(xmlDict['j:job-faxpcsendstatus-pagestatus-state']),"Error")== 0):
+ state = FAX_SEND_STATE_ERROR
+ Fax_send_state = STATUS_JOB_CANCEL
+ elif(cmp(str(xmlDict['j:job-faxpcsendstatus-faxtxmachinestatus']),"Transmitting")== 0):
+ state = FAX_SEND_STATE_SUCCESS
+ Fax_send_state = FAX_SEND_STATE_SUCCESS
+ return state,Fax_send_state
+
+ def format_http_post(self, requst, ledmlen, xmldata, content_type="text/xml; charset=utf-8"):
+ host = self.http_host
+
+ return utils.cat(
+"""POST $requst HTTP/1.1\r
+Host: $host\r
+User-Agent: hplip/2.0\r
+Content-Type: $content_type\r
+Content-Length: $ledmlen\r
+Connection: Keep-alive\r
+SOAPAction: ""\r
+\r
+$xmldata""")
+
+ def format_http_put(self, requst, ledmlen, xmldata, content_type="text/xml; charset=utf-8"):
+ host = self.http_host
+ return utils.cat(
+"""PUT $requst HTTP/1.1\r
+Host: $host\r
+User-Agent: hplip/2.0\r
+Content-Type: $content_type\r
+Content-Length: $ledmlen\r
+\r
+$xmldata""")
+
+
+
+
+
+
+
+
diff --git a/fax/ledmsoapfax.py b/fax/ledmsoapfax.py
new file mode 100644
index 0000000..b58f0c1
--- /dev/null
+++ b/fax/ledmsoapfax.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+from __future__ import division
+
+# Std Lib
+import sys
+import os
+import time
+import cStringIO
+import urllib # TODO: Replace with urllib2 (urllib is deprecated in Python 3.0)
+import re
+
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, codes, dime
+from fax import *
+from ledmfax import *
+from soapfax import SOAPFaxSendThread
+from soapfax import SOAPFaxDevice
+
+
+# **************************************************************************** #
+class LEDMSOAPFaxDevice(SOAPFaxDevice):
+
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ SOAPFaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ #LEDM Specific functions
+ def put(self, url, post):
+ data = """PUT %s HTTP/1.1\r
+Connection: Keep-alive\r
+User-agent: hplip/2.0\r
+Host: %s\r
+Content-length: %d\r
+\r
+%s""" % (url, self.http_host, len(post), post)
+ log.log_data(data)
+ self.writeEWS_LEDM(data)
+ response = cStringIO.StringIO()
+
+ while self.readEWS_LEDM(4096, response, timeout=5):
+ pass
+
+ response = response.getvalue()
+ log.log_data(response)
+ self.closeEWS_LEDM()
+
+ match = http_result_pat.match(response)
+ if match is None: return HTTP_OK
+ try:
+ code = int(match.group(1))
+ except (ValueError, TypeError):
+ code = HTTP_ERROR
+
+ return code == HTTP_OK
+
+
+ def setPhoneNum(self, num):
+ xml = setPhoneNumXML %(num)
+ log.debug("SetPhoneNum:xml Value:%s" %xml)
+ return self.put("/DevMgmt/FaxConfigDyn.xml", xml)
+
+
+ def getPhoneNum(self):
+ return self.readAttributeFromXml_EWS("/DevMgmt/FaxConfigDyn.xml",'faxcfgdyn:faxconfigdyn-faxcfgdyn:systemsettings-dd:phonenumber')
+
+ phone_num = property(getPhoneNum, setPhoneNum)
+
+
+ def setStationName(self, name):
+ xml = setStationNameXML %(name)
+ return self.put("/DevMgmt/FaxConfigDyn.xml", xml)
+
+
+ def getStationName(self):
+ return self.readAttributeFromXml_EWS("/DevMgmt/FaxConfigDyn.xml",'faxcfgdyn:faxconfigdyn-faxcfgdyn:systemsettings-dd:companyname')
+
+ station_name = property(getStationName, setStationName)
diff --git a/fax/marvellfax.py b/fax/marvellfax.py
new file mode 100644
index 0000000..4d5ced3
--- /dev/null
+++ b/fax/marvellfax.py
@@ -0,0 +1,872 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2010 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Suma Byrappa
+#
+
+# Std Lib
+import sys
+import os
+import os.path
+import struct
+import time
+import threading
+import cStringIO
+
+from stat import *
+
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, pml, codes
+from prnt import cups
+from fax import *
+import hpmudext
+
+try:
+ from ctypes import cdll
+ from ctypes import *
+ import ctypes.util as cu
+except ImportError:
+ log.error("Marvell fax support requires python-ctypes module. Exiting!")
+ sys.exit(1)
+
+
+# **************************************************************************** #
+# Marvell Message Types
+START_FAX_JOB = 0
+END_FAX_JOB = 1
+SEND_FAX_JOB = 2
+GET_FAX_LOG_ENTRY = 5
+GET_FAX_SETTINGS = 9
+SET_FAX_SETTINGS = 10
+CLEAR_FAX_STATUS = 11
+REQUEST_FAX_STATUS = 12
+FAX_DATA_BLOCK = 13
+
+SUCCESS = 0
+FAILURE = 1
+
+FAX_DATA_BLOCK_SIZE = 4096
+
+# Fax data variant header TTI header control
+TTI_NONE = 0
+TTI_PREPENDED_TO_IMAGE = 1
+TTI_OVERLAYED_ON_IMAGE = 2
+
+# **************************************************************************** #
+class MarvellFaxDevice(FaxDevice):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ FaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+
+ try:
+ sendfax_path = utils.which('hp-sendfax')
+ sendfax_a_path = os.readlink(sendfax_path+"/hp-sendfax")
+ if not os.path.isabs(sendfax_a_path):
+ sendfax_f_path = os.path.join(sendfax_path, sendfax_a_path)
+ else:
+ sendfax_f_path = sendfax_a_path
+
+ sendfax_abs_path = os.path.realpath(sendfax_f_path)
+ (head, tail) = os.path.split(sendfax_abs_path)
+
+ lib_name = head+"/fax/plugins/fax_marvell.so"
+ log.debug("Load the library %s\n" % lib_name)
+ if not os.path.exists(lib_name):
+ log.error("Loading %s failed. Try after installing plugin libraries\n" %lib_name);
+ log.info("Run \"hp-plugin\" to installa plugin libraries if you are not automatically prompted\n")
+ job_id =0;
+ self.service.SendEvent(device_uri, printer_name, EVENT_FAX_FAILED_MISSING_PLUGIN, os.getenv('USER'), job_id, "Plugin is not installed")
+ sys.exit(1)
+ else:
+ self.libfax_marvell = cdll.LoadLibrary(lib_name)
+ except Error, e:
+ log.error("Loading fax_marvell failed (%s)\n" % e.msg);
+ sys.exit(1)
+
+
+ # Creates a message packet for message type given in argument, and sends it to device
+ #
+ # 1. Gets the message packet using fax_marvell.so
+ # 2. Writes the packets to device
+ # 3. Returns the result of send operation
+ def send_packet_for_message(self, msg_type, param1=0, param2=0, status=0, data_len=0):
+ int_array_8 = c_int * 8
+ i_buf = int_array_8(0, 0, 0, 0, 0, 0, 0, 0)
+
+ result = self.libfax_marvell.create_packet(msg_type, param1, param2, status, data_len, byref(i_buf))
+ buf = buffer(i_buf)
+ log.log_data(buf, 32)
+ self.writeMarvellFax(buf)
+# self.closeMarvellFax()
+
+ return result
+
+
+ # Reads response message packet from the device for message type given in argument.
+ # Reads the response from device, and sends the data read to the caller of this method
+ # No Marvell specific code or info
+ def read_response_for_message(self, msg_type):
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(32, ret_buf, timeout=10):
+ pass
+
+ ret_buf = ret_buf.getvalue()
+ #self.closeMarvellFax()
+
+ log.debug("response_for_message (%d): response packet is\n" % msg_type)
+ log.log_data(ret_buf, 32)
+
+ return ret_buf
+
+
+ def setPhoneNum(self, num):
+ log.debug("************************* setPhoneNum (%s) START **************************" % num)
+
+ set_buf = cStringIO.StringIO()
+
+ int_array = c_int * 8
+ i_buf = int_array(0, 0, 0, 0, 0, 0, 0, 0)
+
+ char_array = c_char * 308
+ c_buf = char_array()
+
+ date_array = c_char * 15
+ date_buf = date_array()
+ t = time.localtime()
+ date_buf = "%4d%02d%02d%02d%02d%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
+ log.debug("Date and Time string is ==>")
+ log.debug(date_buf)
+
+ result = self.libfax_marvell.create_packet(SET_FAX_SETTINGS, 0, 0, 0, 0, byref(i_buf))
+ result = self.libfax_marvell.create_fax_settings_packet(self.station_name, str(num), date_buf, byref(c_buf))
+
+ msg_buf = buffer(i_buf)
+ msg_c_buf = buffer(c_buf)
+
+ for i in range(0, 32):
+ set_buf.write(msg_buf[i])
+ for i in range(0, 308):
+ set_buf.write(msg_c_buf[i])
+
+ set_buf = set_buf.getvalue()
+ log.debug("setPhoneNum: send SET_FAX_SETTINGS message and data ===> ")
+ log.log_data(set_buf, 340)
+
+ self.writeMarvellFax(set_buf)
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(32, ret_buf, timeout=10):
+ pass
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("setPhoneNum: response is %d" % response)
+
+ log.debug("************************* setPhoneNum END **************************")
+ return response
+
+
+ def getPhoneNum(self):
+ int_array_8 = c_int * 8
+ i_buf = int_array_8(0, 0, 0, 0, 0, 0, 0, 0)
+ ph_buf = int_array_8(0, 0, 0, 0, 0, 0, 0, 0)
+
+ log.debug("******************** getPhoneNum START **********************")
+
+ result = self.libfax_marvell.create_packet(GET_FAX_SETTINGS, 0, 0, 0, 0, byref(i_buf))
+
+ buf = buffer(i_buf)
+ self.writeMarvellFax(buf)
+ #self.closeMarvellFax()
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(512, ret_buf, timeout=10):
+ pass
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("create_packet: response is %d" % response)
+
+ response = self.libfax_marvell.extract_phone_number(ret_buf, ph_buf)
+ ph_num_buf = cStringIO.StringIO()
+ for i in range(0, 7):
+ if ph_buf[i]:
+ ph_num_buf.write(str(ph_buf[i]))
+
+ ph_num_buf = ph_num_buf.getvalue()
+ log.debug("getPhoneNum: ph_num_buf=%s " % (ph_num_buf))
+
+ log.debug("******************** getPhoneNum END **********************")
+ return ph_num_buf
+
+
+ # Note down the fax (phone) number
+ phone_num = property(getPhoneNum, setPhoneNum)
+
+
+ # Set the station name in the device's settings
+ #
+ def setStationName(self, name):
+ log.debug("************************* setStationName(%s) START **************************" % name)
+
+ int_array = c_int * 8
+ i_buf = int_array(0, 0, 0, 0, 0, 0, 0, 0)
+ set_buf = cStringIO.StringIO()
+
+ char_array = c_char * 308
+ c_buf = char_array()
+
+ date_array = c_char * 15
+ date_buf = date_array()
+ t = time.localtime()
+ date_buf = "%4d%02d%02d%02d%02d%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
+ log.debug("Date and Time string is ==>")
+ log.debug(date_buf)
+
+ result = self.libfax_marvell.create_packet(SET_FAX_SETTINGS, 0, 0, 0, 0, byref(i_buf))
+ result = self.libfax_marvell.create_fax_settings_packet(str(name), self.phone_num, date_buf, byref(c_buf))
+
+ msg_buf = buffer(i_buf)
+ msg_c_buf = buffer(c_buf)
+
+ for i in range(0, 32):
+ set_buf.write(msg_buf[i])
+ for i in range(0, 308):
+ set_buf.write(msg_c_buf[i])
+ set_buf = set_buf.getvalue()
+ log.debug("setStationName: SET_FAX_SETTINGS message and data ===> ")
+ log.log_data(set_buf, 340)
+
+ self.writeMarvellFax(set_buf)
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(32, ret_buf, timeout=10):
+ pass
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("setStationName: response is %d" % response)
+
+ log.debug("************************* setStationName END **************************")
+ return response
+
+
+ def getStationName(self):
+ int_array = c_int * 8
+ i_buf = int_array(0, 0, 0, 0, 0, 0, 0, 0)
+ st_buf = create_string_buffer(128)
+
+ log.debug("************************* getStationName START **************************")
+
+ result = self.libfax_marvell.create_packet(GET_FAX_SETTINGS, 0, 0, 0, 0, byref(i_buf))
+
+ buf = buffer(i_buf)
+ self.writeMarvellFax(buf)
+ #self.closeMarvellFax()
+
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(512, ret_buf, timeout=10):
+ pass
+
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("getStationName: response is %d" % response)
+
+ result = self.libfax_marvell.extract_station_name(ret_buf, st_buf)
+ log.debug("getStationName: station_name=%s ; result is %d" % (st_buf.value, result))
+
+ log.debug("************************* getStationName END **************************")
+ return st_buf.value
+
+
+ # Note down the station-name
+ station_name = property(getStationName, setStationName)
+
+
+ # Set date and time in the device's settings
+ #
+ # 1. Gets the message packet and fax_settings packet using fax_marvell.so
+ # 2. Writes the packets to the device; Reads response from the device
+ # 3. Extracts the status from the device's response
+ def setDateAndTime(self):
+ int_array = c_int * 8
+ i_buf = int_array(0, 0, 0, 0, 0, 0, 0, 0)
+
+ log.debug("************************* setDateAndTime START **************************")
+
+ c_buf = create_string_buffer(308)
+ set_buf = cStringIO.StringIO()
+ ret_buf = cStringIO.StringIO()
+ date_array = c_char * 15
+ date_buf = date_array()
+
+ t = time.localtime()
+
+ date_buf = "%4d%02d%02d%02d%02d%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
+ log.debug("Date and Time string is ==>")
+ log.debug(date_buf)
+
+ result = self.libfax_marvell.create_packet(SET_FAX_SETTINGS, 0, 0, 0, 0, byref(i_buf))
+ result = create_marvell_faxsettings_pkt(self.phone_num, self.station_name, date_buf, c_buf)
+
+ msg_buf = buffer(i_buf)
+ for i in range(0, 31):
+ set_buf.write(msg_buf[i])
+
+ set_buf.write(c_buf.raw)
+ set_buf = set_buf.getvalue()
+ self.dev.writeMarvellFax(set_buf)
+ while self.dev.readMarvellFax(32, ret_buf, timeout=5):
+ pass
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("setDateAndTime: response is %d" % response)
+
+ return response
+
+
+ # Get the state of the device
+ #
+ # 1. Gets the message packet using fax_marvell.so
+ # 2. Writes the packet to the device; Reads response from the device
+ # 3. Extracts the response status and device status from the device's response
+ def getFaxDeviceState(self):
+ log.debug("************************* getFaxDeviceState: START **************************")
+
+ int_array = c_int * 8
+ i_buf = int_array(0, 0, 0, 0, 0, 0, 0, 0)
+ param1 = c_int(0)
+
+ result = self.libfax_marvell.create_packet(REQUEST_FAX_STATUS, 0, 0, 0, 0, byref(i_buf))
+ buf = buffer(i_buf)
+ self.writeMarvellFax(buf)
+
+ ret_buf = cStringIO.StringIO()
+ while self.readMarvellFax(32, ret_buf, timeout=5):
+ pass
+ ret_buf = ret_buf.getvalue()
+ self.closeMarvellFax()
+
+ response = self.libfax_marvell.extract_response(ret_buf)
+ log.debug("getFaxDeviceState: response is %d" % response)
+
+ return response
+
+
+ # Creates a thread which does actual Fax submission the state of the device
+ #
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ if not self.isSendFaxActive():
+
+ self.send_fax_thread = MarvellFaxSendThread(self, self.service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func,
+ preserve_formatting,
+ printer_name, update_queue,
+ event_queue)
+
+ self.send_fax_thread.start()
+ return True
+ else:
+ return False
+
+
+
+# **************************************************************************** #
+# Does the actual Fax transmission
+# **************************************************************************** #
+class MarvellFaxSendThread(FaxSendThread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ FaxSendThread.__init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func, preserve_formatting,
+ printer_name, update_queue, event_queue)
+
+
+ def run(self):
+
+ STATE_DONE = 0
+ STATE_ABORTED = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_READ_SENDER_INFO = 30
+ STATE_PRERENDER = 40
+ STATE_COUNT_PAGES = 50
+ STATE_NEXT_RECIPIENT = 60
+ STATE_COVER_PAGE = 70
+ STATE_SINGLE_FILE = 80
+ STATE_MERGE_FILES = 90
+ STATE_SINGLE_FILE = 100
+ STATE_SEND_FAX = 110
+ STATE_CLEANUP = 120
+ STATE_ERROR = 130
+
+ next_recipient = self.next_recipient_gen()
+
+ rec_name = None
+ rec_num = None
+
+ state = STATE_READ_SENDER_INFO
+ self.rendered_file_list = []
+
+ while state != STATE_DONE: # --------------------------------- Fax state machine
+ if self.check_for_cancel():
+ log.debug("***** Job is Cancelled.")
+ state = STATE_ABORTED
+
+ log.debug("*************** STATE=(%d, 0, 0)" % state)
+
+ if state == STATE_ABORTED: # --------------------------------- Aborted
+ log.error("Aborted by user.")
+ self.write_queue((STATUS_IDLE, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_SUCCESS: # --------------------------------- Success
+ log.debug("Success.")
+ self.write_queue((STATUS_COMPLETED, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_ERROR: # --------------------------------- Error
+ log.error("Error, aborting.")
+ self.write_queue((STATUS_ERROR, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_BUSY: # --------------------------------- Busy
+ log.error("Device busy, aborting.")
+ self.write_queue((STATUS_BUSY, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_READ_SENDER_INFO: # --------------------------------- Get sender info
+ log.debug("%s State: Get sender info" % ("*"*20))
+ state = STATE_PRERENDER
+ try:
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ self.sender_name = self.dev.station_name
+ self.sender_fax = self.dev.phone_num
+ except Error:
+ log.error("Getting station-name and phone_num failed!")
+ state = STATE_ERROR
+
+ finally:
+ self.dev.close()
+
+
+ elif state == STATE_PRERENDER: # --------------------------------- Pre-render non-G3 files
+ log.debug("%s State: Pre-render non-G3 files" % ("*"*20))
+ state = self.pre_render(STATE_COUNT_PAGES)
+
+
+ elif state == STATE_COUNT_PAGES: # --------------------------------- Get total page count
+ log.debug("%s State: Get total page count" % ("*"*20))
+ state = self.count_pages(STATE_NEXT_RECIPIENT)
+
+
+ elif state == STATE_NEXT_RECIPIENT: # --------------------------------- Loop for multiple recipients
+ log.debug("%s State: Next recipient" % ("*"*20))
+ state = STATE_COVER_PAGE
+
+ try:
+ recipient = next_recipient.next()
+
+ self.write_queue((STATUS_SENDING_TO_RECIPIENT, 0, recipient['name']))
+
+ rec_name = recipient['name']
+ rec_num = recipient['fax'].encode('ascii')
+ log.debug("recipient is %s num is %s" % (rec_name, rec_num))
+
+ except StopIteration:
+ state = STATE_SUCCESS
+ log.debug("Last recipient.")
+ continue
+
+ self.recipient_file_list = self.rendered_file_list[:]
+
+
+ elif state == STATE_COVER_PAGE: # --------------------------------- Create cover page
+ log.debug("%s State: Render cover page" % ("*"*20))
+ state = self.cover_page(recipient)
+
+
+ elif state == STATE_SINGLE_FILE: # --------------------------------- Special case for single file (no merge)
+ log.debug("%s State: Handle single file" % ("*"*20))
+ state = self.single_file(STATE_SEND_FAX)
+
+ elif state == STATE_MERGE_FILES: # --------------------------------- Merge multiple G3 files
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ log.debug("Not merging the files for Marvell support")
+ state = STATE_SEND_FAX
+
+ elif state == STATE_SEND_FAX: # --------------------------------- Send fax state machine
+ log.debug("%s State: Send fax" % ("*"*20))
+ state = STATE_NEXT_RECIPIENT
+
+ next_file = self.next_file_gen()
+
+ FAX_SEND_STATE_DONE = 0
+ FAX_SEND_STATE_SUCCESS = 10
+ FAX_SEND_STATE_ABORT = 21
+ FAX_SEND_STATE_ERROR = 22
+ FAX_SEND_STATE_BUSY = 25
+ FAX_SEND_STATE_DEVICE_OPEN = 30
+ FAX_SEND_STATE_NEXT_FILE = 35
+ FAX_SEND_STATE_CHECK_IDLE = 40
+ FAX_SEND_STATE_START_JOB_REQUEST = 50
+ FAX_SEND_STATE_SEND_JOB_REQUEST = 60
+ FAX_SEND_STATE_SET_PARAMS = 70
+ FAX_SEND_STATE_SEND_FAX_HEADER = 80
+ FAX_SEND_STATE_SEND_FILE_DATA = 90
+ FAX_SEND_STATE_END_FILE_DATA = 100
+ FAX_SEND_STATE_END_JOB_REQUEST = 110
+ FAX_SEND_STATE_GET_LOG_INFORMATION = 120
+
+ monitor_state = False
+ current_state = SUCCESS
+ fax_send_state = FAX_SEND_STATE_DEVICE_OPEN
+
+ while fax_send_state != FAX_SEND_STATE_DONE:
+
+ if self.check_for_cancel():
+ log.error("Fax send aborted.")
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if monitor_state:
+ fax_state = self.getFaxDeviceState()
+ if fax_state != SUCCESS:
+ log.error("Device is in error state=%d" % fax_state)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ state = STATE_ERROR
+
+
+ log.debug("********* FAX_SEND_STATE=(%d, %d, %d)" % (STATE_SEND_FAX, fax_send_state, current_state))
+
+ if fax_send_state == FAX_SEND_STATE_ABORT: # -------------- Abort
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_END_JOB_REQUEST
+ state = STATE_ABORTED
+
+ elif fax_send_state == FAX_SEND_STATE_ERROR: # -------------- Error
+ log.error("Fax send error.")
+ monitor_state = False
+
+ fax_send_state = FAX_SEND_STATE_END_JOB_REQUEST
+ state = STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BUSY: # -------------- Busy
+ log.error("Fax device busy.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_END_JOB_REQUEST
+ state = STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_SUCCESS: # -------------- Success
+ log.debug("Fax send success.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_END_JOB_REQUEST
+ state = STATE_NEXT_RECIPIENT
+
+ elif fax_send_state == FAX_SEND_STATE_DEVICE_OPEN: # -------------- Device open
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_NEXT_FILE
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_NEXT_FILE: # -------------- Device open
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_CHECK_IDLE
+ try:
+ fax_file = next_file.next()
+ self.f = fax_file[0]
+ log.debug("***** file name is : %s..." % self.f)
+ except StopIteration:
+ log.debug("file(s) are sent to the device" )
+ fax_send_state = FAX_SEND_STATE_DONE
+
+
+ elif fax_send_state == FAX_SEND_STATE_CHECK_IDLE: # -------------- Check for initial idle
+ log.debug("%s State: Check idle" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_START_JOB_REQUEST
+
+ try:
+ ff = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ try:
+ header = ff.read(FILE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Magic=%s Version=%d Total Pages=%d hDPI=%d vDPI=%d Size=%d Resolution=%d Encoding=%d"
+ % (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
+
+ dev_state = self.dev.getFaxDeviceState()
+
+ if (dev_state == 0):
+ log.debug("State: device status is zero ")
+ else:
+ log.debug("State: device status is non-zero ")
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+
+ elif fax_send_state == FAX_SEND_STATE_START_JOB_REQUEST: # -------------- Request fax start
+ log.debug("%s State: Request start" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_JOB_REQUEST
+
+ file_len = os.stat(self.f)[ST_SIZE]
+ tx_data_len = file_len - FILE_HEADER_SIZE - (PAGE_HEADER_SIZE*total_pages)
+ log.debug("#### file_len = %d" % file_len)
+ log.debug("#### tx_data_len = %d" % tx_data_len)
+ ret_value = self.dev.send_packet_for_message(START_FAX_JOB, tx_data_len, 0, 0, 0)
+ if ret_value:
+ log.debug("Sending start fax request failed with %d" % ret_value)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Successfully sent start fax request")
+ ret_buf = self.dev.read_response_for_message(START_FAX_JOB)
+ dev_response = self.dev.libfax_marvell.extract_response(ret_buf)
+ if dev_response:
+ log.debug("start-fax request failed with %d" % dev_response)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("start-fax request is successful")
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_JOB_REQUEST: # -------------- Set data request
+ log.debug("%s State: Send data request" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SET_PARAMS
+
+ ret_value = self.dev.send_packet_for_message(SEND_FAX_JOB)
+ if ret_value:
+ log.debug("Sending send-data request failed with %d" % ret_value)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Successfully sent send-fax request")
+
+
+ elif fax_send_state == FAX_SEND_STATE_SET_PARAMS: # -------------- Set fax send params
+ log.debug("%s State: Set params" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_FAX_HEADER
+
+ c_buf = create_string_buffer(68)
+ set_buf = cStringIO.StringIO()
+
+ no_data = None
+ ret_val = self.dev.libfax_marvell.create_job_settings_packet(no_data, rec_num, c_buf)
+ set_buf.write(c_buf.raw)
+ set_buf = set_buf.getvalue()
+
+ self.dev.writeMarvellFax(set_buf)
+ #self.dev.closeMarvellFax()
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_FAX_HEADER: # -------------- Fax header
+ # Taken care by the device
+ fax_send_state = FAX_SEND_STATE_SEND_FILE_DATA
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_FILE_DATA: # --------------------------------- Send fax pages state machine
+ log.debug("%s State: Send pages" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_END_FILE_DATA
+ current_state = SUCCESS
+ page = StringIO()
+
+ file_len = os.stat(self.f)[ST_SIZE]
+ bytes_to_read = file_len - FILE_HEADER_SIZE - (PAGE_HEADER_SIZE*total_pages)
+
+ for p in range(total_pages):
+
+ if self.check_for_cancel():
+ current_state = FAILURE
+
+ if current_state == FAILURE:
+ break
+
+ try:
+ header = ff.read(PAGE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ current_state = FAILURE
+ continue
+
+ page_num, ppr, rpp, b_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%d" %
+ (page_num, ppr, rpp, b_to_read, thumbnail_bytes))
+
+ page.write(ff.read(b_to_read))
+ thumbnail = ff.read(thumbnail_bytes) # thrown away for now (should be 0 read)
+ page.seek(0)
+ bytes_to_write = b_to_read
+ total_read = 0
+ while (bytes_to_write > 0):
+ try:
+ data = page.read(FAX_DATA_BLOCK_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ current_state = FAILURE
+ continue
+
+ if data == '':
+ log.error("No data!")
+ current_state = FAILURE
+ break
+
+ if self.check_for_cancel():
+ current_state = FAILURE
+ log.error("Job is cancelled. Aborting...")
+ break
+
+ total_read += FAX_DATA_BLOCK_SIZE
+
+ try:
+ ret_value = self.dev.send_packet_for_message(FAX_DATA_BLOCK, 0, 0, 0, len(data))
+ if ret_value:
+ log.debug("Sending fax-data-block request failed with %d" % ret_value)
+ current_state = FAILURE
+ else:
+ log.debug("Successfully sent fax-data-block request")
+
+ self.dev.writeMarvellFax(data)
+ #self.dev.closeMarvellFax()
+ except Error:
+ log.error("Channel write error.")
+ current_state = FAILURE
+ break
+
+ bytes_to_write = bytes_to_write - FAX_DATA_BLOCK_SIZE
+
+ page.truncate(0)
+ page.seek(0)
+
+
+ elif fax_send_state == FAX_SEND_STATE_END_FILE_DATA: # -------------- end-of-data
+ log.debug("%s State: Send end-of-file-data request" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_END_JOB_REQUEST
+
+ ret_value = self.dev.send_packet_for_message(FAX_DATA_BLOCK, 0, 0, current_state, 0)
+ if ret_value:
+ log.debug("Sending fax-data-block packet failed with %d" % ret_value)
+ current_state = FAILURE
+ else:
+ log.debug("Successfully sent fax-data-block request")
+ ret_buf = self.dev.read_response_for_message(SEND_FAX_JOB)
+ dev_response = self.dev.libfax_marvell.extract_response(ret_buf)
+ if dev_response:
+ log.debug("send-fax request failed with %d" % dev_response)
+ current_state = FAILURE
+ else:
+ log.debug("send-fax request is successful")
+
+ if current_state:
+ log.debug("Exiting...")
+ sys.exit(1)
+
+
+ elif fax_send_state == FAX_SEND_STATE_END_JOB_REQUEST: # -------------- Wait for complete
+ log.debug("%s State: End the job" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_NEXT_FILE
+
+ ret_value = self.dev.send_packet_for_message(END_FAX_JOB, 0, 0, current_state, 0)
+ if ret_value:
+ log.debug("Sending end-fax-job packet failed with %d" % ret_value)
+ current_state = FAILURE
+ else:
+ log.debug("Successfully sent end-fax-job request")
+ ret_buf = self.dev.read_response_for_message(END_FAX_JOB)
+ dev_response = self.dev.libfax_marvell.extract_response(ret_buf)
+ if dev_response:
+ log.debug("end-fax-job request failed with %d" % dev_response)
+ current_state = FAILURE
+ else:
+ log.debug("end-fax-job request is successful")
+
+ if current_state != SUCCESS:
+ # There was an error during transmission...
+ log.error("An error occurred! setting fax_send_state to DONE")
+ fax_send_state = FAX_SEND_STATE_DONE
+
+ try:
+ ff.close()
+ except NameError:
+ pass
+
+ time.sleep(1)
+
+ self.dev.close()
+
+
+ elif state == STATE_CLEANUP: # --------------------------------- Cleanup
+ log.debug("%s State: Cleanup" % ("*"*20))
+
+ if self.remove_temp_file:
+ log.debug("Removing merged file: %s" % self.f)
+ try:
+ os.remove(self.f)
+ log.debug("Removed")
+ except OSError:
+ log.debug("Not found")
+
+ state = STATE_DONE
+
+
diff --git a/fax/pmlfax.py b/fax/pmlfax.py
new file mode 100644
index 0000000..45a55f2
--- /dev/null
+++ b/fax/pmlfax.py
@@ -0,0 +1,1026 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+# Std Lib
+import sys
+import os
+import os.path
+import struct
+import time
+import threading
+
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, pml, codes
+from prnt import cups
+from fax import *
+
+
+# **************************************************************************** #
+
+# Page flags
+PAGE_FLAG_NONE = 0x00
+PAGE_FLAG_NEW_PAGE = 0x01
+PAGE_FLAG_END_PAGE = 0x02
+PAGE_FLAG_NEW_DOC = 0x04
+PAGE_FLAG_END_DOC = 0x08
+PAGE_FLAG_END_STREAM = 0x10
+
+MAJOR_VER = 2
+MINOR_VER = 0
+
+MFPDTF_RASTER_BITMAP = 0 # Not used
+MFPDTF_RASTER_GRAYMAP = 1 # Not used
+MFPDTF_RASTER_MH = 2 # OfficeJets B&W Fax
+MFPDTF_RASTER_MR = 3 # Not used
+MFPDTF_RASTER_MMR = 4 # LaserJets B&W Fax
+MFPDTF_RASTER_RGB = 5 # Not used
+MFPDTF_RASTER_YCC411 = 6 # Not used
+MFPDTF_RASTER_JPEG = 7 # Color Fax
+MFPDTF_RASTER_PCL = 8 # Not used
+MFPDTF_RASTER_NOT = 9 # Not used
+
+# Data types for FH
+DT_UNKNOWN = 0
+DT_FAX_IMAGES = 1
+DT_SCANNED_IMAGES= 2
+DT_DIAL_STRINGS = 3
+DT_DEMO_PAGES = 4
+DT_SPEED_DIALS = 5
+DT_FAX_LOGS = 6
+DT_CFG_PARMS = 7
+DT_LANG_STRS = 8
+DT_JUNK_FAX_CSIDS= 9
+DT_REPORT_STRS = 10
+DT_FONTS = 11
+DT_TTI_BITMAP = 12
+DT_COUNTERS = 13
+DT_DEF_PARMS = 14
+DT_SCAN_OPTIONS = 15
+DT_FW_JOB_TABLE = 17
+
+# Raster data record types
+RT_START_PAGE = 0
+RT_RASTER = 1
+RT_END_PAGE = 2
+
+# FH
+FIXED_HEADER_SIZE = 8
+
+# Variants
+IMAGE_VARIANT_HEADER_SIZE = 10
+DIAL_STRINGS_VARIANT_HEADER_SIZE = 6
+FAX_IMAGE_VARIANT_HEADER_SIZE = 74
+
+# Data records
+SOP_RECORD_SIZE = 36
+RASTER_RECORD_SIZE = 4
+EOP_RECORD_SIZE = 12
+DIAL_STRING_RECORD_SIZE = 51
+
+# Page flags
+PAGE_FLAG_NEW_PAGE = 0x01
+PAGE_FLAG_END_PAGE = 0x02
+PAGE_FLAG_NEW_DOC = 0x04
+PAGE_FLAG_END_DOC = 0x08
+PAGE_FLAG_END_STREAM = 0x10
+
+# Fax data variant header data source
+SRC_UNKNOWN = 0
+SRC_HOST = 2
+SRC_SCANNER = 5
+SRC_HOST_THEN_SCANNER = 6
+SRC_SCANNER_THEN_HOST = 7
+
+# Fax data variant header TTI header control
+TTI_NONE = 0
+TTI_PREPENDED_TO_IMAGE = 1
+TTI_OVERLAYED_ON_IMAGE = 2
+
+RASTER_DATA_SIZE = 504
+
+
+
+# **************************************************************************** #
+class PMLFaxDevice(FaxDevice):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ FaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+
+
+ def setPhoneNum(self, num):
+ return self.setPML(pml.OID_FAX_LOCAL_PHONE_NUM, str(num))
+
+ def getPhoneNum(self):
+ return utils.printable(str(self.getPML(pml.OID_FAX_LOCAL_PHONE_NUM)[1]))
+
+ phone_num = property(getPhoneNum, setPhoneNum, doc="OID_FAX_LOCAL_PHONE_NUM")
+
+
+ def setStationName(self, name):
+ return self.setPML(pml.OID_FAX_STATION_NAME, str(name))
+
+ def getStationName(self):
+ return utils.printable(str(self.getPML(pml.OID_FAX_STATION_NAME)[1]))
+
+ station_name = property(getStationName, setStationName, doc="OID_FAX_STATION_NAME")
+
+ def setDateAndTime(self):
+ t = time.localtime()
+ p = struct.pack("BBBBBBB", t[0]-2000, t[1], t[2], t[6]+1, t[3], t[4], t[5])
+ log.debug(repr(p))
+ return self.setPML(pml.OID_DATE_AND_TIME, p)
+
+ def uploadLog(self):
+ if not self.isUloadLogActive():
+ self.upload_log_thread = UploadLogThread(self)
+ self.upload_log_thread.start()
+ return True
+ else:
+ return False
+
+ def isUploadLogActive(self):
+ if self.upload_log_thread is not None:
+ return self.upload_log_thread.isAlive()
+ else:
+ return False
+
+ def waitForUploadLogThread(self):
+ if self.upload_log_thread is not None and \
+ self.upload_log_thread.isAlive():
+
+ self.upload_log_thread.join()
+
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ if not self.isSendFaxActive():
+
+ self.send_fax_thread = PMLFaxSendThread(self, self.service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func,
+ preserve_formatting,
+ printer_name, update_queue,
+ event_queue)
+
+ self.send_fax_thread.start()
+ return True
+ else:
+ return False
+
+
+
+# **************************************************************************** #
+class PMLUploadLogThread(threading.Thread):
+ def __init__(self, dev):
+ threading.Thread.__init__(self)
+ self.dev = dev
+
+
+ def run(self):
+ STATE_DONE = 0
+ STATE_ABORT = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_DEVICE_OPEN = 28
+ STATE_CHECK_IDLE = 30
+ STATE_REQUEST_START = 40
+ STATE_WAIT_FOR_ACTIVE = 50
+ STATE_UPLOAD_DATA = 60
+ STATE_DEVICE_CLOSE = 70
+
+ state = STATE_CHECK_IDLE
+
+ while state != STATE_DONE: # --------------------------------- Log upload state machine
+ if state == STATE_ABORT:
+ pass
+ elif state == STATE_SUCCESS:
+ pass
+ elif state == STATE_BUSY:
+ pass
+
+ elif state == STATE_DEVICE_OPEN: # --------------------------------- Open device (28)
+ state = STATE_REQUEST_START
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ dev.setPML(pml.OID_UPLOAD_TIMEOUT, pml.DEFAULT_UPLOAD_TIMEOUT)
+ except Error:
+ state = STATE_ERROR
+
+ elif state == STATE_CHECK_IDLE: # --------------------------------- Check idle (30)
+ state = STATE_REQUEST_START
+ ul_state = self.getCfgUploadState()
+
+ if ul_state != pml.UPDN_STATE_IDLE:
+ state = STATE_BUSY
+
+
+ elif state == STATE_REQUEST_START: # --------------------------------- Request start (40)
+ state = STATE_WAIT_FOR_ACTIVE
+ self.dev.setPML(pml.OID_FAX_CFG_UPLOAD_DATA_TYPE, pml.FAX_CFG_UPLOAD_DATA_TYPE_FAXLOGS)
+ self.dev.setPML(pml.OID_DEVICE_CFG_UPLOAD, pml.UPDN_STATE_REQSTART)
+
+ elif state == STATE_WAIT_FOR_ACTIVE: # --------------------------------- Wait for active state (50)
+ state = STATE_UPLOAD_DATA
+
+ tries = 0
+ while True:
+ tries += 1
+ ul_state = self.getCfgUploadState()
+
+ if ul_state == pml.UPDN_STATE_XFERACTIVE:
+ break
+
+ if ul_state in (pml.UPDN_STATE_ERRORABORT, pml.UPDN_STATE_XFERDONE):
+ log.error("Cfg upload aborted!")
+ state = STATE_ERROR
+ break
+
+ if tries > 10:
+ state = STATE_ERROR
+ log.error("Unable to get into active state!")
+ break
+
+ time.sleep(0.5)
+
+ elif state == STATE_UPLOAD_DATA: # --------------------------------- Upload log data (60)
+ pass
+
+ elif state == STATE_DEVICE_CLOSE: # --------------------------------- Close device (70)
+ self.dev.close()
+
+
+
+# **************************************************************************** #
+class PMLFaxSendThread(FaxSendThread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ FaxSendThread.__init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func, preserve_formatting,
+ printer_name, update_queue, event_queue)
+
+
+ def run(self):
+ #results = {} # {'file' : error_code,...}
+
+ STATE_DONE = 0
+ STATE_ABORTED = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_READ_SENDER_INFO = 30
+ STATE_PRERENDER = 40
+ STATE_COUNT_PAGES = 50
+ STATE_NEXT_RECIPIENT = 60
+ STATE_COVER_PAGE = 70
+ STATE_SINGLE_FILE = 80
+ STATE_MERGE_FILES = 90
+ STATE_SINGLE_FILE = 100
+ STATE_SEND_FAX = 110
+ STATE_CLEANUP = 120
+ STATE_ERROR = 130
+
+ next_recipient = self.next_recipient_gen()
+
+ state = STATE_READ_SENDER_INFO
+ self.rendered_file_list = []
+
+ while state != STATE_DONE: # --------------------------------- Fax state machine
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ log.debug("STATE=(%d, 0, 0)" % state)
+
+ if state == STATE_ABORTED: # --------------------------------- Aborted (10, 0, 0)
+ log.error("Aborted by user.")
+ self.write_queue((STATUS_IDLE, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_SUCCESS: # --------------------------------- Success (20, 0, 0)
+ log.debug("Success.")
+ self.write_queue((STATUS_COMPLETED, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_ERROR: # --------------------------------- Error (130, 0, 0)
+ log.error("Error, aborting.")
+ self.write_queue((STATUS_ERROR, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_BUSY: # --------------------------------- Busy (25, 0, 0)
+ log.error("Device busy, aborting.")
+ self.write_queue((STATUS_BUSY, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_READ_SENDER_INFO: # --------------------------------- Get sender info (30, 0, 0)
+ log.debug("%s State: Get sender info" % ("*"*20))
+ state = STATE_PRERENDER
+ try:
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ self.sender_name = self.dev.station_name
+ log.debug("Sender name=%s" % self.sender_name)
+ self.sender_fax = self.dev.phone_num
+ log.debug("Sender fax=%s" % self.sender_fax)
+ except Error:
+ log.error("PML get failed!")
+ state = STATE_ERROR
+
+ finally:
+ self.dev.close()
+
+
+ elif state == STATE_PRERENDER: # --------------------------------- Pre-render non-G3 files (40, 0, 0)
+ log.debug("%s State: Pre-render non-G3 files" % ("*"*20))
+ state = self.pre_render(STATE_COUNT_PAGES)
+
+
+ elif state == STATE_COUNT_PAGES: # --------------------------------- Get total page count (50, 0, 0)
+ log.debug("%s State: Get total page count" % ("*"*20))
+ state = self.count_pages(STATE_NEXT_RECIPIENT)
+
+
+ elif state == STATE_NEXT_RECIPIENT: # --------------------------------- Loop for multiple recipients (60, 0, 0)
+ log.debug("%s State: Next recipient" % ("*"*20))
+ state = STATE_COVER_PAGE
+
+ try:
+ recipient = next_recipient.next()
+ #print recipient
+ log.debug("Processing for recipient %s" % recipient['name'])
+
+ self.write_queue((STATUS_SENDING_TO_RECIPIENT, 0, recipient['name']))
+
+ except StopIteration:
+ state = STATE_SUCCESS
+ log.debug("Last recipient.")
+ continue
+
+ self.recipient_file_list = self.rendered_file_list[:]
+
+
+ elif state == STATE_COVER_PAGE: # --------------------------------- Create cover page (70, 0, 0)
+ log.debug("%s State: Render cover page" % ("*"*20))
+ state = self.cover_page(recipient)
+
+
+ elif state == STATE_SINGLE_FILE: # --------------------------------- Special case for single file (no merge) (80, 0, 0)
+ log.debug("%s State: Handle single file" % ("*"*20))
+ state = self.single_file(STATE_SEND_FAX)
+
+ elif state == STATE_MERGE_FILES: # --------------------------------- Merge multiple G3 files (90, 0, 0)
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ state = self.merge_files(STATE_SEND_FAX)
+
+ elif state == STATE_SEND_FAX: # --------------------------------- Send fax state machine (110, 0, 0)
+ log.debug("%s State: Send fax" % ("*"*20))
+ state = STATE_NEXT_RECIPIENT
+
+ FAX_SEND_STATE_DONE = 0
+ FAX_SEND_STATE_ABORT = 10
+ FAX_SEND_STATE_ERROR = 20
+ FAX_SEND_STATE_BUSY = 25
+ FAX_SEND_STATE_SUCCESS = 30
+ FAX_SEND_STATE_DEVICE_OPEN = 40
+ FAX_SEND_STATE_SET_TOKEN = 50
+ FAX_SEND_STATE_EARLY_OPEN = 60
+ FAX_SEND_STATE_SET_PARAMS = 70
+ FAX_SEND_STATE_CHECK_IDLE = 80
+ FAX_SEND_STATE_START_REQUEST = 90
+ FAX_SEND_STATE_LATE_OPEN = 100
+ FAX_SEND_STATE_SEND_DIAL_STRINGS = 110
+ FAX_SEND_STATE_SEND_FAX_HEADER = 120
+ FAX_SEND_STATE_SEND_PAGES = 130
+ FAX_SEND_STATE_SEND_END_OF_STREAM = 140
+ FAX_SEND_STATE_WAIT_FOR_COMPLETE = 150
+ FAX_SEND_STATE_RESET_TOKEN = 160
+ FAX_SEND_STATE_CLOSE_SESSION = 170
+
+ monitor_state = False
+ error_state = pml.DN_ERROR_NONE
+ fax_send_state = FAX_SEND_STATE_DEVICE_OPEN
+
+ while fax_send_state != FAX_SEND_STATE_DONE:
+
+ if self.check_for_cancel():
+ log.error("Fax send aborted.")
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if monitor_state:
+ fax_state = self.getFaxDownloadState()
+ if not fax_state in (pml.UPDN_STATE_XFERACTIVE, pml.UPDN_STATE_XFERDONE):
+ log.error("D/L error state=%d" % fax_state)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ state = STATE_ERROR
+
+ log.debug("STATE=(%d, %d, 0)" % (STATE_SEND_FAX, fax_send_state))
+
+ if fax_send_state == FAX_SEND_STATE_ABORT: # -------------- Abort (110, 10, 0)
+ # TODO: Set D/L state to ???
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_ABORTED
+
+ elif fax_send_state == FAX_SEND_STATE_ERROR: # -------------- Error (110, 20, 0)
+ log.error("Fax send error.")
+ error_state = self.getFaxDownloadError()
+ log.debug("Error State=%d (%s)" % (error_state, pml.DN_ERROR_STR.get(error_state, "Unknown")))
+ monitor_state = False
+
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BUSY: # -------------- Busy (110, 25, 0)
+ log.error("Fax device busy.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_SUCCESS: # -------------- Success (110, 30, 0)
+ log.debug("Fax send success.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_NEXT_RECIPIENT
+
+ elif fax_send_state == FAX_SEND_STATE_DEVICE_OPEN: # -------------- Device open (110, 40, 0)
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SET_TOKEN
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_SET_TOKEN: # -------------- Acquire fax token (110, 50, 0)
+ log.debug("%s State: Acquire fax token" % ("*"*20))
+ try:
+ result_code, token = self.dev.getPML(pml.OID_FAX_TOKEN)
+ except Error:
+ log.debug("Unable to acquire fax token (1).")
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ else:
+ if result_code > pml.ERROR_MAX_OK:
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ log.debug("Skipping token acquisition.")
+ else:
+ token = time.strftime("%d%m%Y%H:%M:%S", time.gmtime())
+ log.debug("Setting token: %s" % token)
+ try:
+ self.dev.setPML(pml.OID_FAX_TOKEN, token)
+ except Error:
+ log.error("Unable to acquire fax token (2).")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ result_code, check_token = self.dev.getPML(pml.OID_FAX_TOKEN)
+
+ if check_token == token:
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ else:
+ log.error("Unable to acquire fax token (3).")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_EARLY_OPEN: # -------------- Early open (newer models) (110, 60, 0)
+ log.debug("%s State: Early open" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_CHECK_IDLE
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_EARLY_OPEN: # newer
+ log.debug("Opening fax channel.")
+ try:
+ self.dev.openFax()
+ except Error, e:
+ log.error("Unable to open channel (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Skipped.")
+
+
+ elif fax_send_state == FAX_SEND_STATE_CHECK_IDLE: # -------------- Check for initial idle (110, 80, 0)
+ log.debug("%s State: Check idle" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_START_REQUEST
+
+ dl_state = self.getFaxDownloadState()
+ tx_status = self.getFaxJobTxStatus()
+ rx_status = self.getFaxJobRxStatus()
+
+ if ((dl_state == pml.UPDN_STATE_IDLE or \
+ dl_state == pml.UPDN_STATE_ERRORABORT or \
+ dl_state == pml.UPDN_STATE_XFERDONE) and \
+ (tx_status == pml.FAXJOB_TX_STATUS_IDLE or tx_status == pml.FAXJOB_TX_STATUS_DONE) and \
+ (rx_status == pml.FAXJOB_RX_STATUS_IDLE or rx_status == pml.FAXJOB_RX_STATUS_DONE)):
+
+ # xwas if state == pml.UPDN_STATE_IDLE:
+ if dl_state == pml.UPDN_STATE_IDLE:
+ log.debug("Starting in idle state")
+ else:
+ log.debug("Resetting to idle...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_IDLE)
+ time.sleep(0.5)
+ else:
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_START_REQUEST: # -------------- Request fax start (110, 90, 0)
+ log.debug("%s State: Request start" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SET_PARAMS
+
+ dl_state = self.getFaxDownloadState()
+
+ if dl_state == pml.UPDN_STATE_IDLE:
+ log.debug("Try: 0")
+ log.debug("Setting to up/down state request start...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_REQSTART)
+ time.sleep(1)
+
+ log.debug("Waiting for active state...")
+ i = 1
+
+ while i < 10:
+ log.debug("Try: %d" % i)
+ try:
+ dl_state = self.getFaxDownloadState()
+ except Error:
+ log.error("PML/SNMP error")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if dl_state == pml.UPDN_STATE_XFERACTIVE:
+ break
+
+ time.sleep(1)
+ log.debug("Setting to up/down state request start...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_REQSTART)
+
+ i += 1
+
+ else:
+ log.error("Could not get into active state!")
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+ monitor_state = True
+
+ else:
+ log.error("Could not get into idle state!")
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+
+ elif fax_send_state == FAX_SEND_STATE_SET_PARAMS: # -------------- Set fax send params (110, 70, 0)
+ log.debug("%s State: Set params" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_LATE_OPEN
+
+ try:
+ self.dev.setPML(pml.OID_DEV_DOWNLOAD_TIMEOUT, pml.DEFAULT_DOWNLOAD_TIMEOUT)
+ self.dev.setPML(pml.OID_FAXJOB_TX_TYPE, pml.FAXJOB_TX_TYPE_HOST_ONLY)
+ log.debug("Setting date and time on device.")
+ self.dev.setDateAndTime()
+ except Error, e:
+ log.error("PML/SNMP error (%s)" % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_LATE_OPEN: # -------------- Late open (older models) (110, 100, 0)
+ log.debug("%s State: Late open" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_DIAL_STRINGS
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_LATE_OPEN: # older
+ log.debug("Opening fax channel.")
+ try:
+ self.dev.openFax()
+ except Error:
+ log.error("Unable to open channel.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Skipped.")
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_DIAL_STRINGS: # -------------- Dial strings (110, 110, 0)
+ log.debug("%s State: Send dial strings" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_FAX_HEADER
+
+ log.debug("Dialing: %s" % recipient['fax'])
+
+ log.debug("Sending dial strings...")
+ self.create_mfpdtf_fixed_header(DT_DIAL_STRINGS, True,
+ PAGE_FLAG_NEW_DOC | PAGE_FLAG_END_DOC | PAGE_FLAG_END_STREAM) # 0x1c on Windows, we were sending 0x0c
+ #print recipient
+ dial_strings = recipient['fax'].encode('ascii')
+ log.debug(repr(dial_strings))
+ self.create_mfpdtf_dial_strings(dial_strings)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_FAX_HEADER: # -------------- Fax header (110, 120, 0)
+ log.debug("%s State: Send fax header" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_PAGES
+
+ try:
+ ff = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ try:
+ header = ff.read(FILE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
+
+ log.debug("Sending fax header...")
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, True, PAGE_FLAG_NEW_DOC)
+ self.create_mfpdtf_fax_header(total_pages)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Unable to write to channel.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_PAGES: # --------------------------------- Send fax pages state machine (110, 130, 0)
+ log.debug("%s State: Send pages" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_END_OF_STREAM
+ page = StringIO()
+
+ for p in range(total_pages):
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if fax_send_state == FAX_SEND_STATE_ABORT:
+ break
+
+ try:
+ header = ff.read(PAGE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%d" %
+ (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
+
+ page.write(ff.read(bytes_to_read))
+ thumbnail = ff.read(thumbnail_bytes) # thrown away for now (should be 0 read)
+ page.seek(0)
+
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, page_flags=PAGE_FLAG_NEW_PAGE)
+ self.create_sop_record(page_num, hort_dpi, vert_dpi, ppr, rpp, encoding)
+
+ try:
+ data = page.read(RASTER_DATA_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ if data == '':
+ log.error("No data!")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ self.create_raster_data_record(data)
+ total_read = RASTER_DATA_SIZE
+
+ while True:
+ data = page.read(RASTER_DATA_SIZE)
+ total_read += RASTER_DATA_SIZE
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ if data == '':
+ self.create_eop_record(rpp)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ else:
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+ while status == pml.FAXJOB_TX_STATUS_DIALING:
+ self.write_queue((STATUS_DIALING, 0, recipient['fax']))
+ time.sleep(1.0)
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+
+ if fax_send_state not in (FAX_SEND_STATE_ABORT, FAX_SEND_STATE_ERROR):
+
+ while status == pml.FAXJOB_TX_STATUS_CONNECTING:
+ self.write_queue((STATUS_CONNECTING, 0, recipient['fax']))
+ time.sleep(1.0)
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+
+ if status == pml.FAXJOB_TX_STATUS_TRANSMITTING:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, page_flags=0)
+ self.create_raster_data_record(data)
+
+ if fax_send_state in (FAX_SEND_STATE_ABORT, FAX_SEND_STATE_ERROR):
+ break
+
+ page.truncate(0)
+ page.seek(0)
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_END_OF_STREAM: # -------------- EOS (110, 140, 0)
+ log.debug("%s State: Send EOS" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_WAIT_FOR_COMPLETE
+ log.debug("End of stream...")
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, False, PAGE_FLAG_END_STREAM)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ monitor_state = False
+
+
+ elif fax_send_state == FAX_SEND_STATE_WAIT_FOR_COMPLETE: # -------------- Wait for complete (110, 150, 0)
+ log.debug("%s State: Wait for completion" % ("*"*20))
+
+ fax_send_state = FAX_SEND_STATE_WAIT_FOR_COMPLETE
+
+ time.sleep(1.0)
+ status = self.getFaxJobTxStatus()
+
+ if status == pml.FAXJOB_TX_STATUS_DIALING:
+ self.write_queue((STATUS_DIALING, 0, recipient['fax']))
+ log.debug("Dialing ...")
+
+ elif status == pml.FAXJOB_TX_STATUS_TRANSMITTING:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+ log.debug("Transmitting ...")
+
+ elif status in (pml.FAXJOB_TX_STATUS_DONE, pml.FAXJOB_RX_STATUS_IDLE):
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_NEXT_RECIPIENT
+ log.debug("Transmitting done or idle ...")
+
+ else:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+ log.debug("Pending ...")
+
+
+ elif fax_send_state == FAX_SEND_STATE_RESET_TOKEN: # -------------- Release fax token (110, 160, 0)
+ log.debug("%s State: Release fax token" % ("*"*20))
+ self.write_queue((STATUS_CLEANUP, 0, ''))
+
+ try:
+ self.dev.setPML(pml.OID_FAX_TOKEN, '\x00'*16)
+ except Error:
+ log.error("Unable to release fax token.")
+
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+
+
+ elif fax_send_state == FAX_SEND_STATE_CLOSE_SESSION: # -------------- Close session (110, 170, 0)
+ log.debug("%s State: Close session" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_DONE
+ log.debug("Closing session...")
+
+ try:
+ mm.close()
+ except NameError:
+ pass
+
+ try:
+ ff.close()
+ except NameError:
+ pass
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_LATE_OPEN:
+ log.debug("Closing fax channel.")
+ self.dev.closeFax()
+
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_IDLE)
+
+ time.sleep(1)
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_EARLY_OPEN:
+ log.debug("Closing fax channel.")
+ self.dev.closeFax()
+
+ self.dev.close()
+
+
+ elif state == STATE_CLEANUP: # --------------------------------- Cleanup (120, 0, 0)
+ log.debug("%s State: Cleanup" % ("*"*20))
+
+ if self.remove_temp_file:
+ log.debug("Removing merged file: %s" % self.f)
+ try:
+ os.remove(self.f)
+ log.debug("Removed")
+ except OSError:
+ log.debug("Not found")
+
+ state = STATE_DONE
+
+
+
+# --------------------------------- Support functions
+
+
+ def getFaxDownloadState(self):
+ result_code, state = self.dev.getPML(pml.OID_FAX_DOWNLOAD)
+ if state:
+ log.debug("D/L State=%d (%s)" % (state, pml.UPDN_STATE_STR.get(state, 'Unknown')))
+ return state
+ else:
+ return pml.UPDN_STATE_ERRORABORT
+
+ def getFaxDownloadError(self):
+ result_code, state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
+ if state:
+ return state
+ else:
+ return pml.DN_ERROR_UNKNOWN
+
+ def getFaxJobTxStatus(self):
+ result_code, status = self.dev.getPML(pml.OID_FAXJOB_TX_STATUS)
+ if status:
+ log.debug("Tx Status=%d (%s)" % (status, pml.FAXJOB_TX_STATUS_STR.get(status, 'Unknown')))
+ return status
+ else:
+ return pml.FAXJOB_TX_STATUS_IDLE
+
+ def getFaxJobRxStatus(self):
+ result_code, status = self.dev.getPML(pml.OID_FAXJOB_RX_STATUS)
+ if status:
+ log.debug("Rx Status=%d (%s)" % (status, pml.FAXJOB_RX_STATUS_STR.get(status, 'Unknown')))
+ return status
+ else:
+ return pml.FAXJOB_RX_STATUS_IDLE
+
+ def getCfgUploadState(self):
+ result_code, state = self.dev.getPML(pml.OID_DEVICE_CFG_UPLOAD)
+ if state:
+ log.debug("Cfg Upload State = %d (%s)" % (state, pml.UPDN_STATE_STR.get(state, 'Unknown')))
+ return state
+ else:
+ return pml.UPDN_STATE_ERRORABORT
+
+ def create_mfpdtf_fixed_header(self, data_type, send_variant=False, page_flags=0):
+ header_len = FIXED_HEADER_SIZE
+
+ if send_variant:
+ if data_type == DT_DIAL_STRINGS:
+ header_len += DIAL_STRINGS_VARIANT_HEADER_SIZE
+
+ elif data_type == DT_FAX_IMAGES:
+ header_len += FAX_IMAGE_VARIANT_HEADER_SIZE
+
+ self.stream.write(struct.pack("<IHBB",
+ 0, header_len, data_type, page_flags))
+
+
+ def create_mfpdtf_dial_strings(self, number):
+ p = struct.pack("<BBHH51s",
+ MAJOR_VER, MINOR_VER,
+ 1, 51, number[:51])
+ log.debug(repr(p))
+ self.stream.write(p)
+
+
+ def adjust_fixed_header_block_size(self):
+ size = self.stream.tell()
+ self.stream.seek(0)
+ self.stream.write(struct.pack("<I", size))
+
+
+ def create_sop_record(self, page_num, hort_dpi, vert_dpi, ppr, rpp, encoding, bpp=1):
+ self.stream.write(struct.pack("<BBHHHIHHHHHHIHHHH",
+ RT_START_PAGE, encoding, page_num,
+ ppr, bpp,
+ rpp, 0x00, hort_dpi, 0x00, vert_dpi,
+ ppr, bpp,
+ rpp, 0x00, hort_dpi, 0x00, vert_dpi))
+
+
+ def create_eop_record(self, rpp):
+ self.stream.write(struct.pack("<BBBBII",
+ RT_END_PAGE, 0, 0, 0,
+ rpp, 0,))
+
+
+ def create_raster_data_record(self, data):
+ assert len(data) <= RASTER_DATA_SIZE
+ self.stream.write(struct.pack("<BBH",
+ RT_RASTER, 0, len(data),))
+ self.stream.write(data)
+
+
+ def create_mfpdtf_fax_header(self, total_pages):
+ self.stream.write(struct.pack("<BBBHBI20s20s20sI",
+ MAJOR_VER, MINOR_VER, SRC_HOST, total_pages,
+ TTI_PREPENDED_TO_IMAGE, 0, '', '', '', 0))
+
+
+ def write_stream(self):
+ self.adjust_fixed_header_block_size()
+ self.dev.writeFax(self.stream.getvalue())
+ self.stream.truncate(0)
+ self.stream.seek(0)
diff --git a/fax/ppd/HP-Fax-hpcups.ppd.gz b/fax/ppd/HP-Fax-hpcups.ppd.gz
new file mode 100644
index 0000000..bafd439
--- /dev/null
+++ b/fax/ppd/HP-Fax-hpcups.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax-hpijs.ppd.gz b/fax/ppd/HP-Fax-hpijs.ppd.gz
new file mode 100644
index 0000000..612bcaf
--- /dev/null
+++ b/fax/ppd/HP-Fax-hpijs.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax2-hpcups.ppd.gz b/fax/ppd/HP-Fax2-hpcups.ppd.gz
new file mode 100644
index 0000000..ffc35f3
--- /dev/null
+++ b/fax/ppd/HP-Fax2-hpcups.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax2-hpijs.ppd.gz b/fax/ppd/HP-Fax2-hpijs.ppd.gz
new file mode 100644
index 0000000..b228073
--- /dev/null
+++ b/fax/ppd/HP-Fax2-hpijs.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax3-hpcups.ppd.gz b/fax/ppd/HP-Fax3-hpcups.ppd.gz
new file mode 100644
index 0000000..4e289b3
--- /dev/null
+++ b/fax/ppd/HP-Fax3-hpcups.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax3-hpijs.ppd.gz b/fax/ppd/HP-Fax3-hpijs.ppd.gz
new file mode 100644
index 0000000..cb6116c
--- /dev/null
+++ b/fax/ppd/HP-Fax3-hpijs.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax4-hpcups.ppd.gz b/fax/ppd/HP-Fax4-hpcups.ppd.gz
new file mode 100644
index 0000000..40a6f5b
--- /dev/null
+++ b/fax/ppd/HP-Fax4-hpcups.ppd.gz
Binary files differ
diff --git a/fax/ppd/HP-Fax4-hpijs.ppd.gz b/fax/ppd/HP-Fax4-hpijs.ppd.gz
new file mode 100644
index 0000000..2fee3b0
--- /dev/null
+++ b/fax/ppd/HP-Fax4-hpijs.ppd.gz
Binary files differ
diff --git a/fax/soapfax.py b/fax/soapfax.py
new file mode 100644
index 0000000..01af1cc
--- /dev/null
+++ b/fax/soapfax.py
@@ -0,0 +1,719 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+from __future__ import division
+
+# Std Lib
+import sys
+import os
+import time
+import cStringIO
+import urllib # TODO: Replace with urllib2 (urllib is deprecated in Python 3.0)
+import re
+
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, codes, dime
+from fax import *
+
+#import xml.parsers.expat as expat
+
+
+# **************************************************************************** #
+
+http_result_pat = re.compile("""HTTP/\d.\d\s(\d+)""", re.I)
+
+
+TIME_FORMAT_AM_PM = 1
+TIME_FORMAT_24HR = 2
+
+DATE_FORMAT_MM_DD_YYYY = 1
+DATE_FORMAT_DD_MM_YYYY = 2
+DATE_FORMAT_YYYY_MM_DD = 3
+
+AM = 1
+PM = 0
+
+HTTP_OK = 200
+HTTP_ERROR = 500
+
+PIXELS_PER_LINE = 2528
+
+
+# **************************************************************************** #
+class SOAPFaxDevice(FaxDevice):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ FaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+
+ if self.bus == 'net':
+ self.http_host = self.host
+ else:
+ self.http_host = 'localhost'
+
+
+ def post(self, url, post):
+ s = []
+ for k, v in post.items():
+ s.append("%s=%s" % (k, urllib.quote(str(v))))
+
+ s = '&'.join(s)
+
+ log.debug(s)
+
+ data = """POST %s HTTP/1.1
+Connection: Keep-alive
+User-agent: hplip/2.0
+Host: %s
+Content-length: %d
+Cache-control: No-cache
+
+%s""" % (url, self.http_host, len(s), s)
+
+ log.log_data(data)
+ self.writeEWS(data)
+ ret = cStringIO.StringIO()
+
+ while self.readEWS(4096, ret, timeout=5):
+ pass
+
+ ret = ret.getvalue()
+
+ log.log_data(ret)
+
+ self.closeEWS()
+
+ match = http_result_pat.match(ret)
+
+ try:
+ code = int(match.group(1))
+ except (ValueError, TypeError):
+ code = HTTP_ERROR
+
+ return code == HTTP_OK
+
+
+ def setPhoneNum(self, num):
+ return self.post("/hp/device/set_config.html", {"FaxNumber": str(num)})
+
+
+ def getPhoneNum(self):
+ stream = cStringIO.StringIO()
+ self.getEWSUrl("/hp/device/settings_fax_setup_wizard.xml", stream)
+ fax_setup = utils.XMLToDictParser().parseXML(stream.getvalue())
+ return fax_setup['faxsetupwizard-faxvoicenumber-faxnumber']
+
+ phone_num = property(getPhoneNum, setPhoneNum)
+
+
+ def setStationName(self, name):
+ return self.post("/hp/device/set_config.html", {"FaxCompanyName": str(name)})
+
+
+ def getStationName(self):
+ stream = cStringIO.StringIO()
+ self.getEWSUrl("/hp/device/settings_fax_setup_wizard.xml", stream)
+ fax_setup = utils.XMLToDictParser().parseXML(stream.getvalue())
+ return fax_setup['faxsetupwizard-userinformation-faxcompanyname']
+
+ station_name = property(getStationName, setStationName)
+
+
+ def setDateAndTime(self):
+ stream = cStringIO.StringIO()
+ self.getEWSUrl("/hp/device/settings_fax_setup_wizard.xml", stream)
+ fax_setup = utils.XMLToDictParser().parseXML(stream.getvalue())
+ timeformat = fax_setup['faxsetupwizard-time-timeformat']
+
+ try:
+ timeformat = int(timeformat)
+ except (ValueError, TypeError):
+ timeformat = TIME_FORMAT_AM_PM
+
+ log.debug("timeformat: %d" % timeformat)
+
+ dateformat = fax_setup['faxsetupwizard-date-dateformat']
+
+ try:
+ dateformat = int(dateformat)
+ except (ValueError, TypeError):
+ dateformat = DATE_FORMAT_DD_MM_YYYY
+
+ log.debug("dateformat: %d" % dateformat)
+
+ t = time.localtime()
+ hr = t[3]
+
+ am_pm = PM
+ if t[3] < 12:
+ am_pm = AM
+
+ if timeformat == TIME_FORMAT_AM_PM and hr > 12:
+ hr -= 12
+
+ post = {"DateFormat" : dateformat,
+ "Year" : t[0],
+ "Month" : t[1],
+ "Day" : t[2],
+ "TimeFormat" : timeformat,
+ "Hour" : hr,
+ "Minute" : t[4]}
+
+ if timeformat == TIME_FORMAT_AM_PM:
+ post['AM'] = am_pm
+
+ return self.post("/hp/device/set_config.html", post)
+
+
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ if not self.isSendFaxActive():
+
+ self.send_fax_thread = SOAPFaxSendThread(self, self.service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func,
+ preserve_formatting,
+ printer_name, update_queue,
+ event_queue)
+
+ self.send_fax_thread.start()
+ return True
+ else:
+ return False
+
+
+# **************************************************************************** #
+class SOAPFaxSendThread(FaxSendThread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ FaxSendThread.__init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func, preserve_formatting,
+ printer_name, update_queue, event_queue)
+
+ self.job_id = utils.gen_random_uuid()
+ log.debug("JobId: %s" % self.job_id)
+
+ if dev.bus == 'net':
+ self.http_host = "%s:8295" % self.dev.host
+ else:
+ self.http_host = 'localhost:8295'
+
+ #self.http_host = 'localhost'
+
+
+ def run(self):
+ #results = {} # {'file' : error_code,...}
+
+ STATE_DONE = 0
+ STATE_ABORTED = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_READ_SENDER_INFO = 30
+ STATE_PRERENDER = 40
+ STATE_COUNT_PAGES = 50
+ STATE_NEXT_RECIPIENT = 60
+ STATE_COVER_PAGE = 70
+ STATE_SINGLE_FILE = 80
+ STATE_MERGE_FILES = 90
+ STATE_SINGLE_FILE = 100
+ STATE_SEND_FAX = 110
+ STATE_CLEANUP = 120
+ STATE_ERROR = 130
+
+ next_recipient = self.next_recipient_gen()
+
+ state = STATE_READ_SENDER_INFO
+ self.rendered_file_list = []
+
+ while state != STATE_DONE: # --------------------------------- Fax state machine
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ log.debug("STATE=(%d, 0, 0)" % state)
+
+ if state == STATE_ABORTED: # --------------------------------- Aborted (10, 0, 0)
+ log.error("Aborted by user.")
+ self.write_queue((STATUS_IDLE, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_SUCCESS: # --------------------------------- Success (20, 0, 0)
+ log.debug("Success.")
+ self.write_queue((STATUS_COMPLETED, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_ERROR: # --------------------------------- Error (130, 0, 0)
+ log.error("Error, aborting.")
+ self.write_queue((STATUS_ERROR, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_BUSY: # --------------------------------- Busy (25, 0, 0)
+ log.error("Device busy, aborting.")
+ self.write_queue((STATUS_BUSY, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_READ_SENDER_INFO: # --------------------------------- Get sender info (30, 0, 0)
+ log.debug("%s State: Get sender info" % ("*"*20))
+ state = STATE_PRERENDER
+ try:
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ self.sender_name = self.dev.station_name
+ log.debug("Sender name=%s" % self.sender_name)
+ self.sender_fax = self.dev.phone_num
+ log.debug("Sender fax=%s" % self.sender_fax)
+ except Error:
+ log.error("HTTP GET failed!")
+ state = STATE_ERROR
+
+ finally:
+ self.dev.close()
+
+
+ elif state == STATE_PRERENDER: # --------------------------------- Pre-render non-G4 files (40, 0, 0)
+ log.debug("%s State: Pre-render non-G4 files" % ("*"*20))
+ state = self.pre_render(STATE_COUNT_PAGES)
+
+ elif state == STATE_COUNT_PAGES: # --------------------------------- Get total page count (50, 0, 0)
+ log.debug("%s State: Get total page count" % ("*"*20))
+ state = self.count_pages(STATE_NEXT_RECIPIENT)
+
+ elif state == STATE_NEXT_RECIPIENT: # --------------------------------- Loop for multiple recipients (60, 0, 0)
+ log.debug("%s State: Next recipient" % ("*"*20))
+ state = STATE_COVER_PAGE
+
+ try:
+ recipient = next_recipient.next()
+ log.debug("Processing for recipient %s" % recipient['name'])
+ self.write_queue((STATUS_SENDING_TO_RECIPIENT, 0, recipient['name']))
+ except StopIteration:
+ state = STATE_SUCCESS
+ log.debug("Last recipient.")
+ continue
+
+ recipient_file_list = self.rendered_file_list[:]
+
+
+ elif state == STATE_COVER_PAGE: # --------------------------------- Create cover page (70, 0, 0)
+ log.debug("%s State: Render cover page" % ("*"*20))
+ state = self.cover_page(recipient)
+
+
+ elif state == STATE_SINGLE_FILE: # --------------------------------- Special case for single file (no merge) (80, 0, 0)
+ log.debug("%s State: Handle single file" % ("*"*20))
+ state = self.single_file(STATE_SEND_FAX)
+
+ elif state == STATE_MERGE_FILES: # --------------------------------- Merge multiple G4 files (90, 0, 0)
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ state = self.merge_files(STATE_SEND_FAX)
+
+ elif state == STATE_SEND_FAX: # --------------------------------- Send fax state machine (110, 0, 0)
+ log.debug("%s State: Send fax" % ("*"*20))
+ state = STATE_NEXT_RECIPIENT
+
+ FAX_SEND_STATE_DONE = 0
+ FAX_SEND_STATE_ABORT = 10
+ FAX_SEND_STATE_ERROR = 20
+ FAX_SEND_STATE_BUSY = 25
+ FAX_SEND_STATE_SUCCESS = 30
+ FAX_SEND_STATE_DEVICE_OPEN = 40
+ FAX_SEND_STATE_BEGINJOB = 50
+ FAX_SEND_STATE_DOWNLOADPAGES = 60
+ FAX_SEND_STATE_ENDJOB = 70
+ FAX_SEND_STATE_CANCELJOB = 80
+ FAX_SEND_STATE_CLOSE_SESSION = 170
+
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_DEVICE_OPEN
+
+ while fax_send_state != FAX_SEND_STATE_DONE:
+
+ if self.check_for_cancel():
+ log.error("Fax send aborted.")
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if monitor_state:
+ fax_state = self.getFaxDownloadState()
+ if not fax_state in (pml.UPDN_STATE_XFERACTIVE, pml.UPDN_STATE_XFERDONE):
+ log.error("D/L error state=%d" % fax_state)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ state = STATE_ERROR
+
+ log.debug("STATE=(%d, %d, 0)" % (STATE_SEND_FAX, fax_send_state))
+
+ if fax_send_state == FAX_SEND_STATE_ABORT: # -------------- Abort (110, 10, 0)
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CANCELJOB
+ state = STATE_ABORTED
+
+ elif fax_send_state == FAX_SEND_STATE_ERROR: # -------------- Error (110, 20, 0)
+ log.error("Fax send error.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BUSY: # -------------- Busy (110, 25, 0)
+ log.error("Fax device busy.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_SUCCESS: # -------------- Success (110, 30, 0)
+ log.debug("Fax send success.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ state = STATE_NEXT_RECIPIENT
+
+ elif fax_send_state == FAX_SEND_STATE_DEVICE_OPEN: # -------------- Device open (110, 40, 0)
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_BEGINJOB
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BEGINJOB: # -------------- BeginJob (110, 50, 0)
+ log.debug("%s State: BeginJob" % ("*"*20))
+
+ try:
+ ff = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ try:
+ header = ff.read(FILE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi, page_size,
+ resolution, encoding))
+
+ job_id = self.job_id
+ delay = 0
+ faxnum = recipient['fax'].encode('ascii')
+ speeddial = 0
+
+ if resolution == RESOLUTION_STD:
+ res = "STANDARD"
+ elif resolution == RESOLUTION_FINE:
+ res = "FINE"
+ elif resolution == RESOLUTION_300DPI:
+ res = "SUPERFINE"
+
+ soap = utils.cat(
+"""<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><Fax:BeginJob xmlns:Fax="urn:Fax"><ticket xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Fax:Ticket"><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">$job_id</jobId><resolution xsi:type="Fax:Resolution">$res</resolution><delay xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:positiveInteger">$delay</delay><phoneNumber xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">$faxnum</phoneNumber><speedDial xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:positiveInteger">$speeddial</speedDial></ticket></Fax:BeginJob></SOAP-ENV:Body></SOAP-ENV:Envelope>""")
+
+ data = self.format_http(soap)
+ log.log_data(data)
+
+ if log.is_debug():
+ file('beginjob.log', 'w').write(data)
+
+ self.dev.openSoapFax()
+ self.dev.writeSoapFax(data)
+ ret = cStringIO.StringIO()
+
+ while self.dev.readSoapFax(8192, ret, timeout=5):
+ pass
+
+ ret = ret.getvalue()
+
+ if log.is_debug():
+ file('beginjob_ret.log', 'w').write(ret)
+
+ log.log_data(ret)
+ self.dev.closeSoapFax()
+
+ if self.get_error_code(ret) == HTTP_OK:
+ fax_send_state = FAX_SEND_STATE_DOWNLOADPAGES
+ else:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_DOWNLOADPAGES: # -------------- DownloadPages (110, 60, 0)
+ log.debug("%s State: DownloadPages" % ("*"*20))
+ page = StringIO()
+ for p in range(total_pages):
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if fax_send_state == FAX_SEND_STATE_ABORT:
+ break
+
+ try:
+ header = ff.read(PAGE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%d" %
+ (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
+
+ if ppr != PIXELS_PER_LINE:
+ log.error("Pixels per line (width) must be %d!" % PIXELS_PER_LINE)
+
+ page.write(ff.read(bytes_to_read))
+ thumbnail = ff.read(thumbnail_bytes) # thrown away for now (should be 0 read)
+ page.seek(0)
+
+ try:
+ data = page.read(bytes_to_read)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if data == '':
+ log.error("No data!")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ height = rpp
+ job_id = self.job_id
+
+ soap = utils.cat(
+"""<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string" SOAP-ENV:mustUnderstand="1">$job_id</jobId></SOAP-ENV:Header><SOAP-ENV:Body><Fax:DownloadPage xmlns:Fax="urn:Fax"><height xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:positiveInteger">$height</height></Fax:DownloadPage></SOAP-ENV:Body></SOAP-ENV:Envelope>""")
+
+ m = dime.Message()
+ m.add_record(dime.Record("cid:id0", "http://schemas.xmlsoap.org/soap/envelope/",
+ dime.TYPE_T_URI, soap))
+
+ m.add_record(dime.Record("", "image/g4fax", dime.TYPE_T_MIME, data))
+
+ output = cStringIO.StringIO()
+ m.generate(output)
+ data = self.format_http(output.getvalue(), content_type="application/dime")
+ log.log_data(data)
+ if log.is_debug():
+ file('downloadpages%d.log' % p, 'w').write(data)
+
+ try:
+ self.dev.writeSoapFax(data)
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ ret = cStringIO.StringIO()
+
+ try:
+ while self.dev.readSoapFax(8192, ret, timeout=5):
+ pass
+ except Error:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ ret = ret.getvalue()
+
+ if log.is_debug():
+ file('downloadpages%d_ret.log' % p, 'w').write(ret)
+
+ log.log_data(ret)
+ self.dev.closeSoapFax()
+
+ if self.get_error_code(ret) != HTTP_OK:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ page.truncate(0)
+ page.seek(0)
+
+ else:
+ fax_send_state = FAX_SEND_STATE_ENDJOB
+
+
+ elif fax_send_state == FAX_SEND_STATE_ENDJOB: # -------------- EndJob (110, 70, 0)
+ log.debug("%s State: EndJob" % ("*"*20))
+
+ job_id = self.job_id
+
+ soap = utils.cat(
+"""<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string" SOAP-ENV:mustUnderstand="1">$job_id</jobId></SOAP-ENV:Header><SOAP-ENV:Body><Fax:EndJob xmlns:Fax="urn:Fax"><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">$job_id</jobId></Fax:EndJob></SOAP-ENV:Body></SOAP-ENV:Envelope>""")
+
+ data = self.format_http(soap)
+
+ log.log_data(data)
+
+ if log.is_debug():
+ file('endjob.log', 'w').write(data)
+
+ self.dev.writeSoapFax(data)
+ ret = cStringIO.StringIO()
+
+ while self.dev.readSoapFax(8192, ret, timeout=5):
+ pass
+
+ ret = ret.getvalue()
+
+ if log.is_debug():
+ file('endjob_ret.log', 'w').write(ret)
+
+ log.log_data(ret)
+ self.dev.closeSoapFax()
+
+ if self.get_error_code(ret) == HTTP_OK:
+ fax_send_state = FAX_SEND_STATE_SUCCESS
+ else:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_CANCELJOB: # -------------- CancelJob (110, 80, 0)
+ log.debug("%s State: CancelJob" % ("*"*20))
+
+ job_id = self.job_id
+
+ soap = utils.cat(
+"""<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string" SOAP-ENV:mustUnderstand="1">$job_id</jobId></SOAP-ENV:Header><SOAP-ENV:Body><Fax:CancelJob xmlns:Fax="urn:Fax"><jobId xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">$job_id</jobId></Fax:CancelJob></SOAP-ENV:Body></SOAP-ENV:Envelope>""")
+
+ data = self.format_http(soap)
+
+ log.log_data(data)
+
+ if log.is_debug():
+ file('canceljob.log', 'w').write(data)
+
+ self.dev.writeSoapFax(data)
+ ret = cStringIO.StringIO()
+
+ while self.dev.readSoapFax(8192, ret, timeout=5):
+ pass
+
+ ret = ret.getvalue()
+
+ if log.is_debug():
+ file('canceljob_ret.log', 'w').write(ret)
+
+ log.log_data(ret)
+ self.dev.closeSoapFax()
+
+ if self.get_error_code(ret) == HTTP_OK:
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+ else:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_CLOSE_SESSION: # -------------- Close session (110, 170, 0)
+ log.debug("%s State: Close session" % ("*"*20))
+ log.debug("Closing session...")
+
+ try:
+ mm.close()
+ except NameError:
+ pass
+
+ try:
+ ff.close()
+ except NameError:
+ pass
+
+ time.sleep(1)
+
+ self.dev.closeSoapFax()
+ self.dev.close()
+
+ fax_send_state = FAX_SEND_STATE_DONE # Exit inner state machine
+
+
+ elif state == STATE_CLEANUP: # --------------------------------- Cleanup (120, 0, 0)
+ log.debug("%s State: Cleanup" % ("*"*20))
+
+ if self.remove_temp_file:
+ log.debug("Removing merged file: %s" % self.f)
+ try:
+ os.remove(self.f)
+ log.debug("Removed")
+ except OSError:
+ log.debug("Not found")
+
+ state = STATE_DONE # Exit outer state machine
+
+
+ def get_error_code(self, ret):
+ if not ret: return HTTP_ERROR
+
+ match = http_result_pat.match(ret)
+
+ if match is None: return HTTP_OK
+ try:
+ code = int(match.group(1))
+ except (ValueError, TypeError):
+ code = HTTP_ERROR
+
+ return code
+
+
+ def format_http(self, soap, content_type="text/xml; charset=utf-8"):
+ host = self.http_host
+ soap_len = len(soap)
+
+ return utils.cat(
+"""POST / HTTP/1.1\r
+Host: $host\r
+User-Agent: hplip/2.0\r
+Content-Type: $content_type\r
+Content-Length: $soap_len\r
+Connection: close\r
+SOAPAction: ""\r
+\r
+$soap""")
+
+
+
+