~mimose/+junk/hplip-3.16.11

« back to all changes in this revision

Viewing changes to fax/fax.py

  • Committer: guoyalong
  • Date: 2017-09-20 10:13:05 UTC
  • Revision ID: guoyalong@kylinos.cn-20170920101305-82zaolzpv1qghz29
Modified debian/control & debian/rules.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# (c) Copyright 2015 HP Development Company, L.P.
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
18
#
 
19
# Author: Don Welch
 
20
#
 
21
 
 
22
 
 
23
 
 
24
# Std Lib
 
25
import sys
 
26
import os
 
27
import threading
 
28
import pickle
 
29
import time
 
30
import struct
 
31
 
 
32
# Local
 
33
from base.g import *
 
34
from base.codes import *
 
35
from base.ldif import LDIFParser
 
36
from base import device, utils, vcard
 
37
from prnt import cups
 
38
from base.sixext import BytesIO
 
39
from base.sixext import to_bytes_utf8, to_long, to_unicode
 
40
try:
 
41
    from . import coverpages
 
42
except ImportError:
 
43
    pass
 
44
 
 
45
try:
 
46
    import dbus
 
47
except ImportError:
 
48
    log.error("dbus is required for PC send fax.")
 
49
 
 
50
import warnings
 
51
# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
 
52
# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
 
53
warnings.simplefilter("ignore", DeprecationWarning)
 
54
 
 
55
 
 
56
# Update queue values (Send thread ==> UI)
 
57
STATUS_IDLE = 0
 
58
STATUS_PROCESSING_FILES = 1
 
59
STATUS_SENDING_TO_RECIPIENT = 2
 
60
STATUS_DIALING = 3
 
61
STATUS_CONNECTING = 4
 
62
STATUS_SENDING = 5
 
63
STATUS_COMPLETED = 6
 
64
STATUS_CREATING_COVER_PAGE = 7
 
65
STATUS_ERROR = 8
 
66
STATUS_BUSY = 9
 
67
STATUS_CLEANUP = 10
 
68
STATUS_ERROR_IN_CONNECTING = 11
 
69
STATUS_ERROR_IN_TRANSMITTING = 12
 
70
STATUS_ERROR_PROBLEM_IN_FAXLINE = 13
 
71
STATUS_JOB_CANCEL = 14 
 
72
 
 
73
# Event queue values (UI ==> Send thread)
 
74
EVENT_FAX_SEND_CANCELED = 1
 
75
# Other values in queue are:
 
76
#EVENT_FAX_RENDER_COMPLETE_BEGIN = 8010
 
77
#EVENT_FAX_RENDER_COMPLETE_SENDDATA = 8011
 
78
#EVENT_FAX_RENDER_COMPLETE_END = 8012
 
79
 
 
80
# **************************************************************************** #
 
81
# HPLIP G3 Fax File Format (big endian)
 
82
#
 
83
# #==============================================#
 
84
# # File Header: Total 28 bytes                  #
 
85
# #..............................................#
 
86
# # Magic bytes: 8 bytes ("hplip_g3")            #
 
87
# # Format version: 8 bits (1)                   #
 
88
# # Total pages in file(=p): 32 bits             #
 
89
# # Hort DPI: 16 bits (200 or 300)               #
 
90
# # Vert DPI: 16 bits (100, 200, or 300)         #
 
91
# # Page Size: 8 bits (0=Unk, 1=Letter, 2=A4,    #
 
92
# #                    3=Legal)                  #
 
93
# # Resolution: 8 bits (0=Unk, 1=Std, 2=Fine,    #
 
94
# #                     3=300DPI)                #
 
95
# # Encoding: 8 bits (2=MH, 4=MMR, 7=JPEG)       #
 
96
# # Reserved1: 32 bits (0)                       #
 
97
# # Reserved2: 32 bits (0)                       #
 
98
# #----------------------------------------------#
 
99
# # Page 1 Header: Total 24 bytes                #
 
100
# #..............................................#
 
101
# # Page number: 32 bits (1 based)               #
 
102
# # Pixels per row: 32 bits                      #
 
103
# # Rows this page: 32 bits                      #
 
104
# # Image bytes this page(=x): 32 bits           #
 
105
# # Thumbnail bytes this page(=y): 32 bits       #
 
106
# #  (thumbnail not present if y == 0)           #
 
107
# #  (encoding?)                                 #
 
108
# #     letter: 134 px wide x 173 px high        #
 
109
# #     legal:  134 px wide x 221 px high        #
 
110
# #     a4 :    134 px wide x 190 px high        #
 
111
# # Reserved3: 32 bits (0)                       #
 
112
# #..............................................#
 
113
# # Image data: x bytes                          #
 
114
# #..............................................#
 
115
# # Thumbnail data: y bytes (if present)         #
 
116
# #----------------------------------------------#
 
117
# # Page 2 Header: Total 24 bytes                #
 
118
# #..............................................#
 
119
# # Image Data                                   #
 
120
# #..............................................#
 
121
# # Thumbnail data (if present)                  #
 
122
# #----------------------------------------------#
 
123
# # ... Pages 3 - (p-1) ...                      #
 
124
# #----------------------------------------------#
 
125
# # Page p Header: Total 24 bytes                #
 
126
# #..............................................#
 
127
# # Image Data                                   #
 
128
# #..............................................#
 
129
# # Thumbnail data (if present)                  #
 
130
# #==============================================#
 
131
#
 
132
 
 
133
RESOLUTION_STD = 1
 
134
RESOLUTION_FINE = 2
 
135
RESOLUTION_300DPI = 3
 
136
 
 
137
FILE_HEADER_SIZE = 28
 
138
PAGE_HEADER_SIZE = 24
 
139
# **************************************************************************** #
 
140
 
 
141
##skip_dn = ["uid=foo,ou=People,dc=example,dc=com",
 
142
##    "uid=bar,ou=People,dc=example,dc=com", "dc=example,dc=com"]
 
143
 
 
144
class FaxLDIFParser(LDIFParser):
 
145
    def __init__(self, input, db):
 
146
        LDIFParser.__init__(self, input)
 
147
        self.db = db
 
148
 
 
149
    def handle(self, dn, entry):
 
150
        if dn:
 
151
            try:
 
152
                firstname = entry['givenName'][0]
 
153
            except KeyError:
 
154
                try:
 
155
                    firstname = entry['givenname'][0]
 
156
                except KeyError:
 
157
                    firstname = ''
 
158
 
 
159
            try:
 
160
                lastname = entry['sn'][0]
 
161
            except KeyError:
 
162
                lastname = ''
 
163
 
 
164
            try:
 
165
                nickname = entry['cn'][0]
 
166
            except KeyError:
 
167
                nickname = firstname + ' ' + lastname
 
168
 
 
169
            try:
 
170
                fax = entry['facsimiletelephonenumber'][0] # fax
 
171
            except KeyError:
 
172
                try:
 
173
                    fax = entry['fax'][0]
 
174
                except KeyError:
 
175
                    fax  = ''
 
176
 
 
177
            grps = []
 
178
            try:
 
179
                grps = entry['ou']
 
180
            except KeyError:
 
181
                pass
 
182
 
 
183
            grps.append(to_unicode('All'))
 
184
            groups = [g for g in grps if g]
 
185
 
 
186
            if nickname:
 
187
                log.debug("Import: name=%s, fax=%s, group(s)=%s, notes=%s" % ( nickname, fax, ','.join(groups), dn))
 
188
                self.db.set(nickname, title, firstname, lastname, fax, groups, dn)
 
189
 
 
190
 
 
191
 
 
192
# **************************************************************************** #
 
193
class FaxAddressBook(object): # Pickle based address book
 
194
    def __init__(self):
 
195
        self._data = {}
 
196
        #
 
197
        # { 'name' : {'name': u'',
 
198
        #             'firstname' : u'', # NOT USED STARTING IN 2.8.9
 
199
        #             'lastname': u', # NOT USED STARTING IN 2.8.9
 
200
        #             'title' : u'',  # NOT USED STARTING IN 2.8.9
 
201
        #             'fax': u'',
 
202
        #             'groups' : [u'', u'', ...],
 
203
        #             'notes' : u'', } ...
 
204
        # }
 
205
        #
 
206
        self.load()
 
207
 
 
208
    def load(self):
 
209
        self._fab = "/dev/null"
 
210
        if prop.user_dir != None:
 
211
            self._fab = os.path.join(prop.user_dir, "fab.pickle")
 
212
            #old_fab = os.path.join(prop.user_dir, "fab.db")
 
213
 
 
214
            # Load the existing pickle if present
 
215
            if os.path.exists(self._fab):
 
216
               pickle_file = open(self._fab, "rb")
 
217
               self._data = pickle.load(pickle_file)
 
218
               pickle_file.close()
 
219
            else:
 
220
               self.save() # save the empty file to create the file
 
221
 
 
222
 
 
223
    def set(self, name, title, firstname, lastname, fax, groups, notes):
 
224
        # try:
 
225
        #     grps = [to_unicode(s) for s in groups]
 
226
        # except UnicodeDecodeError:
 
227
        #     grps = [to_unicode(s.decode('utf-8')) for s in groups]
 
228
        grps = [to_unicode(s) for s in groups]
 
229
 
 
230
        self._data[to_unicode(name)] = {'name' : to_unicode(name),
 
231
                                    'title': to_unicode(title),  # NOT USED STARTING IN 2.8.9
 
232
                                    'firstname': to_unicode(firstname), # NOT USED STARTING IN 2.8.9
 
233
                                    'lastname': to_unicode(lastname), # NOT USED STARTING IN 2.8.9
 
234
                                    'fax': to_unicode(fax),
 
235
                                    'notes': to_unicode(notes),
 
236
                                    'groups': grps}
 
237
 
 
238
        self.save()
 
239
 
 
240
    insert = set
 
241
 
 
242
 
 
243
    def set_key_value(self, name, key, value):
 
244
        self._data[to_unicode(name)][key] = value
 
245
        self.save()
 
246
 
 
247
 
 
248
    def get(self, name):
 
249
        return self._data.get(name, None)
 
250
 
 
251
    select = get
 
252
 
 
253
    def rename(self, old_name, new_name):
 
254
        try:
 
255
            self._data[old_name]
 
256
        except KeyError:
 
257
            return
 
258
        else:
 
259
            try:
 
260
                self._data[new_name]
 
261
            except KeyError:
 
262
                self._data[new_name] = self._data[old_name].copy()
 
263
                self._data[new_name]['name'] = new_name
 
264
                del self._data[old_name]
 
265
                self.save()
 
266
 
 
267
 
 
268
    def get_all_groups(self):
 
269
        all_groups = []
 
270
        for e, v in list(self._data.items()):
 
271
            for g in v['groups']:
 
272
                if g not in all_groups:
 
273
                    all_groups.append(g)
 
274
        return all_groups
 
275
 
 
276
 
 
277
    def get_all_records(self):
 
278
        return self._data
 
279
 
 
280
 
 
281
    def get_all_names(self):
 
282
        return list(self._data.keys())
 
283
 
 
284
 
 
285
    def save(self):
 
286
        try:
 
287
            pickle_file = open(self._fab, "wb")
 
288
            pickle.dump(self._data, pickle_file, protocol=2)
 
289
            pickle_file.close()
 
290
        except IOError:
 
291
            log.error("I/O error saving fab file.")
 
292
 
 
293
 
 
294
    def clear(self):
 
295
        self._data = {}
 
296
        self.save()
 
297
 
 
298
 
 
299
    def delete(self, name):
 
300
        if name in self._data:
 
301
            del self._data[name]
 
302
            self.save()
 
303
            return True
 
304
 
 
305
        return False
 
306
 
 
307
 
 
308
    def last_modification_time(self):
 
309
        try:
 
310
            return os.stat(self._fab).st_mtime
 
311
        except OSError:
 
312
            return 0
 
313
 
 
314
 
 
315
    def update_groups(self, group, members):
 
316
        for e, v in list(self._data.items()):
 
317
            if v['name'] in members: # membership indicated
 
318
                if not group in v['groups']:
 
319
                    v['groups'].append(to_unicode(group))
 
320
            else:
 
321
                if group in v['groups']:
 
322
                    v['groups'].remove(to_unicode(group))
 
323
        self.save()
 
324
 
 
325
 
 
326
    def delete_group(self, group):
 
327
        for e, v in list(self._data.items()):
 
328
            if group in v['groups']:
 
329
                v['groups'].remove(to_unicode(group))
 
330
        self.save()
 
331
 
 
332
 
 
333
    def group_members(self, group):
 
334
        members = []
 
335
        for e, v in list(self._data.items()):
 
336
            if group in v['groups']:
 
337
                members.append(e)
 
338
        return members
 
339
 
 
340
 
 
341
    def add_to_group(self, group, members):
 
342
        group_members = self.group_members(group)
 
343
        new_group_members = []
 
344
        for m in members:
 
345
            if m not in group_members:
 
346
                new_group_members.append(m)
 
347
 
 
348
        self.update_groups(group, group_members + new_group_members)
 
349
 
 
350
 
 
351
    def remove_from_group(self, group, remove_members):
 
352
        group_members = self.group_members(group)
 
353
        new_group_members = []
 
354
        for m in group_members:
 
355
            if m not in remove_members:
 
356
                new_group_members.append(m)
 
357
 
 
358
        self.update_groups(group, new_group_members)
 
359
 
 
360
 
 
361
    def rename_group(self, old_group, new_group):
 
362
        members = self.group_members(old_group)
 
363
        self.update_groups(old_group, [])
 
364
        self.update_groups(new_group, members)
 
365
 
 
366
 
 
367
    def import_ldif(self, filename):
 
368
        try:
 
369
            data = open(filename, 'r').read()
 
370
            log.debug_block(filename, data)
 
371
            parser = FaxLDIFParser(open(filename, 'r'), self)
 
372
            parser.parse()
 
373
            self.save()
 
374
            return True, ''
 
375
        except ValueError as e:
 
376
            return False, e.message
 
377
 
 
378
 
 
379
    def import_vcard(self, filename):
 
380
        data = open(filename, 'r').read()
 
381
        log.debug_block(filename, data)
 
382
 
 
383
        for card in vcard.VCards(vcard.VFile(vcard.opentextfile(filename))):
 
384
            log.debug(card)
 
385
 
 
386
            if card['name']:
 
387
                fax = ''
 
388
                for x in range(1, 9999):
 
389
                    if x == 1:
 
390
                        s = 'phone'
 
391
                    else:
 
392
                        s = 'phone%d' % x
 
393
 
 
394
                    try:
 
395
                        card[s]
 
396
                    except KeyError:
 
397
                        break
 
398
                    else:
 
399
                        if 'fax' in card[s]['type']:
 
400
                            fax = card[s]['number']
 
401
                            break
 
402
 
 
403
                org = card.get('organisation', '')
 
404
                if org:
 
405
                    org = [org]
 
406
                else:
 
407
                    org = card.get('categories', '').split(';')
 
408
                    if not org:
 
409
                        org = []
 
410
 
 
411
                org.append(to_unicode('All'))
 
412
                groups = [o for o in org if o]
 
413
 
 
414
                name = card['name']
 
415
                notes = card.get('notes', to_unicode(''))
 
416
                log.debug("Import: name=%s, fax=%s group(s)=%s notes=%s" % (name, fax, ','.join(groups), notes))
 
417
                self.set(name, to_unicode(''), to_unicode(''), to_unicode(''), fax, groups, notes)
 
418
 
 
419
        return True, ''
 
420
 
 
421
 
 
422
# **************************************************************************** #
 
423
class FaxDevice(device.Device):
 
424
 
 
425
    def __init__(self, device_uri=None, printer_name=None,
 
426
                 callback=None,
 
427
                 fax_type=FAX_TYPE_NONE,
 
428
                 disable_dbus=False):
 
429
 
 
430
        device.Device.__init__(self, device_uri, printer_name,
 
431
                               None, callback, disable_dbus)
 
432
 
 
433
        self.send_fax_thread = None
 
434
        self.upload_log_thread = None
 
435
        self.fax_type = fax_type
 
436
 
 
437
        if not disable_dbus:
 
438
            session_bus = dbus.SessionBus()
 
439
            self.service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
 
440
        else:
 
441
            self.service = None
 
442
 
 
443
 
 
444
    def setPhoneNum(self, num):
 
445
        raise AttributeError
 
446
 
 
447
    def getPhoneNum(self):
 
448
        raise AttributeError
 
449
 
 
450
    phone_num = property(getPhoneNum, setPhoneNum)
 
451
 
 
452
 
 
453
    def setStationName(self, name):
 
454
        raise AttributeError
 
455
 
 
456
    def getStationName(self):
 
457
        raise AttributeError
 
458
 
 
459
    station_name = property(getStationName, setStationName)
 
460
 
 
461
    def setDateAndTime(self):
 
462
        raise AttributeError
 
463
 
 
464
    def uploadLog(self):
 
465
        raise AttributeError
 
466
 
 
467
    def isUploadLogActive(self):
 
468
        raise AttributeError
 
469
 
 
470
    def waitForUploadLogThread(self):
 
471
        raise AttributeError
 
472
 
 
473
    def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
 
474
                  cover_func=None, preserve_formatting=False, printer_name='',
 
475
                  update_queue=None, event_queue=None):
 
476
 
 
477
        raise AttributeError
 
478
 
 
479
    def isSendFaxActive(self):
 
480
        if self.send_fax_thread is not None:
 
481
            return self.send_fax_thread.isAlive()
 
482
        else:
 
483
            return False
 
484
 
 
485
    def waitForSendFaxThread(self):
 
486
        if self.send_fax_thread is not None and \
 
487
            self.send_fax_thread.isAlive():
 
488
 
 
489
            try:
 
490
                self.send_fax_thread.join()
 
491
            except KeyboardInterrupt:
 
492
                pass
 
493
 
 
494
 
 
495
# **************************************************************************** #
 
496
 
 
497
 
 
498
def getFaxDevice(device_uri=None, printer_name=None,
 
499
                 callback=None,
 
500
                 fax_type=FAX_TYPE_NONE,
 
501
                 disable_dbus=False):
 
502
 
 
503
    if fax_type == FAX_TYPE_NONE:
 
504
        if device_uri is None and printer_name is not None:
 
505
            printers = cups.getPrinters()
 
506
 
 
507
            for p in printers:
 
508
                if p.name.lower() == printer_name.lower():
 
509
                    device_uri = p.device_uri
 
510
                    break
 
511
            else:
 
512
                raise Error(ERROR_DEVICE_NOT_FOUND)
 
513
 
 
514
        if device_uri is not None:
 
515
            mq = device.queryModelByURI(device_uri)
 
516
            fax_type = mq['fax-type']
 
517
 
 
518
    log.debug("fax-type=%d" % fax_type)
 
519
 
 
520
    if fax_type in (FAX_TYPE_BLACK_SEND_EARLY_OPEN, FAX_TYPE_BLACK_SEND_LATE_OPEN):
 
521
        from .pmlfax import PMLFaxDevice
 
522
        return PMLFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
 
523
 
 
524
    elif fax_type == FAX_TYPE_SOAP:
 
525
        from .soapfax import SOAPFaxDevice
 
526
        return SOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
 
527
 
 
528
    elif fax_type == FAX_TYPE_LEDMSOAP:
 
529
        from .ledmsoapfax import LEDMSOAPFaxDevice
 
530
        return LEDMSOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
 
531
 
 
532
    elif fax_type == FAX_TYPE_MARVELL:
 
533
        from .marvellfax import MarvellFaxDevice
 
534
        return MarvellFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
 
535
 
 
536
    elif fax_type == FAX_TYPE_LEDM:
 
537
        from .ledmfax import LEDMFaxDevice
 
538
        return LEDMFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
 
539
 
 
540
    else:
 
541
        raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
 
542
 
 
543
# **************************************************************************** #
 
544
 
 
545
 
 
546
 
 
547
 
 
548
# TODO: Define these in only 1 place!
 
549
STATE_DONE = 0
 
550
STATE_ABORTED = 10
 
551
STATE_SUCCESS = 20
 
552
STATE_BUSY = 25
 
553
STATE_READ_SENDER_INFO = 30
 
554
STATE_PRERENDER = 40
 
555
STATE_COUNT_PAGES = 50
 
556
STATE_NEXT_RECIPIENT = 60
 
557
STATE_COVER_PAGE = 70
 
558
STATE_SINGLE_FILE = 80
 
559
STATE_MERGE_FILES = 90
 
560
STATE_SINGLE_FILE = 100
 
561
STATE_SEND_FAX = 110
 
562
STATE_CLEANUP = 120
 
563
STATE_ERROR = 130
 
564
 
 
565
class FaxSendThread(threading.Thread):
 
566
    def __init__(self, dev, service, phone_num_list, fax_file_list,
 
567
                 cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
 
568
                 printer_name='', update_queue=None, event_queue=None):
 
569
 
 
570
        threading.Thread.__init__(self)
 
571
 
 
572
        self.dev = dev # device.Device
 
573
        self.service = service # dbus proxy to status server object
 
574
        self.phone_num_list = phone_num_list
 
575
        self.fax_file_list = fax_file_list
 
576
        self.update_queue = update_queue
 
577
        self.event_queue = event_queue
 
578
        self.cover_message = cover_message
 
579
        self.cover_re = cover_re
 
580
        self.cover_func = cover_func
 
581
        self.current_printer = printer_name
 
582
        self.stream = BytesIO()
 
583
        self.prev_update = ''
 
584
        self.remove_temp_file = False
 
585
        self.preserve_formatting = preserve_formatting
 
586
        self.results = {} # {'file' : error_code,...}
 
587
        self.cover_page_present = False
 
588
        self.recipient_file_list = []
 
589
        self.f = None # final file of fax data to send (pages merged)
 
590
        self.job_hort_dpi = 0
 
591
        self.job_hort_dpi = 0
 
592
        self.job_vert_dpi = 0
 
593
        self.job_page_size = 0
 
594
        self.job_resolution = 0
 
595
        self.job_encoding = 0
 
596
 
 
597
 
 
598
    def pre_render(self, state):
 
599
        # pre-render each page that needs rendering
 
600
        # except for the cover page
 
601
        self.cover_page_present = False
 
602
        log.debug(self.fax_file_list)
 
603
 
 
604
        for fax_file in self.fax_file_list: # (file, type, desc, title)
 
605
            fax_file_name, fax_file_type, fax_file_desc, \
 
606
                fax_file_title, fax_file_pages = fax_file
 
607
 
 
608
            if fax_file_type == "application/hplip-fax-coverpage": # render later
 
609
                self.cover_page_present = True
 
610
                log.debug("Skipping coverpage")
 
611
 
 
612
            #if fax_file_type == "application/hplip-fax": # already rendered
 
613
            else:
 
614
                self.rendered_file_list.append((fax_file_name, "application/hplip-fax",
 
615
                    "HP Fax", fax_file_title))
 
616
 
 
617
                log.debug("Processing pre-rendered file: %s (%d pages)" %
 
618
                    (fax_file_name, fax_file_pages))
 
619
 
 
620
            if self.check_for_cancel():
 
621
                state = STATE_ABORTED
 
622
 
 
623
        log.debug(self.rendered_file_list)
 
624
 
 
625
        if self.check_for_cancel():
 
626
            state = STATE_ABORTED
 
627
 
 
628
        return state
 
629
 
 
630
 
 
631
    def count_pages(self, state):
 
632
        self.recipient_file_list = self.rendered_file_list[:]
 
633
        log.debug("Counting total pages...")
 
634
        self.job_total_pages = 0
 
635
        log.debug(self.recipient_file_list)
 
636
 
 
637
        i = 0
 
638
        for fax_file in self.recipient_file_list: # (file, type, desc, title)
 
639
            fax_file_name = fax_file[0]
 
640
            log.debug("Processing file (counting pages): %s..." % fax_file_name)
 
641
 
 
642
            #self.write_queue((STATUS_PROCESSING_FILES, self.job_total_pages, ''))
 
643
 
 
644
            if os.path.exists(fax_file_name):
 
645
                self.results[fax_file_name] = ERROR_SUCCESS
 
646
                fax_file_fd = open(fax_file_name, 'rb')
 
647
                header = fax_file_fd.read(FILE_HEADER_SIZE)
 
648
 
 
649
                magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
 
650
                    resolution, encoding, reserved1, reserved2 = \
 
651
                        self.decode_fax_header(header)
 
652
 
 
653
                if magic != b'hplip_g3':
 
654
                    log.error("Invalid file header. Bad magic.")
 
655
                    self.results[fax_file_name] = ERROR_FAX_INVALID_FAX_FILE
 
656
                    state = STATE_ERROR
 
657
                    continue
 
658
 
 
659
                if not i:
 
660
                    self.job_hort_dpi, self.job_vert_dpi, self.job_page_size, \
 
661
                        self.job_resolution, self.job_encoding = \
 
662
                        hort_dpi, vert_dpi, page_size, resolution, encoding
 
663
 
 
664
                    i += 1
 
665
                else:
 
666
                    if self.job_hort_dpi != hort_dpi or \
 
667
                        self.job_vert_dpi != vert_dpi or \
 
668
                        self.job_page_size != page_size or \
 
669
                        self.job_resolution != resolution or \
 
670
                        self.job_encoding != encoding:
 
671
 
 
672
                        log.error("Incompatible options for file: %s" % fax_file_name)
 
673
                        self.results[fax_file_name] = ERROR_FAX_INCOMPATIBLE_OPTIONS
 
674
                        state = STATE_ERROR
 
675
 
 
676
 
 
677
                log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
 
678
                          (magic, version, total_pages, hort_dpi,
 
679
                           vert_dpi, page_size, resolution, encoding))
 
680
 
 
681
                self.job_total_pages += total_pages
 
682
 
 
683
                fax_file_fd.close()
 
684
 
 
685
            else:
 
686
                log.error("Unable to find HP Fax file: %s" % fax_file_name)
 
687
                self.results[fax_file_name] = ERROR_FAX_FILE_NOT_FOUND
 
688
                state = STATE_ERROR
 
689
                break
 
690
 
 
691
            if self.check_for_cancel():
 
692
                state = STATE_ABORTED
 
693
                break
 
694
 
 
695
 
 
696
        if self.cover_page_present:
 
697
            self.job_total_pages += 1 # Cover pages are truncated to 1 page
 
698
 
 
699
        log.debug("Total fax pages=%d" % self.job_total_pages)
 
700
 
 
701
        return state
 
702
 
 
703
    def decode_fax_header(self, header):
 
704
        try:
 
705
            return struct.unpack(">8sBIHHBBBII", header)
 
706
        except struct.error:
 
707
            return -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
 
708
 
 
709
    def decode_page_header(self, header):
 
710
        try:
 
711
            return struct.unpack(">IIIIII", header)
 
712
        except struct.error:
 
713
            return -1, -1, -1, -1, -1, -1
 
714
 
 
715
    def cover_page(self,  recipient):
 
716
        if self.job_total_pages > 1:
 
717
            state = STATE_MERGE_FILES
 
718
        else:
 
719
            state = STATE_SINGLE_FILE
 
720
 
 
721
        if self.cover_page_present:
 
722
            log.debug("Creating cover page for recipient: %s" % recipient['name'])
 
723
            fax_file, canceled = self.render_cover_page(recipient)
 
724
 
 
725
            if canceled:
 
726
                state = STATE_ABORTED
 
727
            elif not fax_file:
 
728
                state = STATE_ERROR # timeout
 
729
            else:
 
730
                self.recipient_file_list.insert(0, (fax_file, "application/hplip-fax",
 
731
                                                    "HP Fax", 'Cover Page'))
 
732
 
 
733
                log.debug("Cover page G3 file: %s" % fax_file)
 
734
 
 
735
                self.results[fax_file] = ERROR_SUCCESS
 
736
 
 
737
        return state
 
738
 
 
739
    def single_file(self, state):
 
740
        state = STATE_SEND_FAX
 
741
 
 
742
        log.debug("Processing single file...")
 
743
        self.f = self.recipient_file_list[0][0]
 
744
 
 
745
        try:
 
746
            f_fd = open(self.f, 'rb')
 
747
        except IOError:
 
748
            log.error("Unable to open fax file: %s" % self.f)
 
749
            state = STATE_ERROR
 
750
        else:
 
751
            header = f_fd.read(FILE_HEADER_SIZE)
 
752
 
 
753
            magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
 
754
                resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
 
755
 
 
756
            self.results[self.f] = ERROR_SUCCESS
 
757
 
 
758
            if magic != b'hplip_g3':
 
759
                log.error("Invalid file header. Bad magic.")
 
760
                self.results[self.f] = ERROR_FAX_INVALID_FAX_FILE
 
761
                state = STATE_ERROR
 
762
 
 
763
            log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
 
764
                      (magic, version, total_pages, hort_dpi, vert_dpi,
 
765
                       page_size, resolution, encoding))
 
766
 
 
767
            f_fd.close()
 
768
 
 
769
        return state
 
770
 
 
771
 
 
772
    def merge_files(self, state):
 
773
        log.debug("%s State: Merge multiple files" % ("*"*20))
 
774
        log.debug(self.recipient_file_list)
 
775
        log.debug("Merging g3 files...")
 
776
        self.remove_temp_file = True
 
777
 
 
778
        if self.job_total_pages:
 
779
            f_fd, self.f = utils.make_temp_file()
 
780
            log.debug("Temp file=%s" % self.f)
 
781
 
 
782
            data = struct.pack(">8sBIHHBBBII", b"hplip_g3", to_long(1), self.job_total_pages,
 
783
                self.job_hort_dpi, self.job_vert_dpi, self.job_page_size,
 
784
                self.job_resolution, self.job_encoding,
 
785
                to_long(0), to_long(0))
 
786
 
 
787
            os.write(f_fd, data)
 
788
 
 
789
            job_page_num = 1
 
790
 
 
791
            for fax_file in self.recipient_file_list:
 
792
                fax_file_name = fax_file[0]
 
793
                log.debug("Processing file: %s..." % fax_file_name)
 
794
 
 
795
                if self.results[fax_file_name] == ERROR_SUCCESS:
 
796
                    fax_file_fd = open(fax_file_name, 'rb')
 
797
                    header = fax_file_fd.read(FILE_HEADER_SIZE)
 
798
 
 
799
                    magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
 
800
                        resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
 
801
 
 
802
                    if magic != b'hplip_g3':
 
803
                        log.error("Invalid file header. Bad magic.")
 
804
                        state = STATE_ERROR
 
805
                        break
 
806
 
 
807
                    log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
 
808
                              (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
 
809
 
 
810
                    for p in range(total_pages):
 
811
                        header = fax_file_fd.read(PAGE_HEADER_SIZE)
 
812
 
 
813
                        page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
 
814
                            self.decode_page_header(header)
 
815
 
 
816
                        if page_num == -1:
 
817
                            log.error("Page header error")
 
818
                            state - STATE_ERROR
 
819
                            break
 
820
 
 
821
                        header = struct.pack(">IIIIII", job_page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, to_long(0))
 
822
                        os.write(f_fd, header)
 
823
 
 
824
                        self.write_queue((STATUS_PROCESSING_FILES, job_page_num, ''))
 
825
 
 
826
                        log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%s" %
 
827
                                  (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
 
828
 
 
829
                        os.write(f_fd, fax_file_fd.read(bytes_to_read))
 
830
                        job_page_num += 1
 
831
 
 
832
                    fax_file_fd.close()
 
833
 
 
834
                    if self.check_for_cancel():
 
835
                        state = STATE_ABORTED
 
836
                        break
 
837
 
 
838
                else:
 
839
                    log.error("Skipping file: %s" % fax_file_name)
 
840
                    continue
 
841
 
 
842
            os.close(f_fd)
 
843
            log.debug("Total pages=%d" % self.job_total_pages)
 
844
 
 
845
        return state
 
846
 
 
847
 
 
848
    def next_recipient_gen(self):
 
849
        for a in self.phone_num_list:
 
850
            yield a
 
851
 
 
852
    def next_file_gen(self):
 
853
        for a in self.recipient_file_list:
 
854
            yield a
 
855
 
 
856
 
 
857
    def render_file(self, path, title, mime_type, force_single_page=False):
 
858
        all_pages = True
 
859
        page_range = ''
 
860
        page_set = 0
 
861
        nup = 1
 
862
 
 
863
        cups.resetOptions()
 
864
 
 
865
        if mime_type in ["application/x-cshell",
 
866
                         "application/x-perl",
 
867
                         "application/x-python",
 
868
                         "application/x-shell",
 
869
                         "application/x-sh",
 
870
                         "text/plain",]:
 
871
 
 
872
            cups.addOption('prettyprint')
 
873
 
 
874
        if nup > 1:
 
875
            cups.addOption('number-up=%d' % nup)
 
876
 
 
877
        if force_single_page:
 
878
            cups.addOption('page-ranges=1') # Force coverpage to 1 page
 
879
 
 
880
        sent_job_id = cups.printFile(self.current_printer, path, title)
 
881
        cups.resetOptions()
 
882
 
 
883
        log.debug("Job ID=%d" % sent_job_id)
 
884
        job_id = 0
 
885
 
 
886
        time.sleep(1)
 
887
 
 
888
        fax_file = ''
 
889
        complete = False
 
890
 
 
891
        end_time = time.time() + 300.0 # wait for 5 min. max
 
892
        while time.time() < end_time:
 
893
            log.debug("Waiting for fax... type =%s"%type(self.dev.device_uri))
 
894
 
 
895
            result = list(self.service.CheckForWaitingFax(self.dev.device_uri, prop.username, sent_job_id))
 
896
 
 
897
            fax_file = str(result[7])
 
898
            log.debug("Fax file=%s" % fax_file)
 
899
 
 
900
            if fax_file:
 
901
                break
 
902
 
 
903
            if self.check_for_cancel():
 
904
                log.error("Render canceled. Canceling job #%d..." % sent_job_id)
 
905
                cups.cancelJob(sent_job_id)
 
906
                return '', True
 
907
 
 
908
            time.sleep(1)
 
909
 
 
910
        else:
 
911
            log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
 
912
            cups.cancelJob(sent_job_id)
 
913
            return '', False
 
914
 
 
915
        return fax_file, False
 
916
 
 
917
 
 
918
    def check_for_cancel(self):
 
919
        canceled = False
 
920
        while self.event_queue.qsize():
 
921
            try:
 
922
                event = self.event_queue.get(0)
 
923
                if event[0] == EVENT_FAX_SEND_CANCELED:
 
924
                    canceled = True
 
925
                    log.debug("Cancel pressed!")
 
926
            except Queue.Empty:
 
927
                break
 
928
 
 
929
        return canceled
 
930
 
 
931
    def render_cover_page(self, a):
 
932
        log.debug("Creating cover page...")
 
933
 
 
934
        #Read file again just before creating the coverpage, so that we get updated voice_phone and email_address from /hplip.conf file
 
935
        #hplip.conf file get updated, whenever user changes coverpage info from hp-faxsetup window.
 
936
        user_conf.read()
 
937
 
 
938
        pdf = self.cover_func(page_size=coverpages.PAGE_SIZE_LETTER,
 
939
                              total_pages=self.job_total_pages,
 
940
 
 
941
                              recipient_name=a['name'],
 
942
                              recipient_phone='', # ???
 
943
                              recipient_fax=a['fax'],
 
944
 
 
945
                              sender_name=self.sender_name,
 
946
                              sender_phone=user_conf.get('fax', 'voice_phone'),
 
947
                              sender_fax=self.sender_fax,
 
948
                              sender_email=user_conf.get('fax', 'email_address'),
 
949
 
 
950
                              regarding=self.cover_re,
 
951
                              message=self.cover_message,
 
952
                              preserve_formatting=self.preserve_formatting)
 
953
 
 
954
        log.debug("PDF File=%s" % pdf)
 
955
        fax_file, canceled = self.render_file(pdf, 'Cover Page', "application/pdf",
 
956
            force_single_page=True)
 
957
 
 
958
        try:
 
959
            os.remove(pdf)
 
960
        except IOError:
 
961
            pass
 
962
 
 
963
        return fax_file, canceled
 
964
 
 
965
 
 
966
    def write_queue(self, message):
 
967
        if self.update_queue is not None and message != self.prev_update:
 
968
            self.update_queue.put(message)
 
969
            time.sleep(0)
 
970
            self.prev_update = message
 
971
 
 
972
 
 
973
    def run(self):
 
974
        pass
 
975
 
 
976
 
 
977