1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_vcl.hxx" 30 31 #ifdef ENABLE_CUPS 32 #include <cups/cups.h> 33 #include <cups/ppd.h> 34 35 #else // !ENABLE_CUPS 36 typedef void ppd_file_t; 37 typedef void cups_dest_t; 38 typedef void cups_option_t; 39 #endif 40 41 #include <unistd.h> 42 43 #include "cupsmgr.hxx" 44 45 #include "osl/thread.h" 46 #include "osl/diagnose.h" 47 #include "osl/conditn.hxx" 48 49 #include "rtl/ustrbuf.hxx" 50 51 #include <algorithm> 52 #include <setjmp.h> 53 #include <signal.h> 54 55 #define CUPS_LIB_NAME "libcups.so.2" 56 57 namespace psp 58 { 59 class CUPSWrapper 60 { 61 oslModule m_pLib; 62 osl::Mutex m_aGetPPDMutex; 63 bool m_bPPDThreadRunning; 64 65 int (*m_pcupsPrintFile)(const char*, const char*, const char*, int, cups_option_t*); 66 int (*m_pcupsGetDests)(cups_dest_t**); 67 void (*m_pcupsSetDests)(int,cups_dest_t*); 68 void (*m_pcupsFreeDests)(int,cups_dest_t*); 69 const char* (*m_pcupsGetPPD)(const char*); 70 int (*m_pcupsMarkOptions)(ppd_file_t*,int,cups_option_t*); 71 int (*m_pcupsAddOption)(const char*,const char*,int,cups_option_t**); 72 void (*m_pcupsFreeOptions)(int,cups_option_t*); 73 ppd_file_t* (*m_pppdOpenFile)(const char* pFile); 74 void (*m_pppdClose)(ppd_file_t*); 75 const char* (*m_pcupsServer)(); 76 void (*m_pcupsSetPasswordCB)(const char*(cb)(const char*)); 77 const char* (*m_pcupsUser)(); 78 void (*m_pcupsSetUser)(const char*); 79 const char* (*m_pcupsGetOption)(const char*,int,cups_option_t*); 80 81 oslGenericFunction loadSymbol( const char* ); 82 public: 83 CUPSWrapper(); 84 ~CUPSWrapper(); 85 86 bool isValid(); 87 88 int cupsGetDests(cups_dest_t** pDests) 89 { return m_pcupsGetDests(pDests); } 90 91 void cupsSetDests( int nDests, cups_dest_t* pDests ) 92 { m_pcupsSetDests( nDests, pDests ); } 93 94 void cupsFreeDests(int nDests, cups_dest_t* pDests) 95 { m_pcupsFreeDests(nDests, pDests); } 96 97 int cupsPrintFile( const char* pPrinter, 98 const char* pFileName, 99 const char* pTitle, 100 int nOptions, 101 cups_option_t* pOptions ) 102 { return m_pcupsPrintFile( pPrinter, pFileName, pTitle, nOptions, pOptions ); } 103 104 rtl::OString cupsGetPPD( const char* pPrinter ); 105 106 int cupsMarkOptions(ppd_file_t* pPPD, int nOptions, cups_option_t* pOptions ) 107 { return m_pcupsMarkOptions(pPPD, nOptions, pOptions); } 108 109 int cupsAddOption( const char* pName, const char* pValue, int nOptions, cups_option_t** pOptions ) 110 { return m_pcupsAddOption( pName, pValue, nOptions, pOptions ); } 111 112 void cupsFreeOptions( int nOptions, cups_option_t* pOptions ) 113 { m_pcupsFreeOptions( nOptions, pOptions ); } 114 115 ppd_file_t* ppdOpenFile( const char* pFileName ) 116 { return m_pppdOpenFile( pFileName ); } 117 118 void ppdClose( ppd_file_t* pPPD ) 119 { m_pppdClose( pPPD ); } 120 121 const char *cupsServer(void) 122 { return m_pcupsServer(); } 123 124 const char *cupsUser(void) 125 { return m_pcupsUser(); } 126 127 void cupsSetPasswordCB(const char *(*cb)(const char *)) 128 { m_pcupsSetPasswordCB( cb ); } 129 130 void cupsSetUser(const char *user) 131 { m_pcupsSetUser( user ); } 132 133 const char* cupsGetOption(const char* name, int num_options, cups_option_t* options) 134 { return m_pcupsGetOption( name, num_options, options ); } 135 136 }; 137 } 138 139 using namespace psp; 140 using namespace osl; 141 using namespace rtl; 142 143 /* 144 * CUPSWrapper class 145 */ 146 147 oslGenericFunction CUPSWrapper::loadSymbol( const char* pSymbol ) 148 { 149 OUString aSym( OUString::createFromAscii( pSymbol ) ); 150 oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); 151 #if OSL_DEBUG_LEVEL > 1 152 fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); 153 #endif 154 return pSym; 155 } 156 157 CUPSWrapper::CUPSWrapper() 158 : m_pLib( NULL ), 159 m_bPPDThreadRunning( false ) 160 { 161 #ifdef ENABLE_CUPS 162 OUString aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME ) ); 163 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); 164 if( ! m_pLib ) 165 { 166 aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) ); 167 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); 168 } 169 #endif 170 171 if( ! m_pLib ) 172 { 173 #if OSL_DEBUG_LEVEL > 1 174 fprintf( stderr, "no cups library found\n" ); 175 #endif 176 return; 177 } 178 179 m_pcupsPrintFile = (int(*)(const char*,const char*,const char*,int,cups_option_t*)) 180 loadSymbol( "cupsPrintFile" ); 181 m_pcupsGetDests = (int(*)(cups_dest_t**)) 182 loadSymbol( "cupsGetDests" ); 183 m_pcupsSetDests = (void(*)(int,cups_dest_t*)) 184 loadSymbol( "cupsSetDests" ); 185 m_pcupsFreeDests = (void(*)(int,cups_dest_t*)) 186 loadSymbol( "cupsFreeDests" ); 187 m_pcupsGetPPD = (const char*(*)(const char*)) 188 loadSymbol( "cupsGetPPD" ); 189 m_pcupsMarkOptions = (int(*)(ppd_file_t*,int,cups_option_t*)) 190 loadSymbol( "cupsMarkOptions" ); 191 m_pcupsAddOption = (int(*)(const char*,const char*,int,cups_option_t**)) 192 loadSymbol( "cupsAddOption" ); 193 m_pcupsFreeOptions = (void(*)(int,cups_option_t*)) 194 loadSymbol( "cupsFreeOptions" ); 195 m_pppdOpenFile = (ppd_file_t*(*)(const char*)) 196 loadSymbol( "ppdOpenFile" ); 197 m_pppdClose = (void(*)(ppd_file_t*)) 198 loadSymbol( "ppdClose" ); 199 m_pcupsServer = (const char*(*)()) 200 loadSymbol( "cupsServer" ); 201 m_pcupsUser = (const char*(*)()) 202 loadSymbol( "cupsUser" ); 203 m_pcupsSetPasswordCB = (void(*)(const char*(*)(const char*))) 204 loadSymbol( "cupsSetPasswordCB" ); 205 m_pcupsSetUser = (void(*)(const char*)) 206 loadSymbol( "cupsSetUser" ); 207 m_pcupsGetOption = (const char*(*)(const char*,int,cups_option_t*)) 208 loadSymbol( "cupsGetOption" ); 209 210 if( ! ( 211 m_pcupsPrintFile && 212 m_pcupsGetDests && 213 m_pcupsSetDests && 214 m_pcupsFreeDests && 215 m_pcupsGetPPD && 216 m_pcupsMarkOptions && 217 m_pcupsAddOption && 218 m_pcupsServer && 219 m_pcupsUser && 220 m_pcupsSetPasswordCB && 221 m_pcupsSetUser && 222 m_pcupsFreeOptions && 223 m_pppdOpenFile && 224 m_pppdClose && 225 m_pcupsGetOption 226 ) ) 227 { 228 osl_unloadModule( m_pLib ); 229 m_pLib = NULL; 230 } 231 } 232 233 CUPSWrapper::~CUPSWrapper() 234 { 235 if( m_pLib ) 236 osl_unloadModule( m_pLib ); 237 } 238 239 bool CUPSWrapper::isValid() 240 { 241 return m_pLib != NULL; 242 } 243 244 typedef const char*(*PPDFunction)(const char*); 245 struct GetPPDAttribs 246 { 247 PPDFunction m_pFunction; 248 osl::Condition m_aCondition; 249 OString m_aParameter; 250 OString m_aResult; 251 oslThread m_aThread; 252 int m_nRefs; 253 bool* m_pResetRunning; 254 osl::Mutex* m_pSyncMutex; 255 256 GetPPDAttribs( PPDFunction pFn, const char * m_pParameter, 257 bool* pResetRunning, osl::Mutex* pSyncMutex ) 258 : m_pFunction( pFn ), 259 m_aParameter( m_pParameter ), 260 m_pResetRunning( pResetRunning ), 261 m_pSyncMutex( pSyncMutex ) 262 { 263 m_nRefs = 2; 264 m_aCondition.reset(); 265 } 266 267 ~GetPPDAttribs() 268 { 269 if( m_aResult.getLength() ) 270 unlink( m_aResult.getStr() ); 271 } 272 273 void unref() 274 { 275 if( --m_nRefs == 0 ) 276 { 277 *m_pResetRunning = false; 278 delete this; 279 } 280 } 281 282 void executeCall() 283 { 284 // This CUPS method is not at all thread-safe we need 285 // to dup the pointer to a static buffer it returns ASAP 286 OString aResult = m_pFunction( m_aParameter ); 287 MutexGuard aGuard( *m_pSyncMutex ); 288 m_aResult = aResult; 289 m_aCondition.set(); 290 unref(); 291 } 292 293 OString waitResult( TimeValue *pDelay ) 294 { 295 m_pSyncMutex->release(); 296 297 if (m_aCondition.wait( pDelay ) != Condition::result_ok 298 ) 299 { 300 #if OSL_DEBUG_LEVEL > 1 301 fprintf( stderr, "cupsGetPPD %s timed out\n", 302 (const sal_Char *) m_aParameter 303 ); 304 #endif 305 } 306 m_pSyncMutex->acquire(); 307 308 OString aRetval = m_aResult; 309 m_aResult = OString(); 310 unref(); 311 312 return aRetval; 313 } 314 }; 315 316 extern "C" { 317 static void getPPDWorker(void* pData) 318 { 319 GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData; 320 pAttribs->executeCall(); 321 } 322 } 323 324 OString CUPSWrapper::cupsGetPPD( const char* pPrinter ) 325 { 326 OString aResult; 327 328 m_aGetPPDMutex.acquire(); 329 // if one thread hangs in cupsGetPPD already, don't start another 330 if( ! m_bPPDThreadRunning ) 331 { 332 m_bPPDThreadRunning = true; 333 GetPPDAttribs* pAttribs = new GetPPDAttribs( m_pcupsGetPPD, 334 pPrinter, 335 &m_bPPDThreadRunning, 336 &m_aGetPPDMutex ); 337 338 oslThread aThread = osl_createThread( getPPDWorker, pAttribs ); 339 340 TimeValue aValue; 341 aValue.Seconds = 5; 342 aValue.Nanosec = 0; 343 344 // NOTE: waitResult release and acquires the GetPPD mutex 345 aResult = pAttribs->waitResult( &aValue ); 346 osl_destroyThread( aThread ); 347 } 348 m_aGetPPDMutex.release(); 349 350 return aResult; 351 } 352 353 #ifdef ENABLE_CUPS 354 static const char* setPasswordCallback( const char* pIn ) 355 { 356 const char* pRet = NULL; 357 358 PrinterInfoManager& rMgr = PrinterInfoManager::get(); 359 if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check 360 pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn ); 361 return pRet; 362 } 363 #endif 364 365 /* 366 * CUPSManager class 367 */ 368 369 CUPSManager* CUPSManager::tryLoadCUPS() 370 { 371 CUPSManager* pManager = NULL; 372 #ifdef ENABLE_CUPS 373 static const char* pEnv = getenv( "SAL_DISABLE_CUPS" ); 374 375 if( ! pEnv || ! *pEnv ) 376 { 377 // try to load CUPS 378 CUPSWrapper* pWrapper = new CUPSWrapper(); 379 if( pWrapper->isValid() ) 380 pManager = new CUPSManager( pWrapper ); 381 else 382 delete pWrapper; 383 } 384 #endif 385 return pManager; 386 } 387 388 extern "C" 389 { 390 static void run_dest_thread_stub( void* pThis ) 391 { 392 CUPSManager::runDestThread( pThis ); 393 } 394 } 395 396 CUPSManager::CUPSManager( CUPSWrapper* pWrapper ) : 397 PrinterInfoManager( CUPS ), 398 m_pCUPSWrapper( pWrapper ), 399 m_nDests( 0 ), 400 m_pDests( NULL ), 401 m_bNewDests( false ) 402 { 403 m_aDestThread = osl_createThread( run_dest_thread_stub, this ); 404 } 405 406 CUPSManager::~CUPSManager() 407 { 408 if( m_aDestThread ) 409 { 410 // if the thread is still running here, then 411 // cupsGetDests is hung; terminate the thread instead of joining 412 osl_terminateThread( m_aDestThread ); 413 osl_destroyThread( m_aDestThread ); 414 } 415 416 if( m_nDests && m_pDests ) 417 m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); 418 delete m_pCUPSWrapper; 419 } 420 421 void CUPSManager::runDestThread( void* pThis ) 422 { 423 ((CUPSManager*)pThis)->runDests(); 424 } 425 426 static sigjmp_buf aViolationBuffer; 427 428 extern "C" 429 { 430 static void lcl_signal_action(int nSignal) 431 { 432 fprintf( stderr, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal ); 433 siglongjmp( aViolationBuffer, 1 ); 434 } 435 } 436 437 void CUPSManager::runDests() 438 { 439 #if OSL_DEBUG_LEVEL > 1 440 fprintf( stderr, "starting cupsGetDests\n" ); 441 #endif 442 int nDests = 0; 443 cups_dest_t* pDests = NULL; 444 445 // #i86306# prepare against really broken CUPS installations / missing servers 446 447 // install signal handler for SEGV, BUS and ABRT 448 struct sigaction act; 449 struct sigaction oact[3]; 450 451 act.sa_handler = lcl_signal_action; 452 act.sa_flags = 0; 453 sigemptyset(&(act.sa_mask)); 454 455 int nSegvSignalInstalled = sigaction(SIGSEGV, &act, &oact[0]); 456 int nBusSignalInstalled = sigaction(SIGBUS, &act, &oact[1]); 457 int nAbortSignalInstalled = sigaction(SIGABRT, &act, &oact[2]); 458 459 // prepare against a signal during FcInit or FcConfigGetCurrent 460 if( sigsetjmp( aViolationBuffer, ~0 ) == 0 ) 461 { 462 nDests = m_pCUPSWrapper->cupsGetDests( &pDests ); 463 #if OSL_DEBUG_LEVEL > 1 464 fprintf( stderr, "came out of cupsGetDests\n" ); 465 #endif 466 467 osl::MutexGuard aGuard( m_aCUPSMutex ); 468 m_nDests = nDests; 469 m_pDests = pDests; 470 m_bNewDests = true; 471 #if OSL_DEBUG_LEVEL > 1 472 fprintf( stderr, "finished cupsGetDests\n" ); 473 #endif 474 } 475 else 476 { 477 #if OSL_DEBUG_LEVEL > 1 478 fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" ); 479 #endif 480 } 481 482 // restore old signal handlers 483 if( nSegvSignalInstalled == 0 ) 484 sigaction( SIGSEGV, &oact[0], NULL ); 485 if( nBusSignalInstalled == 0 ) 486 sigaction( SIGBUS, &oact[1], NULL ); 487 if( nAbortSignalInstalled == 0 ) 488 sigaction( SIGABRT, &oact[2], NULL ); 489 } 490 491 void CUPSManager::initialize() 492 { 493 // get normal printers, clear printer list 494 PrinterInfoManager::initialize(); 495 496 #ifdef ENABLE_CUPS 497 // check whether thread has completed 498 // if not behave like old printing system 499 osl::MutexGuard aGuard( m_aCUPSMutex ); 500 501 if( ! m_bNewDests ) 502 return; 503 504 // dest thread has run, clean up 505 if( m_aDestThread ) 506 { 507 osl_joinWithThread( m_aDestThread ); 508 osl_destroyThread( m_aDestThread ); 509 m_aDestThread = NULL; 510 } 511 m_bNewDests = false; 512 513 // clear old stuff 514 m_aCUPSDestMap.clear(); 515 516 if( ! (m_nDests && m_pDests ) ) 517 return; 518 519 if( isCUPSDisabled() ) 520 return; 521 522 // check for CUPS server(?) > 1.2 523 // since there is no API to query, check for options that were 524 // introduced in dests with 1.2 525 // this is needed to check for %%IncludeFeature support 526 // (#i65684#, #i65491#) 527 bool bUsePDF = false; 528 cups_dest_t* pDest = ((cups_dest_t*)m_pDests); 529 const char* pOpt = m_pCUPSWrapper->cupsGetOption( "printer-info", 530 pDest->num_options, 531 pDest->options ); 532 if( pOpt ) 533 { 534 m_bUseIncludeFeature = true; 535 bUsePDF = true; 536 if( m_aGlobalDefaults.m_nPSLevel == 0 && m_aGlobalDefaults.m_nPDFDevice == 0 ) 537 m_aGlobalDefaults.m_nPDFDevice = 1; 538 } 539 // do not send include JobPatch; CUPS will insert that itself 540 // TODO: currently unknwon which versions of CUPS insert JobPatches 541 // so currently it is assumed CUPS = don't insert JobPatch files 542 m_bUseJobPatch = false; 543 544 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 545 int nPrinter = m_nDests; 546 547 // reset global default PPD options; these are queried on demand from CUPS 548 m_aGlobalDefaults.m_pParser = NULL; 549 m_aGlobalDefaults.m_aContext = PPDContext(); 550 551 // add CUPS printers, should there be a printer 552 // with the same name as a CUPS printer, overwrite it 553 while( nPrinter-- ) 554 { 555 pDest = ((cups_dest_t*)m_pDests)+nPrinter; 556 OUString aPrinterName = OStringToOUString( pDest->name, aEncoding ); 557 if( pDest->instance && *pDest->instance ) 558 { 559 OUStringBuffer aBuf( 256 ); 560 aBuf.append( aPrinterName ); 561 aBuf.append( sal_Unicode( '/' ) ); 562 aBuf.append( OStringToOUString( pDest->instance, aEncoding ) ); 563 aPrinterName = aBuf.makeStringAndClear(); 564 } 565 566 // initialize printer with possible configuration from psprint.conf 567 bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end(); 568 Printer aPrinter = m_aPrinters[ aPrinterName ]; 569 if( bSetToGlobalDefaults ) 570 aPrinter.m_aInfo = m_aGlobalDefaults; 571 aPrinter.m_aInfo.m_aPrinterName = aPrinterName; 572 if( pDest->is_default ) 573 m_aDefaultPrinter = aPrinterName; 574 575 for( int k = 0; k < pDest->num_options; k++ ) 576 { 577 if(!strcmp(pDest->options[k].name, "printer-info")) 578 aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding); 579 if(!strcmp(pDest->options[k].name, "printer-location")) 580 aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding); 581 } 582 583 584 OUStringBuffer aBuf( 256 ); 585 aBuf.appendAscii( "CUPS:" ); 586 aBuf.append( aPrinterName ); 587 // note: the parser that goes with the PrinterInfo 588 // is created implicitly by the JobData::operator=() 589 // when it detects the NULL ptr m_pParser. 590 // if we wanted to fill in the parser here this 591 // would mean we'd have to download PPDs for each and 592 // every printer - which would be really bad runtime 593 // behaviour 594 aPrinter.m_aInfo.m_pParser = NULL; 595 aPrinter.m_aInfo.m_aContext.setParser( NULL ); 596 std::hash_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName ); 597 if( c_it != m_aDefaultContexts.end() ) 598 { 599 aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); 600 aPrinter.m_aInfo.m_aContext = c_it->second; 601 } 602 if( bUsePDF && aPrinter.m_aInfo.m_nPSLevel == 0 && aPrinter.m_aInfo.m_nPDFDevice == 0 ) 603 aPrinter.m_aInfo.m_nPDFDevice = 1; 604 aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear(); 605 aPrinter.m_bModified = false; 606 607 m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter; 608 m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter; 609 } 610 611 // remove everything that is not a CUPS printer and not 612 // a special purpose printer (PDF, Fax) 613 std::list< OUString > aRemovePrinters; 614 for( std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin(); 615 it != m_aPrinters.end(); ++it ) 616 { 617 if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() ) 618 continue; 619 620 if( it->second.m_aInfo.m_aFeatures.getLength() > 0 ) 621 continue; 622 aRemovePrinters.push_back( it->first ); 623 } 624 while( aRemovePrinters.begin() != aRemovePrinters.end() ) 625 { 626 m_aPrinters.erase( aRemovePrinters.front() ); 627 aRemovePrinters.pop_front(); 628 } 629 630 m_pCUPSWrapper->cupsSetPasswordCB( setPasswordCallback ); 631 #endif // ENABLE_CUPS 632 } 633 634 #ifdef ENABLE_CUPS 635 static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext ) 636 { 637 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 638 for( int i = 0; i < pPPDGroup->num_options; i++ ) 639 { 640 ppd_option_t* pOption = pPPDGroup->options + i; 641 for( int n = 0; n < pOption->num_choices; n++ ) 642 { 643 ppd_choice_t* pChoice = pOption->choices + n; 644 if( pChoice->marked ) 645 { 646 const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) ); 647 if( pKey ) 648 { 649 const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) ); 650 if( pValue ) 651 { 652 if( pValue != pKey->getDefaultValue() ) 653 { 654 rContext.setValue( pKey, pValue, true ); 655 #if OSL_DEBUG_LEVEL > 1 656 fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice ); 657 #endif 658 659 } 660 #if OSL_DEBUG_LEVEL > 1 661 else 662 fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice ); 663 #endif 664 } 665 #if OSL_DEBUG_LEVEL > 1 666 else 667 fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword ); 668 #endif 669 } 670 #if OSL_DEBUG_LEVEL > 1 671 else 672 fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword ); 673 #endif 674 } 675 } 676 } 677 678 // recurse through subgroups 679 for( int g = 0; g < pPPDGroup->num_subgroups; g++ ) 680 { 681 updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext ); 682 } 683 } 684 #endif // ENABLE_CUPS 685 686 const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter ) 687 { 688 const PPDParser* pNewParser = NULL; 689 OUString aPrinter; 690 691 if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 ) 692 aPrinter = rPrinter.copy( 5 ); 693 else 694 aPrinter = rPrinter; 695 696 #ifdef ENABLE_CUPS 697 if( m_aCUPSMutex.tryToAcquire() ) 698 { 699 if( m_nDests && m_pDests && ! isCUPSDisabled() ) 700 { 701 std::hash_map< OUString, int, OUStringHash >::iterator dest_it = 702 m_aCUPSDestMap.find( aPrinter ); 703 if( dest_it != m_aCUPSDestMap.end() ) 704 { 705 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; 706 OString aPPDFile = m_pCUPSWrapper->cupsGetPPD( pDest->name ); 707 #if OSL_DEBUG_LEVEL > 1 708 fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() ); 709 #endif 710 if( aPPDFile.getLength() ) 711 { 712 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 713 OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) ); 714 // update the printer info with context information 715 ppd_file_t* pPPD = m_pCUPSWrapper->ppdOpenFile( aPPDFile.getStr() ); 716 if( pPPD ) 717 { 718 // create the new parser 719 PPDParser* pCUPSParser = new PPDParser( aFileName ); 720 pCUPSParser->m_aFile = rPrinter; 721 pNewParser = pCUPSParser; 722 723 /*int nConflicts =*/ m_pCUPSWrapper->cupsMarkOptions( pPPD, pDest->num_options, pDest->options ); 724 #if OSL_DEBUG_LEVEL > 1 725 fprintf( stderr, "processing the following options for printer %s (instance %s):\n", 726 pDest->name, pDest->instance ); 727 for( int k = 0; k < pDest->num_options; k++ ) 728 fprintf( stderr, " \"%s\" = \"%s\"\n", 729 pDest->options[k].name, 730 pDest->options[k].value ); 731 #endif 732 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; 733 734 // remember the default context for later use 735 PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; 736 rContext.setParser( pNewParser ); 737 // set system default paper; printer CUPS PPD options 738 // may overwrite it 739 setDefaultPaper( rContext ); 740 for( int i = 0; i < pPPD->num_groups; i++ ) 741 updatePrinterContextInfo( pPPD->groups + i, rContext ); 742 743 rInfo.m_pParser = pNewParser; 744 rInfo.m_aContext = rContext; 745 746 // clean up the mess 747 m_pCUPSWrapper->ppdClose( pPPD ); 748 } 749 #if OSL_DEBUG_LEVEL > 1 750 else 751 fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" ); 752 #endif 753 754 // remove temporary PPD file 755 unlink( aPPDFile.getStr() ); 756 } 757 #if OSL_DEBUG_LEVEL > 1 758 else 759 fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" ); 760 #endif 761 } 762 #if OSL_DEBUG_LEVEL > 1 763 else 764 fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() ); 765 #endif 766 } 767 m_aCUPSMutex.release(); 768 } 769 #if OSL_DEBUG_LEVEL >1 770 else 771 fprintf( stderr, "could not acquire CUPS mutex !!!\n" ); 772 #endif 773 #endif // ENABLE_CUPS 774 775 if( ! pNewParser ) 776 { 777 // get the default PPD 778 pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); 779 780 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; 781 782 rInfo.m_pParser = pNewParser; 783 rInfo.m_aContext.setParser( pNewParser ); 784 } 785 786 return pNewParser; 787 } 788 789 void CUPSManager::setupJobContextData( 790 JobData& 791 #ifdef ENABLE_CUPS 792 rData 793 #endif 794 ) 795 { 796 #ifdef ENABLE_CUPS 797 std::hash_map< OUString, int, OUStringHash >::iterator dest_it = 798 m_aCUPSDestMap.find( rData.m_aPrinterName ); 799 800 if( dest_it == m_aCUPSDestMap.end() ) 801 return PrinterInfoManager::setupJobContextData( rData ); 802 803 std::hash_map< OUString, Printer, OUStringHash >::iterator p_it = 804 m_aPrinters.find( rData.m_aPrinterName ); 805 if( p_it == m_aPrinters.end() ) // huh ? 806 { 807 #if OSL_DEBUG_LEVEL > 1 808 fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() ); 809 #endif 810 return; 811 } 812 813 if( p_it->second.m_aInfo.m_pParser == NULL ) 814 { 815 // in turn calls createCUPSParser 816 // which updates the printer info 817 p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); 818 } 819 if( p_it->second.m_aInfo.m_aContext.getParser() == NULL ) 820 { 821 OUString aPrinter; 822 if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 ) 823 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 ); 824 else 825 aPrinter = p_it->second.m_aInfo.m_aDriverName; 826 827 p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; 828 } 829 830 rData.m_pParser = p_it->second.m_aInfo.m_pParser; 831 rData.m_aContext = p_it->second.m_aInfo.m_aContext; 832 #endif 833 } 834 835 FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) 836 { 837 OSL_TRACE( "endSpool: %s, %s", 838 rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), 839 bQuickCommand ? "true" : "false" ); 840 841 if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() ) 842 { 843 OSL_TRACE( "defer to PrinterInfoManager::startSpool" ); 844 return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); 845 } 846 847 #ifdef ENABLE_CUPS 848 OUString aTmpURL, aTmpFile; 849 osl_createTempFile( NULL, NULL, &aTmpURL.pData ); 850 osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); 851 OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); 852 FILE* fp = fopen( aSysFile.getStr(), "w" ); 853 if( fp ) 854 m_aSpoolFiles[fp] = aSysFile; 855 856 return fp; 857 #else 858 return NULL; 859 #endif 860 } 861 862 struct less_ppd_key : public ::std::binary_function<double, double, bool> 863 { 864 bool operator()(const PPDKey* left, const PPDKey* right) 865 { return left->getOrderDependency() < right->getOrderDependency(); } 866 }; 867 868 void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const 869 { 870 rNumOptions = 0; 871 *rOptions = NULL; 872 int i; 873 874 // emit features ordered to OrderDependency 875 // ignore features that are set to default 876 877 // sanity check 878 if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) 879 { 880 int nKeys = rJob.m_aContext.countValuesModified(); 881 ::std::vector< const PPDKey* > aKeys( nKeys ); 882 for( i = 0; i < nKeys; i++ ) 883 aKeys[i] = rJob.m_aContext.getModifiedKey( i ); 884 ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); 885 886 for( i = 0; i < nKeys; i++ ) 887 { 888 const PPDKey* pKey = aKeys[i]; 889 const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); 890 if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() ) 891 { 892 OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); 893 OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ); 894 rNumOptions = m_pCUPSWrapper->cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions ); 895 } 896 } 897 } 898 899 if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 ) 900 { 901 rtl::OString aVal( rtl::OString::valueOf( sal_Int32( rJob.m_nCopies ) ) ); 902 rNumOptions = m_pCUPSWrapper->cupsAddOption( "copies", aVal.getStr(), rNumOptions, (cups_option_t**)rOptions ); 903 } 904 if( ! bBanner ) 905 { 906 rNumOptions = m_pCUPSWrapper->cupsAddOption( "job-sheets", "none", rNumOptions, (cups_option_t**)rOptions ); 907 } 908 } 909 910 int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner ) 911 { 912 OSL_TRACE( "endSpool: %s, %s, copy count = %d", 913 rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), 914 rtl::OUStringToOString( rJobTitle, RTL_TEXTENCODING_UTF8 ).getStr(), 915 rDocumentJobData.m_nCopies 916 ); 917 918 int nJobID = 0; 919 920 osl::MutexGuard aGuard( m_aCUPSMutex ); 921 922 std::hash_map< OUString, int, OUStringHash >::iterator dest_it = 923 m_aCUPSDestMap.find( rPrintername ); 924 if( dest_it == m_aCUPSDestMap.end() ) 925 { 926 OSL_TRACE( "defer to PrinterInfoManager::endSpool" ); 927 return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner ); 928 } 929 930 #ifdef ENABLE_CUPS 931 std::hash_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); 932 if( it != m_aSpoolFiles.end() ) 933 { 934 fclose( pFile ); 935 rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); 936 937 // setup cups options 938 int nNumOptions = 0; 939 cups_option_t* pOptions = NULL; 940 getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions ); 941 942 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; 943 nJobID = m_pCUPSWrapper->cupsPrintFile( pDest->name, 944 it->second.getStr(), 945 OUStringToOString( rJobTitle, aEnc ).getStr(), 946 nNumOptions, pOptions ); 947 #if OSL_DEBUG_LEVEL > 1 948 fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n", 949 pDest->name, 950 it->second.getStr(), 951 OUStringToOString( rJobTitle, aEnc ).getStr(), 952 nNumOptions, 953 pOptions, 954 nJobID 955 ); 956 for( int n = 0; n < nNumOptions; n++ ) 957 fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value ); 958 OString aCmd( "cp " ); 959 aCmd = aCmd + it->second; 960 aCmd = aCmd + OString( " $HOME/cupsprint.ps" ); 961 system( aCmd.getStr() ); 962 #endif 963 964 unlink( it->second.getStr() ); 965 m_aSpoolFiles.erase( pFile ); 966 if( pOptions ) 967 m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions ); 968 } 969 #endif // ENABLE_CUPS 970 971 return nJobID; 972 } 973 974 975 void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) 976 { 977 PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo ); 978 } 979 980 bool CUPSManager::checkPrintersChanged( bool bWait ) 981 { 982 bool bChanged = false; 983 if( bWait ) 984 { 985 if( m_aDestThread ) 986 { 987 // initial asynchronous detection still running 988 #if OSL_DEBUG_LEVEL > 1 989 fprintf( stderr, "syncing cups discovery thread\n" ); 990 #endif 991 osl_joinWithThread( m_aDestThread ); 992 osl_destroyThread( m_aDestThread ); 993 m_aDestThread = NULL; 994 #if OSL_DEBUG_LEVEL > 1 995 fprintf( stderr, "done: syncing cups discovery thread\n" ); 996 #endif 997 } 998 else 999 { 1000 // #i82321# check for cups printer updates 1001 // with this change the whole asynchronous detection in a thread is 1002 // almost useless. The only relevance left is for some stalled systems 1003 // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION 1004 // (see vcl/unx/source/gdi/salprnpsp.cxx) 1005 // so that checkPrintersChanged( true ) will never be called 1006 1007 // there is no way to query CUPS whether the printer list has changed 1008 // so get the dest list anew 1009 if( m_nDests && m_pDests ) 1010 m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); 1011 m_nDests = 0; 1012 m_pDests = NULL; 1013 runDests(); 1014 } 1015 } 1016 if( m_aCUPSMutex.tryToAcquire() ) 1017 { 1018 bChanged = m_bNewDests; 1019 m_aCUPSMutex.release(); 1020 } 1021 1022 if( ! bChanged ) 1023 { 1024 bChanged = PrinterInfoManager::checkPrintersChanged( bWait ); 1025 // #i54375# ensure new merging with CUPS list in :initialize 1026 if( bChanged ) 1027 m_bNewDests = true; 1028 } 1029 1030 if( bChanged ) 1031 initialize(); 1032 1033 return bChanged; 1034 } 1035 1036 bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver ) 1037 { 1038 // don't touch the CUPS printers 1039 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() || 1040 rDriver.compareToAscii( "CUPS:", 5 ) == 0 1041 ) 1042 return false; 1043 return PrinterInfoManager::addPrinter( rName, rDriver ); 1044 } 1045 1046 bool CUPSManager::removePrinter( const OUString& rName, bool bCheck ) 1047 { 1048 // don't touch the CUPS printers 1049 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ) 1050 return false; 1051 return PrinterInfoManager::removePrinter( rName, bCheck ); 1052 } 1053 1054 bool CUPSManager::setDefaultPrinter( const OUString& rName ) 1055 { 1056 bool bSuccess = false; 1057 #ifdef ENABLE_CUPS 1058 std::hash_map< OUString, int, OUStringHash >::iterator nit = 1059 m_aCUPSDestMap.find( rName ); 1060 if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() ) 1061 { 1062 cups_dest_t* pDests = (cups_dest_t*)m_pDests; 1063 for( int i = 0; i < m_nDests; i++ ) 1064 pDests[i].is_default = 0; 1065 pDests[ nit->second ].is_default = 1; 1066 m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); 1067 m_aDefaultPrinter = rName; 1068 m_aCUPSMutex.release(); 1069 bSuccess = true; 1070 } 1071 else 1072 #endif 1073 bSuccess = PrinterInfoManager::setDefaultPrinter( rName ); 1074 1075 return bSuccess; 1076 } 1077 1078 bool CUPSManager::writePrinterConfig() 1079 { 1080 #ifdef ENABLE_CUPS 1081 bool bDestModified = false; 1082 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 1083 1084 for( std::hash_map< OUString, Printer, OUStringHash >::iterator prt = 1085 m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt ) 1086 { 1087 std::hash_map< OUString, int, OUStringHash >::iterator nit = 1088 m_aCUPSDestMap.find( prt->first ); 1089 if( nit == m_aCUPSDestMap.end() ) 1090 continue; 1091 1092 if( ! prt->second.m_bModified ) 1093 continue; 1094 1095 if( m_aCUPSMutex.tryToAcquire() ) 1096 { 1097 bDestModified = true; 1098 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second; 1099 PrinterInfo& rInfo = prt->second.m_aInfo; 1100 1101 // create new option list 1102 int nNewOptions = 0; 1103 cups_option_t* pNewOptions = NULL; 1104 int nValues = rInfo.m_aContext.countValuesModified(); 1105 for( int i = 0; i < nValues; i++ ) 1106 { 1107 const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i ); 1108 const PPDValue* pValue = rInfo.m_aContext.getValue( pKey ); 1109 if( pKey && pValue ) // sanity check 1110 { 1111 OString aName = OUStringToOString( pKey->getKey(), aEncoding ); 1112 OString aValue = OUStringToOString( pValue->m_aOption, aEncoding ); 1113 nNewOptions = m_pCUPSWrapper->cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions ); 1114 } 1115 } 1116 // set PPD options on CUPS dest 1117 m_pCUPSWrapper->cupsFreeOptions( pDest->num_options, pDest->options ); 1118 pDest->num_options = nNewOptions; 1119 pDest->options = pNewOptions; 1120 m_aCUPSMutex.release(); 1121 } 1122 } 1123 if( bDestModified && m_aCUPSMutex.tryToAcquire() ) 1124 { 1125 m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); 1126 m_aCUPSMutex.release(); 1127 } 1128 #endif // ENABLE_CUPS 1129 1130 return PrinterInfoManager::writePrinterConfig(); 1131 } 1132 1133 bool CUPSManager::addOrRemovePossible() const 1134 { 1135 return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible(); 1136 } 1137 1138 const char* CUPSManager::authenticateUser( const char* /*pIn*/ ) 1139 { 1140 const char* pRet = NULL; 1141 1142 #ifdef ENABLE_CUPS 1143 OUString aLib = OUString::createFromAscii( _XSALSET_LIBNAME ); 1144 oslModule pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); 1145 if( pLib ) 1146 { 1147 OUString aSym( RTL_CONSTASCII_USTRINGPARAM( "Sal_authenticateQuery" ) ); 1148 bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) = 1149 (bool(*)(const OString&,OString&,OString&))osl_getFunctionSymbol( pLib, aSym.pData ); 1150 if( getpw ) 1151 { 1152 osl::MutexGuard aGuard( m_aCUPSMutex ); 1153 1154 OString aUser = m_pCUPSWrapper->cupsUser(); 1155 OString aServer = m_pCUPSWrapper->cupsServer(); 1156 OString aPassword; 1157 if( getpw( aServer, aUser, aPassword ) ) 1158 { 1159 m_aPassword = aPassword; 1160 m_aUser = aUser; 1161 m_pCUPSWrapper->cupsSetUser( m_aUser.getStr() ); 1162 pRet = m_aPassword.getStr(); 1163 } 1164 } 1165 osl_unloadModule( pLib ); 1166 } 1167 #if OSL_DEBUG_LEVEL > 1 1168 else fprintf( stderr, "loading of module %s failed\n", OUStringToOString( aLib, osl_getThreadTextEncoding() ).getStr() ); 1169 #endif 1170 #endif // ENABLE_CUPS 1171 1172 return pRet; 1173 } 1174