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