1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# 4# (c) Copyright 2003-2015 HP Development Company, L.P. 5# 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. 10# 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. 15# 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 19# 20# Author: Don Welch 21# 22 23# Std Lib 24import os 25import os.path 26import gzip 27import re 28import time 29import tempfile 30import glob 31 32# Local 33from base.g import * 34from base import utils, models, os_utils 35from base.sixext import PY3 36 37INVALID_PRINTER_NAME_CHARS = """~`!@#$%^&*()=+[]{}()\\/,.<>?'\";:| """ 38 39# Handle case where cups.py (via device.py) is loaded 40# and cupsext doesn't exist yet. This happens in the 41# installer and in a fresh sandbox if the Python extensions 42# aren't installed yet. 43try: 44 current_language = os.getenv("LANG") 45 newlang = "C" 46 47 # this is a workaround due CUPS rejecting all encoding except ASCII 48 # and utf-8 49 # if the locale contains the encoding, switch to the same locale, 50 # but with utf-8 encoding. Otherwise use C locale. 51 if current_language is not None and current_language.count('.'): 52 newlang, encoding = current_language.split('.') 53 newlang += ".UTF-8" 54 55 os.environ['LANG'] = newlang 56 57 import cupsext 58 59 # restore the old env values 60 if current_language is not None: 61 os.environ['LANG'] = current_language 62 63except ImportError: 64 if not os.getenv("HPLIP_BUILD"): 65 log.warn("CUPSEXT could not be loaded. Please check HPLIP installation.") 66 sys.exit(1) 67 68 69IPP_PRINTER_STATE_IDLE = 3 70IPP_PRINTER_STATE_PROCESSING = 4 71IPP_PRINTER_STATE_STOPPED = 5 72 73# Std CUPS option types 74PPD_UI_BOOLEAN = 0 # True or False option 75PPD_UI_PICKONE = 1 # Pick one from a list 76PPD_UI_PICKMANY = 2 # Pick zero or more from a list 77 78# Non-std: General 79UI_SPINNER = 100 # Simple spinner with opt. suffix (ie, %) 80UI_UNITS_SPINNER = 101 # Spinner control w/pts, cm, in, etc. units (not impl.) 81UI_BANNER_JOB_SHEETS = 102 # dual combos for banner job-sheets 82UI_PAGE_RANGE = 103 # Radio + page range entry field 83 84# Non-std: Job storage 85UI_JOB_STORAGE_MODE = 104 # Combo w/linkage 86UI_JOB_STORAGE_PIN = 105 # Radios w/PIN entry 87UI_JOB_STORAGE_USERNAME = 106 # Radios w/text entry 88UI_JOB_STORAGE_ID = 107 # Radios w/text entry 89UI_JOB_STORAGE_ID_EXISTS = 108 # Combo 90 91UI_INFO = 109 # Information field, required Information name and Value 92 93# ipp_op_t 94IPP_PAUSE_PRINTER = 0x0010 95IPP_RESUME_PRINTER = 0x011 96IPP_PURGE_JOBS = 0x012 97CUPS_GET_DEFAULT = 0x4001 98CUPS_GET_PRINTERS = 0x4002 99CUPS_ADD_MODIFY_PRINTER = 0x4003 100CUPS_DELETE_PRINTER = 0x4004 101CUPS_GET_CLASSES = 0x4005 102CUPS_ADD_MODIFY_CLASS = 0x4006 103CUPS_DELETE_CLASS = 0x4007 104CUPS_ACCEPT_JOBS = 0x4008 105CUPS_REJECT_JOBS = 0x4009 106CUPS_SET_DEFAULT = 0x400a 107CUPS_GET_DEVICES = 0x400b 108CUPS_GET_PPDS = 0x400c 109CUPS_MOVE_JOB = 0x400d 110CUPS_AUTHENTICATE_JOB = 0x400e 111 112# ipp_jstate_t 113IPP_JOB_PENDING = 3 # Job is waiting to be printed 114IPP_JOB_HELD = 4 # Job is held for printing 115IPP_JOB_PROCESSING = 5 # Job is currently printing 116IPP_JOB_STOPPED = 6 # Job has been stopped 117IPP_JOB_CANCELLED = 7 # Job has been cancelled 118IPP_JOB_ABORTED = 8 # Job has aborted due to error 119IPP_JOB_COMPLETED = 8 # Job has completed successfully 120 121# ipp_status_e 122IPP_OK = 0x0000 # successful-ok 123IPP_OK_SUBST = 0x001 # successful-ok-ignored-or-substituted-attributes 124IPP_OK_CONFLICT = 0x002 # successful-ok-conflicting-attributes 125IPP_OK_IGNORED_SUBSCRIPTIONS = 0x003 # successful-ok-ignored-subscriptions 126IPP_OK_IGNORED_NOTIFICATIONS = 0x004 # successful-ok-ignored-notifications 127IPP_OK_TOO_MANY_EVENTS = 0x005 # successful-ok-too-many-events 128IPP_OK_BUT_CANCEL_SUBSCRIPTION = 0x006 # successful-ok-but-cancel-subscription 129IPP_OK_EVENTS_COMPLETE = 0x007 # successful-ok-events-complete 130IPP_REDIRECTION_OTHER_SITE = 0x300 131IPP_BAD_REQUEST = 0x0400 # client-error-bad-request 132IPP_FORBIDDEN = 0x0401 # client-error-forbidden 133IPP_NOT_AUTHENTICATED = 0x0402 # client-error-not-authenticated 134IPP_NOT_AUTHORIZED = 0x0403 # client-error-not-authorized 135IPP_NOT_POSSIBLE = 0x0404 # client-error-not-possible 136IPP_TIMEOUT = 0x0405 # client-error-timeout 137IPP_NOT_FOUND = 0x0406 # client-error-not-found 138IPP_GONE = 0x0407 # client-error-gone 139IPP_REQUEST_ENTITY = 0x0408 # client-error-request-entity-too-large 140IPP_REQUEST_VALUE = 0x0409 # client-error-request-value-too-long 141IPP_DOCUMENT_FORMAT = 0x040a # client-error-document-format-not-supported 142IPP_ATTRIBUTES = 0x040b # client-error-attributes-or-values-not-supported 143IPP_URI_SCHEME = 0x040c # client-error-uri-scheme-not-supported 144IPP_CHARSET = 0x040d # client-error-charset-not-supported 145IPP_CONFLICT = 0x040e # client-error-conflicting-attributes 146IPP_COMPRESSION_NOT_SUPPORTED = 0x040f # client-error-compression-not-supported 147IPP_COMPRESSION_ERROR = 0x0410 # client-error-compression-error 148IPP_DOCUMENT_FORMAT_ERROR = 0x0411 # client-error-document-format-error 149IPP_DOCUMENT_ACCESS_ERROR = 0x0412 # client-error-document-access-error 150IPP_ATTRIBUTES_NOT_SETTABLE = 0x0413 # client-error-attributes-not-settable 151IPP_IGNORED_ALL_SUBSCRIPTIONS = 0x0414 # client-error-ignored-all-subscriptions 152IPP_TOO_MANY_SUBSCRIPTIONS = 0x0415 # client-error-too-many-subscriptions 153IPP_IGNORED_ALL_NOTIFICATIONS = 0x0416 # client-error-ignored-all-notifications 154IPP_PRINT_SUPPORT_FILE_NOT_FOUND = 0x0417 # client-error-print-support-file-not-found 155IPP_INTERNAL_ERROR = 0x0500 # server-error-internal-error 156IPP_OPERATION_NOT_SUPPORTED = 0x0501 # server-error-operation-not-supported 157IPP_SERVICE_UNAVAILABLE = 0x0502 # server-error-service-unavailable 158IPP_VERSION_NOT_SUPPORTED = 0x0503 # server-error-version-not-supported 159IPP_DEVICE_ERROR = 0x0504 # server-error-device-error 160IPP_TEMPORARY_ERROR = 0x0505 # server-error-temporary-error 161IPP_NOT_ACCEPTING = 0x0506 # server-error-not-accepting-jobs 162IPP_PRINTER_BUSY = 0x0507 # server-error-busy 163IPP_ERROR_JOB_CANCELLED = 0x0508 # server-error-job-canceled 164IPP_MULTIPLE_JOBS_NOT_SUPPORTED = 0x0509 # server-error-multiple-document-jobs-not-supported 165IPP_PRINTER_IS_DEACTIVATED = 0x050a # server-error-printer-is-deactivated 166 167CUPS_ERROR_BAD_NAME = 0x0f00 168CUPS_ERROR_BAD_PARAMETERS = 0x0f01 169 170nickname_pat = re.compile(r'''\*NickName:\s*\"(.*)"''', re.MULTILINE) 171pat_cups_error_log = re.compile("""^loglevel\s?(debug|debug2|warn|info|error|none)""", re.I) 172ppd_pat = re.compile(r'''.*hp-(.*?)(-.*)*\.ppd.*''', re.I) 173ppd_pat1 = re.compile(r'''.*hp-(.*?)(_.*)*\.ppd.*''', re.I) 174 175def getFamilyClassName(model): 176 models_dir = getPPDPath() 177 m = models.ModelData() 178 dict=m.read_all_files(False) 179 family_type = [] 180 for m in dict: 181 if model in m: 182 family_type= dict[m]['family-class'] 183 184 for f in models.FAMILY_CLASSES: 185 if f in family_type: 186 return f 187 188def isfamilydrv(ppds): 189 family_check=0 190 #for f in ppds: 191 # for m in models.FAMILY_CLASSES: 192 # if m in f: 193 # family_check=1 194 filename_config = "/usr/local/etc/hp/hplip.conf" 195 file_conf = open(filename_config,'r') 196 for line in file_conf: 197 if 'class-driver' in line: 198 count = line.find('=') 199 family_check_str = line[count+1:len(line)-1] 200 if family_check_str == 'yes': 201 family_check = 1 202 return family_check 203 204def getPPDPath(addtional_paths=None): 205 """ 206 Returns the CUPS ppd path (not the foomatic one under /usr/local/share/ppd). 207 Usually this is /usr/local/share/cups/model. 208 """ 209 if addtional_paths is None: 210 addtional_paths = [] 211 212 search_paths = prop.ppd_search_path.split(';') + addtional_paths 213 214 for path in search_paths: 215 ppd_path = os.path.join(path, 'cups/model') 216 if os.path.exists(ppd_path): 217 return ppd_path 218 219 220def getAllowableMIMETypes(): 221 """ 222 Scan all /usr/local/etc/cups/*.convs and /usr/local/share/cups/mime 223 files for allowable file formats. 224 """ 225 paths = [] 226 allowable_mime_types = [] 227 files = [] 228 if os.path.exists("/usr/local/etc/cups"): 229 paths.append("/usr/local/etc/cups/*.convs") 230 if os.path.exists("/usr/local/share/cups/mime"): 231 paths.append("/usr/local/share/cups/mime/*.convs") 232 for path in paths: 233 files.extend(glob.glob(path)) 234 for f in files: 235 #log.debug( "Capturing allowable MIME types from: %s" % f ) 236 conv_file = open(f, 'r') 237 238 for line in conv_file: 239 if not line.startswith("#") and len(line) > 1: 240 try: 241 source, dest, cost, prog = line.split() 242 except ValueError: 243 continue 244 245 if source not in ('application/octet-stream', 'application/vnd.cups-postscript'): 246 allowable_mime_types.append(source) 247 248 # Add some well-known MIME types that may not appear in the .convs files 249 allowable_mime_types.append("image/x-bmp") 250 allowable_mime_types.append("text/cpp") 251 allowable_mime_types.append("application/x-python") 252 allowable_mime_types.append("application/hplip-fax") 253 254 return allowable_mime_types 255 256 257def getPPDDescription(f): 258 if f.endswith('.gz'): 259 nickname = gzip.GzipFile(f, 'r').read(4096) 260 else: 261 nickname = open(f, 'r').read(4096) 262 263 try: 264 desc = nickname_pat.search(nickname.decode('utf-8')).group(1) 265 except AttributeError: 266 desc = '' 267 268 return desc 269 270 271def getSystemPPDs(): 272 major, minor, patch = getVersionTuple() 273 ppds = {} # {'ppd name' : 'desc', ...} 274 275 if major == 1 and minor < 2: 276 ppd_dir = sys_conf.get('dirs', 'ppd') 277 log.debug("(CUPS 1.1.x) Searching for PPDs in: %s" % ppd_dir) 278 279 for f in utils.walkFiles(ppd_dir, pattern="HP*ppd*;hp*ppd*", abs_paths=True): 280 desc = getPPDDescription(f) 281 282 if not ('foo2' in desc or 283 'gutenprint' in desc.lower() or 284 'gutenprint' in f): 285 286 ppds[f] = desc 287 log.debug("%s: %s" % (f, desc)) 288 289 else: # 1.2.x 290 log.debug("(CUPS 1.2.x) Getting list of PPDs using CUPS_GET_PPDS...") 291 ppd_dict = cupsext.getPPDList() 292 cups_ppd_path = getPPDPath() # usually /usr/local/share/cups/model 293 foomatic_ppd_path = sys_conf.get('dirs', 'ppdbase', '/usr/local/share/ppd') 294 295 if not foomatic_ppd_path or not os.path.exists(foomatic_ppd_path): 296 foomatic_ppd_path = '/usr/local/share/ppd' 297 298 log.debug("CUPS PPD base path = %s" % cups_ppd_path) 299 log.debug("Foomatic PPD base path = %s" % foomatic_ppd_path) 300 301 for ppd in ppd_dict: 302 if not ppd: 303 continue 304 305 if 'hp-' in ppd.lower() or 'hp_' in ppd.lower() and \ 306 ppd_dict[ppd]['ppd-make'] == 'HP': 307 308 desc = ppd_dict[ppd]['ppd-make-and-model'] 309 310 if not ('foo2' in desc.lower() or 311 'gutenprint' in desc.lower() or 312 'gutenprint' in ppd): 313 314 # PPD files returned by CUPS_GET_PPDS (and by lpinfo -m) 315 # can be relative to /usr/local/share/ppd/ or to 316 # /usr/local/share/cups/model/. Not sure why this is. 317 # Here we will try both and see which one it is... 318 319 if os.path.exists(ppd): 320 path = ppd 321 else: 322 try: 323 path = os.path.join(foomatic_ppd_path, ppd) 324 except AttributeError: # happens on some boxes with provider: style ppds (foomatic: etc) 325 path = ppd 326 else: 327 if not os.path.exists(path): 328 try: 329 path = os.path.join(cups_ppd_path, ppd) 330 except AttributeError: 331 path = ppd 332 else: 333 if not os.path.exists(path): 334 path = ppd # foomatic: or some other driver 335 336 ppds[path] = desc 337 #log.debug("%s: %s" % (path, desc)) 338 339 return ppds 340 341 342## TODO: Move this to CUPSEXT for better performance 343def levenshtein_distance(a,b): 344 """ 345 Calculates the Levenshtein distance between a and b. 346 Written by Magnus Lie Hetland. 347 """ 348 n, m = len(a), len(b) 349 if n > m: 350 a,b = b,a 351 n,m = m,n 352 353 current = list(range(n+1)) 354 for i in range(1,m+1): 355 previous, current = current, [i]+[0]*m 356 357 for j in range(1,n+1): 358 add, delete = previous[j]+1, current[j-1]+1 359 change = previous[j-1] 360 361 if a[j-1] != b[i-1]: 362 change = change + 1 363 364 current[j] = min(add, delete, change) 365 366 return current[n] 367 368 369number_pat = re.compile(r""".*?(\d+)""", re.IGNORECASE) 370 371STRIP_STRINGS2 = ['foomatic:', 'hp-', 'hp_', 'hp ', '.gz', '.ppd', 372 'drv:', '-pcl', '-pcl3', '-jetready', 373 '-zxs', '-zjs', '-ps', '-postscript', '-pdf', 374 '-jr', '-lidl', '-lidil', '-ldl', '-hpijs'] 375 376 377for p in list(models.TECH_CLASS_PDLS.values()): 378 pp = '-%s' % p 379 if pp not in STRIP_STRINGS2: 380 STRIP_STRINGS2.append(pp) 381 382 383STRIP_STRINGS = STRIP_STRINGS2[:] 384STRIP_STRINGS.extend(['-series', ' series', '_series']) 385 386 387def stripModel2(model): # For new 2.8.10+ PPD find algorithm 388 model = model.lower() 389 390 for x in STRIP_STRINGS2: 391 model = model.replace(x, '') 392 393 return model 394 395 396def stripModel(model): # for old PPD find algorithm (removes "series" as well) 397 model = model.lower() 398 399 for x in STRIP_STRINGS: 400 model = model.replace(x, '') 401 402 return model 403 404 405def getPPDFile(stripped_model, ppds): # Old PPD find 406 """ 407 Match up a model name to a PPD from a list of system PPD files. 408 """ 409 log.debug("1st stage edit distance match") 410 mins = {} 411 eds = {} 412 min_edit_distance = sys.maxsize 413 414 log.debug("Determining edit distance from %s (only showing edit distances < 4)..." % stripped_model) 415 for f in ppds: 416 t = stripModel(os.path.basename(f)) 417 eds[f] = levenshtein_distance(stripped_model, t) 418 if eds[f] < 4: 419 log.debug("dist('%s') = %d" % (t, eds[f])) 420 min_edit_distance = min(min_edit_distance, eds[f]) 421 422 log.debug("Min. dist = %d" % min_edit_distance) 423 424 for f in ppds: 425 if eds[f] == min_edit_distance: 426 for m in mins: 427 if os.path.basename(m) == os.path.basename(f): 428 break # File already in list possibly with different path (Ubuntu, etc) 429 else: 430 mins[f] = ppds[f] 431 432 log.debug(mins) 433 434 if len(mins) > 1: # try pattern matching the model number 435 log.debug("2nd stage matching with model number") 436 437 try: 438 model_number = number_pat.match(stripped_model).group(1) 439 model_number = int(model_number) 440 except AttributeError: 441 pass 442 except ValueError: 443 pass 444 else: 445 log.debug("model_number=%d" % model_number) 446 matches = {} #[] 447 for x in range(3): # 1, 10, 100 448 factor = 10**x 449 log.debug("Factor = %d" % factor) 450 adj_model_number = int(model_number/factor)*factor 451 number_matching, match = 0, '' 452 453 for m in mins: 454 try: 455 mins_model_number = number_pat.match(os.path.basename(m)).group(1) 456 mins_model_number = int(mins_model_number) 457 log.debug("mins_model_number= %d" % mins_model_number) 458 except AttributeError: 459 continue 460 except ValueError: 461 continue 462 463 mins_adj_model_number = int(mins_model_number/factor)*factor 464 log.debug("mins_adj_model_number=%d" % mins_adj_model_number) 465 log.debug("adj_model_number=%d" % adj_model_number) 466 467 if mins_adj_model_number == adj_model_number: 468 log.debug("match") 469 number_matching += 1 470 matches[m] = ppds[m] 471 log.debug(matches) 472 473 log.debug("***") 474 475 if len(matches): 476 mins = matches 477 break 478 479 return mins 480 481 482def getPPDFile2(mq,model, ppds): # New PPD find 483 # This routine is for the new PPD naming scheme begun in 2.8.10 484 # and beginning with implementation in 2.8.12 (Qt4 hp-setup) 485 # hp-<model name from models.dat w/o beginning hp_>[-<pdl>][-<pdl>][...].ppd[.gz] 486 # 3.9.6: Added handling for hpijs vs. hpcups PPDs/DRVs 487 488 489 #Check if common ppd name is already given in models.dat(This is needed because in case of devices having more than one derivatives 490 #will have diffrent model name strings in device ID, because of which we don't get the common ppd name for search) 491 family_check=isfamilydrv(ppds) 492 family_class=getFamilyClassName(model) 493 model = models.normalizeModelName(model) 494 if family_check==0: 495 ppd_name = mq.get('ppd-name',0) 496 else: 497 ppd_name = mq.get('family-ppd',0) 498 499 if ppd_name == 0: 500 stripped_model = stripModel2(model) 501 else: 502 stripped_model = stripModel2(ppd_name) 503 504 log.debug("Matching PPD list to model %s..." % stripped_model) 505 506 matches = [] 507 if family_check ==0 : 508 for f in ppds: 509 match = ppd_pat.match(f) 510 if match is not None: 511 if match.group(1) == stripped_model: 512 log.debug("Found match: %s" % f) 513 try: 514 pdls = match.group(2).split('-') 515 except AttributeError: 516 pdls = [] 517 if (prop.hpcups_build and 'hpijs' not in f) or \ 518 ((prop.hpijs_build and 'hpijs' in pdls) or (prop.hpcups_build and 'hpijs' not in pdls)) or \ 519 ('ps' in pdls) or ('pdf' in pdls): 520 matches.append((f, [p for p in pdls if p and p != 'hpijs'])) 521 else: 522 for f in ppds: 523 match = ppd_pat1.match(f) 524 if match is not None: 525 if match.group(1) == family_class: 526 log.debug("Found match: %s" % f) 527 try: 528 pdls = match.group(2).split('-') 529 except AttributeError: 530 pdls = [] 531 if (prop.hpcups_build and 'hpijs' not in f) or \ 532 ((prop.hpijs_build and 'hpijs' in pdls) or (prop.hpcups_build and 'hpijs' not in pdls)) or \ 533 ('ps' in pdls) or ('pdf' in pdls): 534 matches.append((f, [p for p in pdls if p and p != 'hpijs'])) 535 log.debug(matches) 536 num_matches = len(matches) 537 538 if num_matches == 0: 539 log.debug("No PPD found for model %s using new algorithm. Trying old algorithm..." % stripped_model) 540 #Using Old algo, ignores the series keyword in ppd searching. 541 matches2 = list(getPPDFile(stripModel(stripped_model), ppds).items()) 542 log.debug(matches2) 543 num_matches2 = len(matches2) 544 if num_matches2: 545 for f, d in matches2: 546 match = ppd_pat.match(f) 547 if match is not None: 548 log.debug("Found match: %s" % f) 549 try: 550 pdls = match.group(2).split('-') 551 except AttributeError: 552 pdls = [] 553 554 if (prop.hpcups_build and 'hpijs' not in f) or \ 555 ((prop.hpijs_build and 'hpijs' in pdls) or (prop.hpcups_build and 'hpijs' not in pdls)) or \ 556 ('ps' in pdls) or ('pdf' in pdls): 557 matches.append((f, [p for p in pdls if p and p != 'hpijs'])) 558 559 log.debug(matches) 560 num_matches = len(matches) 561 562 if num_matches == 0: 563 log.error("No PPD found for model %s using old algorithm." % stripModel(stripped_model)) 564 return None 565 566 elif num_matches == 1: 567 log.debug("One match found.") 568 return (matches[0][0], '') 569 570 # > 1 571 log.debug("%d matches found. Searching based on PDL: Host > PS,PDF > PCL/Other" % num_matches) 572 for p in [models.PDL_TYPE_HOST, models.PDL_TYPE_PS,models.PDL_TYPE_PDF, models.PDL_TYPE_PCL]: 573 for f, pdl_list in matches: 574 for x in pdl_list: 575 # default to HOST-based PDLs, as newly supported PDLs will most likely be of this type 576 if models.PDL_TYPES.get(x, models.PDL_TYPE_HOST) == p: 577 log.debug("Selecting '-%s' PPD: %s" % (x, f)) 578 return (f, '') 579 580 log.debug("%d matches found. Searching based on Filters: HPCUPS > HPIJS" % num_matches) 581 for p in ["hpcups","hpijs"]: 582 for f, pdl_list in matches: 583 if p in f: 584 log.debug("Selecting PPD: %s" % (f)) 585 return (f, '') 586 587 # No specific PDL or Filter found, so just return 1st found PPD file 588 log.debug("No specific PDL located. Defaulting to first found PPD file.") 589 return (matches[0][0], '') 590 591## 592# Function :- getFaxPPDFile() 593# Arguments:- 594# 1) mq --> Device model query object 595# 2) model --> Fax model name 596# Return arguments:- 597# 1) fax_ppd --> Found Fax ppd file. (Returns None if not found) 598# 2) expt_fax_ppd_name -> Expected Fax PPD name 599# 3) nick --> Expected Fax PPD description 600# 601def getFaxPPDFile(mq, model): 602 try: 603 fax_ppd = None 604 nick = "HP Fax hpcups" 605 expected_fax_ppd_name = "HP-Fax-hpcups" 606 log.debug("Searching for fax PPD for model %s hpcups_build =%d" % (model,prop.hpcups_build)) 607 if prop.hpcups_build: 608 if mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_MARVELL: 609 expected_fax_ppd_name = "HP-Fax3-hpcups" # Fixed width (2528 pixels) and 300dpi rendering 610 nick = "HP Fax3 hpcups" 611 elif mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_SOAP or mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_LEDMSOAP: 612 expected_fax_ppd_name = "HP-Fax2-hpcups" # Fixed width (2528 pixels) and 300dpi rendering 613 nick = "HP Fax2 hpcups" 614 elif mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_LEDM: 615 expected_fax_ppd_name = "HP-Fax4-hpcups"# Fixed width (2528 pixels) and 300dpi rendering 616 nick = "HP Fax4 hpcups" 617 else: 618 expected_fax_ppd_name = "HP-Fax-hpcups" # Standard 619 nick = "HP Fax hpcups" 620 621 else: # hpijs 622 if mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_MARVELL: 623 expected_fax_ppd_name = "HP-Fax3-hpijs" # Fixed width (2528 pixels) and 300dpi rendering 624 nick = "HP Fax3 hpijs" 625 if mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_SOAP or mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_LEDMSOAP: 626 expected_fax_ppd_name = "HP-Fax2-hpijs" # Fixed width (2528 pixels) and 300dpi rendering 627 nick = "HP Fax2 hpijs" 628 if mq.get('fax-type', FAX_TYPE_NONE) == FAX_TYPE_LEDM: 629 expected_fax_ppd_name = "HP-Fax4-hpijs" # Fixed width (2528 pixels) and 300dpi rendering 630 nick = "HP Fax4 hpijs" 631 else: 632 expected_fax_ppd_name = "HP-Fax-hpijs" # Standard 633 nick = "HP Fax hpijs" 634 635 ppds = [] 636 for f in utils.walkFiles(sys_conf.get('dirs', 'ppd'), pattern="HP-Fax*.ppd*", abs_paths=True): 637 ppds.append(f) 638 log.debug("ppds=%s"%ppds) 639 for f in ppds: 640 if f.find(expected_fax_ppd_name) >= 0 and getPPDDescription(f) == nick: 641 fax_ppd = f 642 log.debug("Found fax PPD: %s" % f) 643 break 644 else: 645 log.error("Unable to locate the HPLIP Fax PPD file: %s.ppd.gz file."%expected_fax_ppd_name) 646 647 finally: 648 return fax_ppd,expected_fax_ppd_name, nick 649 650 651 652 653def getErrorLogLevel(): 654 cups_conf = '/usr/local/etc/cups/cupsd.conf' 655 try: 656 f = open(cups_conf, 'r') 657 except OSError: 658 log.error("%s not found." % cups_conf) 659 except IOError: 660 log.error("%s: I/O error." % cups_conf) 661 else: 662 for l in f: 663 m = pat_cups_error_log.match(l) 664 if m is not None: 665 level = m.group(1).lower() 666 log.debug("CUPS error_log LogLevel: %s" % level) 667 return level 668 669 log.debug("CUPS error_log LogLevel: unknown") 670 return 'unknown' 671 672 673def getPrintJobErrorLog(job_id, max_lines=1000, cont_interval=5): 674 ret = [] 675 s = '[Job %d]' % job_id 676 #level = getErrorLogLevel() 677 cups_conf = '/var/log/cups/error_log' 678 679 #if level in ('debug', 'debug2'): 680 if 1: 681 try: 682 f = open(cups_conf, 'r') 683 except (IOError, OSError): 684 log.error("Could not open the CUPS error_log file: %s" % cups_conf) 685 return '' 686 687 else: 688 if s in open(cups_conf, 'r').read(): 689 queue = utils.Queue() 690 job_found = False 691 692 while True: 693 line = f.readline() 694 695 if s in line: 696 job_found = True 697 698 while len(queue): 699 ret.append(queue.get()) 700 701 ret.append(line.strip()) 702 703 if len(ret) > max_lines: 704 break 705 706 else: 707 if job_found: 708 queue.put(line.strip()) 709 710 if len(queue) > cont_interval: 711 break 712 713 return '\n'.join(ret) 714 715 716# 717# cupsext wrappers 718# 719 720def getDefaultPrinter(): 721 r = cupsext.getDefaultPrinter() 722 if r is None: 723 log.debug("The CUPS default printer is not set.") 724 return r 725 726def setDefaultPrinter(printer_name): 727 if PY3: 728 printer_name = str(printer_name, "utf-8") 729 setPasswordPrompt("You do not have permission to set the default printer. You need authentication.") 730 return cupsext.setDefaultPrinter(printer_name) 731 732def accept(printer_name): 733 setPasswordPrompt("You do not have permission to accept jobs on a printer queue. You need authentication.") 734 return controlPrinter(printer_name, CUPS_ACCEPT_JOBS) 735 736def reject(printer_name): 737 setPasswordPrompt("You do not have permission to reject jobs on a printer queue. You need authentication.") 738 return controlPrinter(printer_name, CUPS_REJECT_JOBS) 739 740def start(printer_name): 741 setPasswordPrompt("You do not have permission to start a printer queue. You need authentication.") 742 return controlPrinter(printer_name, IPP_RESUME_PRINTER) 743 744def stop(printer_name): 745 setPasswordPrompt("You do not have permission to stop a printer queue. You need authentication.") 746 return controlPrinter(printer_name, IPP_PAUSE_PRINTER) 747 748def purge(printer_name): 749 setPasswordPrompt("You do not have permission to purge jobs. You need authentication.") 750 return controlPrinter(printer_name, IPP_PURGE_JOBS) 751 752def controlPrinter(printer_name, cups_op): 753 if cups_op in (CUPS_ACCEPT_JOBS, CUPS_REJECT_JOBS, IPP_PAUSE_PRINTER, IPP_RESUME_PRINTER, IPP_PURGE_JOBS): 754 return cupsext.controlPrinter(printer_name, cups_op) 755 756 return 0; 757 758def openPPD(printer): 759 if not printer: 760 return 761 762 return cupsext.openPPD(printer) 763 764def closePPD(): 765 return cupsext.closePPD() 766 767def getPPD(printer): 768 if not printer: 769 return 770 771 return cupsext.getPPD(printer) 772 773def getPPDOption(option): 774 return cupsext.getPPDOption(option) 775 776def getPPDPageSize(): 777 return cupsext.getPPDPageSize() 778 779def getPrinters(): 780## p2 = [] 781## p = cupsext.getPrinters() 782## for pp in p: 783## print pp 784## try: 785## pn = pp.name.decode('utf-8') 786## except UnicodeError: 787## pass 788## 789## p2.append(pp) 790## 791## return p2 792 return cupsext.getPrinters() 793 794def getJobs(my_job=0, completed=0): 795 return cupsext.getJobs(my_job, completed) 796 797def getAllJobs(my_job=0): 798 return cupsext.getJobs(my_job, 0) + cupsext.getJobs(my_job, 1) 799 800def getVersion(): 801 return cupsext.getVersion() 802 803def getVersionTuple(): 804 return cupsext.getVersionTuple() 805 806def getServer(): 807 return cupsext.getServer() 808 809def cancelJob(jobid, dest=None): 810 setPasswordPrompt("You do not have permission to cancel a job. You need authentication.") 811 if dest is not None: 812 return cupsext.cancelJob(dest, jobid) 813 else: 814 jobs = cupsext.getJobs(0, 0) 815 for j in jobs: 816 if j.id == jobid: 817 return cupsext.cancelJob(j.dest, jobid) 818 819 return False 820 821def resetOptions(): 822 return cupsext.resetOptions() 823 824def addOption(option): 825 return cupsext.addOption(option) 826 827def getOptions(): 828 return cupsext.getOptions() 829 830def duplicateSection(section): 831 return cupsext.duplicateSection(section) 832 833def printFile(printer, filename, title): 834 if os.path.exists(filename): 835 if not PY3: 836 printer = printer.encode('utf-8') 837 filename = filename.encode('utf-8') 838 title = title.encode('utf-8') 839 840 return cupsext.printFileWithOptions(printer, filename, title) 841 842 else: 843 return -1 844 845def addPrinter(printer_name, device_uri, location, ppd_file, model, info): 846 setPasswordPrompt("You do not have permission to add a printer. You need authentication.") 847 log.debug("addPrinter('%s', '%s', '%s', '%s', '%s', '%s')" % 848 ( printer_name, device_uri, location, ppd_file, model, info)) 849 850 if ppd_file and not os.path.exists(ppd_file): 851 log.error("PPD file '%s' not found." % ppd_file) 852 return (-1, "PPD file not found") 853 854 return cupsext.addPrinter(printer_name, device_uri, location, ppd_file, model, info) 855 856def delPrinter(printer_name): 857 setPasswordPrompt("You do not have permission to delete a printer. You need authentication.") 858 return cupsext.delPrinter(printer_name) 859 860def enablePrinter(printer_name): 861 setPasswordPrompt("You do not have permission to enable a printer. You need authentication.") 862 cmd_full_path = utils.which('cupsenable', True) 863 cmd= "%s %s" % (cmd_full_path, printer_name) 864 return os_utils.execute(cmd) 865 866def getGroupList(): 867 return cupsext.getGroupList() 868 869def getGroup(group): 870 return cupsext.getGroup(group) 871 872def getOptionList(group): 873 return cupsext.getOptionList(group) 874 875def getOption(group, option): 876 return cupsext.getOption(group, option) 877 878def getChoiceList(group, option): 879 return cupsext.getChoiceList(group, option) 880 881def getChoice(group, option, choice): 882 return cupsext.getChoice(group, option, choice) 883 884def setOptions(): 885 return cupsext.setOptions() 886 887def removeOption(option): 888 return cupsext.removeOption(option) 889 890def setPasswordCallback(func): 891 return cupsext.setPasswordCallback(func) 892 893def setPasswordPrompt(prompt): 894 return cupsext.setPasswordPrompt(prompt) 895 896def findPPDAttribute(name, spec): 897 return cupsext.findPPDAttribute(name, spec) 898 899def releaseCupsInstance(): 900 return cupsext.releaseCupsInstance() 901 902 903def cups_operation(operation_func, mode, ui_toolkit, ui_obj, *cups_op_args): 904 cnt = 0 905 while cnt < 3: 906 cnt += 1 907 result, status_str = operation_func(*cups_op_args) 908 if result != IPP_FORBIDDEN: 909 break 910 else: 911 releaseCupsInstance() 912 if cnt < 3: 913 if mode == INTERACTIVE_MODE: 914 log.error("Could not connect to CUPS Server due to insufficient privileges.Try with valid user") 915 elif ui_toolkit == 'qt3': 916 ui_obj.FailureUI("<b>Could not connect to CUPS Server due to insufficient privileges.</b><p>Try with valid user") 917 else: 918 from ui4 import ui_utils 919 ui_utils.FailureUI(ui_obj, "<b>Could not connect to CUPS Server due to insufficient privileges.</b><p>Try with valid user") 920 921 return result, status_str 922