2 # -*- coding: utf-8 -*-
4 # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 # Thanks to Henrique M. Holschuh <hmh@debian.org> for various security patches
26 __title__ = 'PC Sendfax Utility'
27 __mod__ = 'hp-sendfax'
28 __doc__ = "PC send fax for HPLIP supported multifunction printers."
41 import base.utils as utils
42 from base import device, tui, module
44 username = prop.username
50 mod = module.Module(__mod__, __title__, __version__, __doc__, None,
51 (GUI_MODE, NON_INTERACTIVE_MODE),
52 (UI_TOOLKIT_QT3, UI_TOOLKIT_QT4))
54 mod.setUsage(module.USAGE_FLAG_DEVICE_ARGS | module.USAGE_FLAG_SUPRESS_G_DEBUG_FLAG,
56 ("Specify the fax number(s):", "-f<number(s)> or --faxnum=<number(s)> or --fax-num=<number(s)> or --num=<number(s)>(-n only)", "option", False),
57 ("Specify the recipient(s):", "-r<recipient(s)> or --recipient=<recipient(s)> (-n only)", "option", False),
58 ("Specify the groups(s):", "--group=<group(s)> or --groups=<group(s)> (-n only)", "option", False) ],
59 see_also_list=['hp-faxsetup', 'hp-fab'])
61 opts, device_uri, printer_name, mode, ui_toolkit, loc = \
62 mod.parseStdOpts('f:r:g:',
63 ['faxnum=', 'fax-num=', 'recipient=', 'group=',
65 supress_g_debug_flag=True)
69 log.set_level('debug')
71 elif o in ('-z', '--logfile'):
73 log.set_where(log.LOG_TO_CONSOLE_AND_FILE)
78 elif o in ('-f', '--faxnum', '--fax-num', '--num'):
79 faxnum_list.extend(a.split(','))
81 elif o in ('-r', '--recipient'):
82 recipient_list.extend(a.split(','))
84 elif o in ('-g', '--group'):
85 group_list.extend(a.split(','))
88 if not prop.fax_build:
89 log.error("Fax is disabled (turned off during build). Exiting")
92 printer_name, device_uri = mod.getPrinterName(printer_name, device_uri,
93 filter={'fax-type': (operator.gt, 0)}, back_end_filter=['hpfax'])
96 if ui_toolkit == 'qt3':
97 if not utils.canEnterGUIMode():
98 log.error("%s requires GUI support (try running with --qt4). Also, try using non-interactive (-n) mode." % __mod__)
101 if not utils.canEnterGUIMode4():
102 log.error("%s requires GUI support (try running with --qt3). Also, try using non-interactive (-n) mode." % __mod__)
106 if ui_toolkit == 'qt3':
112 from ui.faxsendjobform import FaxSendJobForm
114 log.error("Unable to load Qt3 support. Is it installed?")
117 # create the main application object
118 app = QApplication(sys.argv)
121 loc = user_conf.get('ui', 'loc', 'system')
122 if loc.lower() == 'system':
123 loc = str(QTextCodec.locale())
124 log.debug("Using system locale: %s" % loc)
126 if loc.lower() != 'c':
129 l, x = loc.split('.')
130 loc = '.'.join([l, e])
133 loc = '.'.join([loc, e])
135 log.debug("Trying to load .qm file for %s locale." % loc)
136 trans = QTranslator(None)
138 qm_file = 'hplip_%s.qm' % l
139 log.debug("Name of .qm file: %s" % qm_file)
140 loaded = trans.load(qm_file, prop.localization_dir)
143 app.installTranslator(trans)
148 log.debug("Using default 'C' locale")
150 log.debug("Using locale: %s" % loc)
152 QLocale.setDefault(QLocale(loc))
155 locale.setlocale(locale.LC_ALL, locale.normalize(loc))
160 if os.geteuid() == 0:
161 log.error("You must not be root to run this utility.")
163 QMessageBox.critical(None,
164 "HP Device Manager - Send Fax",
165 "You must not be root to run hp-sendfax.",
167 QMessageBox.NoButton,
168 QMessageBox.NoButton)
172 # TODO: Fix instance lock
173 sendfax = FaxSendJobForm(device_uri,
177 app.setMainWidget(sendfax)
180 log.debug('pid=%d' % pid)
185 log.debug("Starting GUI loop...")
187 except KeyboardInterrupt:
193 from PyQt4.QtGui import QApplication
194 from ui4.sendfaxdialog import SendFaxDialog
197 log.error("Unable to load Qt4 support. Is it installed?")
200 app = QApplication(sys.argv)
202 dlg = SendFaxDialog(None, printer_name, device_uri, mod.args)
206 log.debug("Starting GUI loop...")
208 except KeyboardInterrupt:
214 else: # NON_INTERACTIVE_MODE
216 log.error("%s cannot be run as root." % __mod__)
221 from prnt import cups
222 from base import magic
227 # This can fail on Python < 2.3 due to the datetime module
228 log.error("Fax address book disabled - Python 2.3+ required.")
231 db = fax.FaxAddressBook() # FAB instance
236 log.error("PC send fax requires dBus and python-dbus")
240 # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
241 # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
242 warnings.simplefilter("ignore", DeprecationWarning)
244 dbus_avail, service, session_bus = device.init_dbus()
246 if not dbus_avail or service is None:
247 log.error("Unable to initialize dBus. PC send fax requires dBus and hp-systray support. Exiting.")
252 log.debug("Faxnum list = %s" % faxnum_list)
253 faxnum_list = utils.uniqueList(faxnum_list)
254 log.debug("Unique list=%s" % faxnum_list)
256 for f in faxnum_list:
258 if c not in '0123456789-(+) *#':
259 log.error("Invalid character in fax number '%s'. Only the characters '0123456789-(+) *#' are valid." % f)
262 log.debug("Group list = %s" % group_list)
263 group_list = utils.uniqueList(group_list)
264 log.debug("Unique list=%s" % group_list)
267 entries = db.group_members(g)
269 log.warn("Unknown group name: %s" % g)
272 recipient_list.append(e)
274 log.debug("Recipient list = %s" % recipient_list)
275 recipient_list = utils.uniqueList(recipient_list)
276 log.debug("Unique list=%s" % recipient_list)
278 for r in recipient_list:
279 if db.get(r) is None:
280 log.error("Unknown fax recipient '%s' in the recipient list." % r)
281 all_entries = db.get_all_records()
282 log.info(log.bold("\nKnown recipients (entries):"))
284 for a in all_entries:
286 log.info("%s (fax number: %s)" % (a, aa['fax']))
291 for p in recipient_list:
294 phone_num_list.append(a)
295 log.debug("Name=%s Number=%s" % (a['name'], a['fax']))
297 for p in faxnum_list:
298 phone_num_list.append({'fax': p, 'name': u'Unknown'})
299 log.debug("Number=%s" % p)
301 log.debug("Phone num list = %s" % phone_num_list)
303 if not phone_num_list:
304 mod.usage(error_msg=["No recipients specified. Please use -f, -r, and/or -g to specify recipients."])
306 allowable_mime_types = cups.getAllowableMIMETypes()
309 path = os.path.realpath(f)
312 if os.path.exists(path):
313 mime_type = magic.mime_type(path)
316 log.error("File '%s' does not exist." % path)
319 if mime_type not in allowable_mime_types:
320 log.error("File '%s' has a non-allowed mime-type of '%s'" % (path, mime_type))
323 log.info(log.bold("Using fax %s (%s)" % (printer_name, device_uri)))
325 #ok, lock_file = utils.lock_app('%s-%s' % (__mod__, printer_name), True)
326 mod.lockInstance(printer_name)
329 ppd_file = cups.getPPD(printer_name)
331 if ppd_file is not None and os.path.exists(ppd_file):
332 if file(ppd_file, 'r').read(8192).find('HP Fax') == -1:
333 log.error("Fax configuration error. The CUPS fax queue for '%s' is incorrectly configured. Please make sure that the CUPS fax queue is configured with the 'HP Fax' Model/Driver." % printer_name)
337 mod.usage(error_msg=["No files specfied to send. Please specify the file(s) to send on the command line."])
344 # Submit each file to CUPS for rendering by hpijsfax
346 path = os.path.realpath(f)
348 mime_type = magic.mime_type(path)
350 if mime_type == 'application/hplip-fax': # .g3
351 log.info("\nPreparing fax file %s..." % f)
352 fax_file_fd = file(f, 'r')
353 header = fax_file_fd.read(fax.FILE_HEADER_SIZE)
356 mg, version, pages, hort_dpi, vert_dpi, page_size, \
357 resolution, encoding, reserved1, reserved2 = struct.unpack(">8sBIHHBBBII", header)
360 log.error("%s: Invalid file header. Bad magic." % f)
363 file_list.append((f, mime_type, "", "", pages))
373 if mime_type in ["application/x-cshell",
374 "application/x-perl",
375 "application/x-python",
376 "application/x-shell",
377 "text/plain",] and prettyprint:
379 cups.addOption('prettyprint')
382 cups.addOption('number-up=%d' % nup)
386 cups_printers = cups.getPrinters()
387 printer_state = cups.IPP_PRINTER_STATE_STOPPED
388 for p in cups_printers:
389 if p.name == printer_name:
390 printer_state = p.state
392 log.debug("Printer state = %d" % printer_state)
394 if printer_state == cups.IPP_PRINTER_STATE_IDLE:
395 log.debug("Printer name = %s file = %s" % (printer_name, path))
396 sent_job_id = cups.printFile(printer_name, path, os.path.basename(path))
397 log.info("\nRendering file '%s' (job %d)..." % (path, sent_job_id))
398 log.debug("Job ID=%d" % sent_job_id)
400 elif printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
401 log.debug("Waiting for CUPS queue '%s' to become idle." % printer_name)
403 log.error("The CUPS queue for '%s' is in a stopped or busy state (%d). Please check the queue and try again." % (printer_name, printer_state))
409 # Wait for fax to finish rendering
412 end_time = time.time() + 120.0
413 while time.time() < end_time:
414 log.debug("Waiting for fax...")
416 result = list(service.CheckForWaitingFax(device_uri, prop.username, sent_job_id))
417 log.debug(repr(result))
419 except dbus.exceptions.DBusException:
420 log.error("Cannot communicate with hp-systray. Canceling...")
421 cups.cancelJob(sent_job_id)
424 fax_file = str(result[7])
428 log.debug("Fax file=%s" % fax_file)
429 title = str(result[5])
435 log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
436 cups.cancelJob(sent_job_id)
439 # open the rendered file to read the file header
440 f = file(fax_file, 'r')
441 header = f.read(fax.FILE_HEADER_SIZE)
443 if len(header) != fax.FILE_HEADER_SIZE:
444 log.error("Invalid fax file! (truncated header or no data)")
447 mg, version, total_pages, hort_dpi, vert_dpi, page_size, \
448 resolution, encoding, reserved1, reserved2 = \
449 struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
451 log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
452 (mg, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
454 file_list.append((fax_file, mime_type, "", title, total_pages))
458 # Insure that the device is in an OK state
463 log.debug("\nChecking device state...")
465 dev = fax.getFaxDevice(device_uri, printer_name)
473 dev.queryDevice(quick=True)
475 log.error("Query device error (%s)." % e.msg)
476 dev.error_state = ERROR_STATE_ERROR
478 if dev.error_state > ERROR_STATE_MAX_OK and \
479 dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
481 log.error("Device is busy or in an error state (code=%d). Please wait for the device to become idle or clear the error and try again." % dev.error_state)
484 user_conf.set('last_used', 'device_uri', dev.device_uri)
486 log.debug("File list:")
491 service.SendEvent(device_uri, printer_name, EVENT_START_FAX_JOB, prop.username, 0, '')
493 update_queue = Queue.Queue()
494 event_queue = Queue.Queue()
496 log.info("\nSending fax...")
498 if not dev.sendFaxes(phone_num_list, file_list, "",
499 "", None, False, printer_name,
500 update_queue, event_queue):
502 log.error("Send fax is active. Please wait for operation to complete.")
503 service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
509 while update_queue.qsize():
511 status, page_num, phone_num = update_queue.get(0)
515 if status == fax.STATUS_IDLE:
518 elif status == fax.STATUS_PROCESSING_FILES:
519 log.info("\nProcessing page %d" % page_num)
521 elif status == fax.STATUS_DIALING:
522 log.info("\nDialing %s..." % phone_num)
524 elif status == fax.STATUS_CONNECTING:
525 log.info("\nConnecting to %s..." % phone_num)
527 elif status == fax.STATUS_SENDING:
528 log.info("\nSending page %d to %s..." % (page_num, phone_num))
530 elif status == fax.STATUS_CLEANUP:
531 log.info("\nCleaning up...")
533 elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
536 if status == fax.STATUS_ERROR:
537 log.error("Fax send error.")
538 service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
540 elif status == fax.STATUS_BUSY:
541 log.error("Fax device is busy. Please try again later.")
542 service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
544 elif status == fax.STATUS_COMPLETED:
545 log.info("\nCompleted successfully.")
546 service.SendEvent(device_uri, printer_name, EVENT_END_FAX_JOB, prop.username, 0, '')
553 except KeyboardInterrupt:
554 event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
555 service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_CANCELED, prop.username, 0, '')
556 log.error("Cancelling...")
559 log.debug("Waiting for send fax thread to exit...")
561 dev.waitForSendFaxThread()
562 log.debug("Closing device...")
568 except KeyboardInterrupt:
569 log.error("User exit")