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