sync master with lastest vba changes
[ooovba.git] / psprint / source / printer / cupsmgr.cxx
blobcf92931bfee7ceb7fd8e207d672a5141808aba85
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cupsmgr.cxx,v $
10 * $Revision: 1.28 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_psprint.hxx"
34 #ifdef ENABLE_CUPS
35 #include <cups/cups.h>
36 #include <cups/ppd.h>
38 #else // !ENABLE_CUPS
39 typedef void ppd_file_t;
40 typedef void cups_dest_t;
41 typedef void cups_option_t;
42 #endif
44 #include <unistd.h>
46 #include <osl/thread.h>
47 #include <osl/diagnose.h>
48 #include <osl/conditn.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <cupsmgr.hxx>
52 #include <algorithm>
54 #include <setjmp.h>
55 #include <signal.h>
57 // FIXME: SAL_MODULENAME_WITH_VERSION needs to be fixed on OS X
58 #ifdef MACOSX
59 #define CUPS_LIB_NAME "libcups.2.dylib"
60 #else
61 #define CUPS_LIB_NAME "libcups.so.2"
62 #endif
64 namespace psp
66 class CUPSWrapper
68 oslModule m_pLib;
69 osl::Mutex m_aGetPPDMutex;
70 bool m_bPPDThreadRunning;
72 int (*m_pcupsPrintFile)(const char*, const char*, const char*, int, cups_option_t*);
73 int (*m_pcupsGetDests)(cups_dest_t**);
74 void (*m_pcupsSetDests)(int,cups_dest_t*);
75 void (*m_pcupsFreeDests)(int,cups_dest_t*);
76 const char* (*m_pcupsGetPPD)(const char*);
77 int (*m_pcupsMarkOptions)(ppd_file_t*,int,cups_option_t*);
78 int (*m_pcupsAddOption)(const char*,const char*,int,cups_option_t**);
79 void (*m_pcupsFreeOptions)(int,cups_option_t*);
80 ppd_file_t* (*m_pppdOpenFile)(const char* pFile);
81 void (*m_pppdClose)(ppd_file_t*);
82 const char* (*m_pcupsServer)();
83 void (*m_pcupsSetPasswordCB)(const char*(cb)(const char*));
84 const char* (*m_pcupsUser)();
85 void (*m_pcupsSetUser)(const char*);
86 const char* (*m_pcupsGetOption)(const char*,int,cups_option_t*);
88 oslGenericFunction loadSymbol( const char* );
89 public:
90 CUPSWrapper();
91 ~CUPSWrapper();
93 bool isValid();
95 int cupsGetDests(cups_dest_t** pDests)
96 { return m_pcupsGetDests(pDests); }
98 void cupsSetDests( int nDests, cups_dest_t* pDests )
99 { m_pcupsSetDests( nDests, pDests ); }
101 void cupsFreeDests(int nDests, cups_dest_t* pDests)
102 { m_pcupsFreeDests(nDests, pDests); }
104 int cupsPrintFile( const char* pPrinter,
105 const char* pFileName,
106 const char* pTitle,
107 int nOptions,
108 cups_option_t* pOptions )
109 { return m_pcupsPrintFile( pPrinter, pFileName, pTitle, nOptions, pOptions ); }
111 rtl::OString cupsGetPPD( const char* pPrinter );
113 int cupsMarkOptions(ppd_file_t* pPPD, int nOptions, cups_option_t* pOptions )
114 { return m_pcupsMarkOptions(pPPD, nOptions, pOptions); }
116 int cupsAddOption( const char* pName, const char* pValue, int nOptions, cups_option_t** pOptions )
117 { return m_pcupsAddOption( pName, pValue, nOptions, pOptions ); }
119 void cupsFreeOptions( int nOptions, cups_option_t* pOptions )
120 { m_pcupsFreeOptions( nOptions, pOptions ); }
122 ppd_file_t* ppdOpenFile( const char* pFileName )
123 { return m_pppdOpenFile( pFileName ); }
125 void ppdClose( ppd_file_t* pPPD )
126 { m_pppdClose( pPPD ); }
128 const char *cupsServer(void)
129 { return m_pcupsServer(); }
131 const char *cupsUser(void)
132 { return m_pcupsUser(); }
134 void cupsSetPasswordCB(const char *(*cb)(const char *))
135 { m_pcupsSetPasswordCB( cb ); }
137 void cupsSetUser(const char *user)
138 { m_pcupsSetUser( user ); }
140 const char* cupsGetOption(const char* name, int num_options, cups_option_t* options)
141 { return m_pcupsGetOption( name, num_options, options ); }
146 using namespace psp;
147 using namespace osl;
148 using namespace rtl;
151 * CUPSWrapper class
154 oslGenericFunction CUPSWrapper::loadSymbol( const char* pSymbol )
156 OUString aSym( OUString::createFromAscii( pSymbol ) );
157 oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData );
158 #if OSL_DEBUG_LEVEL > 1
159 fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" );
160 #endif
161 return pSym;
164 CUPSWrapper::CUPSWrapper()
165 : m_pLib( NULL ),
166 m_bPPDThreadRunning( false )
168 #ifdef ENABLE_CUPS
169 OUString aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME ) );
170 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
171 if( ! m_pLib )
173 aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) );
174 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
176 #endif
178 if( ! m_pLib )
180 #if OSL_DEBUG_LEVEL > 1
181 fprintf( stderr, "no cups library found\n" );
182 #endif
183 return;
186 m_pcupsPrintFile = (int(*)(const char*,const char*,const char*,int,cups_option_t*))
187 loadSymbol( "cupsPrintFile" );
188 m_pcupsGetDests = (int(*)(cups_dest_t**))
189 loadSymbol( "cupsGetDests" );
190 m_pcupsSetDests = (void(*)(int,cups_dest_t*))
191 loadSymbol( "cupsSetDests" );
192 m_pcupsFreeDests = (void(*)(int,cups_dest_t*))
193 loadSymbol( "cupsFreeDests" );
194 m_pcupsGetPPD = (const char*(*)(const char*))
195 loadSymbol( "cupsGetPPD" );
196 m_pcupsMarkOptions = (int(*)(ppd_file_t*,int,cups_option_t*))
197 loadSymbol( "cupsMarkOptions" );
198 m_pcupsAddOption = (int(*)(const char*,const char*,int,cups_option_t**))
199 loadSymbol( "cupsAddOption" );
200 m_pcupsFreeOptions = (void(*)(int,cups_option_t*))
201 loadSymbol( "cupsFreeOptions" );
202 m_pppdOpenFile = (ppd_file_t*(*)(const char*))
203 loadSymbol( "ppdOpenFile" );
204 m_pppdClose = (void(*)(ppd_file_t*))
205 loadSymbol( "ppdClose" );
206 m_pcupsServer = (const char*(*)())
207 loadSymbol( "cupsServer" );
208 m_pcupsUser = (const char*(*)())
209 loadSymbol( "cupsUser" );
210 m_pcupsSetPasswordCB = (void(*)(const char*(*)(const char*)))
211 loadSymbol( "cupsSetPasswordCB" );
212 m_pcupsSetUser = (void(*)(const char*))
213 loadSymbol( "cupsSetUser" );
214 m_pcupsGetOption = (const char*(*)(const char*,int,cups_option_t*))
215 loadSymbol( "cupsGetOption" );
217 if( ! (
218 m_pcupsPrintFile &&
219 m_pcupsGetDests &&
220 m_pcupsSetDests &&
221 m_pcupsFreeDests &&
222 m_pcupsGetPPD &&
223 m_pcupsMarkOptions &&
224 m_pcupsAddOption &&
225 m_pcupsServer &&
226 m_pcupsUser &&
227 m_pcupsSetPasswordCB &&
228 m_pcupsSetUser &&
229 m_pcupsFreeOptions &&
230 m_pppdOpenFile &&
231 m_pppdClose &&
232 m_pcupsGetOption
235 osl_unloadModule( m_pLib );
236 m_pLib = NULL;
240 CUPSWrapper::~CUPSWrapper()
242 if( m_pLib )
243 osl_unloadModule( m_pLib );
246 bool CUPSWrapper::isValid()
248 return m_pLib != NULL;
251 typedef const char*(*PPDFunction)(const char*);
252 struct GetPPDAttribs
254 PPDFunction m_pFunction;
255 osl::Condition m_aCondition;
256 OString m_aParameter;
257 OString m_aResult;
258 oslThread m_aThread;
259 int m_nRefs;
260 bool* m_pResetRunning;
261 osl::Mutex* m_pSyncMutex;
263 GetPPDAttribs( PPDFunction pFn, const char * m_pParameter,
264 bool* pResetRunning, osl::Mutex* pSyncMutex )
265 : m_pFunction( pFn ),
266 m_aParameter( m_pParameter ),
267 m_pResetRunning( pResetRunning ),
268 m_pSyncMutex( pSyncMutex )
270 m_nRefs = 2;
271 m_aCondition.reset();
274 ~GetPPDAttribs()
276 if( m_aResult.getLength() )
277 unlink( m_aResult.getStr() );
280 void unref()
282 if( --m_nRefs == 0 )
284 *m_pResetRunning = false;
285 delete this;
289 void executeCall()
291 // This CUPS method is not at all thread-safe we need
292 // to dup the pointer to a static buffer it returns ASAP
293 OString aResult = m_pFunction( m_aParameter );
294 MutexGuard aGuard( *m_pSyncMutex );
295 m_aResult = aResult;
296 m_aCondition.set();
297 unref();
300 OString waitResult( TimeValue *pDelay )
302 m_pSyncMutex->release();
304 if (m_aCondition.wait( pDelay ) != Condition::result_ok
307 #if OSL_DEBUG_LEVEL > 1
308 fprintf( stderr, "cupsGetPPD %s timed out\n",
309 (const sal_Char *) m_aParameter
311 #endif
313 m_pSyncMutex->acquire();
315 OString aRetval = m_aResult;
316 m_aResult = OString();
317 unref();
319 return aRetval;
323 extern "C" {
324 static void getPPDWorker(void* pData)
326 GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData;
327 pAttribs->executeCall();
331 OString CUPSWrapper::cupsGetPPD( const char* pPrinter )
333 OString aResult;
335 m_aGetPPDMutex.acquire();
336 // if one thread hangs in cupsGetPPD already, don't start another
337 if( ! m_bPPDThreadRunning )
339 m_bPPDThreadRunning = true;
340 GetPPDAttribs* pAttribs = new GetPPDAttribs( m_pcupsGetPPD,
341 pPrinter,
342 &m_bPPDThreadRunning,
343 &m_aGetPPDMutex );
345 oslThread aThread = osl_createThread( getPPDWorker, pAttribs );
347 TimeValue aValue;
348 aValue.Seconds = 5;
349 aValue.Nanosec = 0;
351 // NOTE: waitResult release and acquires the GetPPD mutex
352 aResult = pAttribs->waitResult( &aValue );
353 osl_destroyThread( aThread );
355 m_aGetPPDMutex.release();
357 return aResult;
360 #ifdef ENABLE_CUPS
361 static const char* setPasswordCallback( const char* pIn )
363 const char* pRet = NULL;
365 PrinterInfoManager& rMgr = PrinterInfoManager::get();
366 if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check
367 pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn );
368 return pRet;
370 #endif
373 * CUPSManager class
376 CUPSManager* CUPSManager::tryLoadCUPS()
378 CUPSManager* pManager = NULL;
379 #ifdef ENABLE_CUPS
380 static const char* pEnv = getenv( "SAL_DISABLE_CUPS" );
382 if( ! pEnv || ! *pEnv )
384 // try to load CUPS
385 CUPSWrapper* pWrapper = new CUPSWrapper();
386 if( pWrapper->isValid() )
387 pManager = new CUPSManager( pWrapper );
388 else
389 delete pWrapper;
391 #endif
392 return pManager;
395 extern "C"
397 static void run_dest_thread_stub( void* pThis )
399 CUPSManager::runDestThread( pThis );
403 CUPSManager::CUPSManager( CUPSWrapper* pWrapper ) :
404 PrinterInfoManager( CUPS ),
405 m_pCUPSWrapper( pWrapper ),
406 m_nDests( 0 ),
407 m_pDests( NULL ),
408 m_bNewDests( false )
410 m_aDestThread = osl_createThread( run_dest_thread_stub, this );
413 CUPSManager::~CUPSManager()
415 if( m_aDestThread )
417 // if the thread is still running here, then
418 // cupsGetDests is hung; terminate the thread instead of joining
419 osl_terminateThread( m_aDestThread );
420 osl_destroyThread( m_aDestThread );
423 if( m_nDests && m_pDests )
424 m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests );
425 delete m_pCUPSWrapper;
428 void CUPSManager::runDestThread( void* pThis )
430 ((CUPSManager*)pThis)->runDests();
433 static sigjmp_buf aViolationBuffer;
435 extern "C"
437 static void lcl_signal_action(int nSignal)
439 fprintf( stderr, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal );
440 siglongjmp( aViolationBuffer, 1 );
444 void CUPSManager::runDests()
446 #if OSL_DEBUG_LEVEL > 1
447 fprintf( stderr, "starting cupsGetDests\n" );
448 #endif
449 int nDests = 0;
450 cups_dest_t* pDests = NULL;
452 // #i86306# prepare against really broken CUPS installations / missing servers
454 // install signal handler for SEGV, BUS and ABRT
455 struct sigaction act;
456 struct sigaction oact[3];
458 act.sa_handler = lcl_signal_action;
459 act.sa_flags = 0;
460 sigemptyset(&(act.sa_mask));
462 int nSegvSignalInstalled = sigaction(SIGSEGV, &act, &oact[0]);
463 int nBusSignalInstalled = sigaction(SIGBUS, &act, &oact[1]);
464 int nAbortSignalInstalled = sigaction(SIGABRT, &act, &oact[2]);
466 // prepare against a signal during FcInit or FcConfigGetCurrent
467 if( sigsetjmp( aViolationBuffer, ~0 ) == 0 )
469 nDests = m_pCUPSWrapper->cupsGetDests( &pDests );
470 #if OSL_DEBUG_LEVEL > 1
471 fprintf( stderr, "came out of cupsGetDests\n" );
472 #endif
474 osl::MutexGuard aGuard( m_aCUPSMutex );
475 m_nDests = nDests;
476 m_pDests = pDests;
477 m_bNewDests = true;
478 #if OSL_DEBUG_LEVEL > 1
479 fprintf( stderr, "finished cupsGetDests\n" );
480 #endif
482 else
484 #if OSL_DEBUG_LEVEL > 1
485 fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" );
486 #endif
489 // restore old signal handlers
490 if( nSegvSignalInstalled == 0 )
491 sigaction( SIGSEGV, &oact[0], NULL );
492 if( nBusSignalInstalled == 0 )
493 sigaction( SIGBUS, &oact[1], NULL );
494 if( nAbortSignalInstalled == 0 )
495 sigaction( SIGABRT, &oact[2], NULL );
498 void CUPSManager::initialize()
500 // get normal printers, clear printer list
501 PrinterInfoManager::initialize();
503 #ifdef ENABLE_CUPS
504 // check whether thread has completed
505 // if not behave like old printing system
506 osl::MutexGuard aGuard( m_aCUPSMutex );
508 if( ! m_bNewDests )
509 return;
511 // dest thread has run, clean up
512 if( m_aDestThread )
514 osl_joinWithThread( m_aDestThread );
515 osl_destroyThread( m_aDestThread );
516 m_aDestThread = NULL;
518 m_bNewDests = false;
520 // clear old stuff
521 m_aCUPSDestMap.clear();
523 if( ! (m_nDests && m_pDests ) )
524 return;
526 if( isCUPSDisabled() )
527 return;
529 // check for CUPS server(?) > 1.2
530 // since there is no API to query, check for options that were
531 // introduced in dests with 1.2
532 // this is needed to check for %%IncludeFeature support
533 // (#i65684#, #i65491#)
534 cups_dest_t* pDest = ((cups_dest_t*)m_pDests);
535 const char* pOpt = m_pCUPSWrapper->cupsGetOption( "printer-info",
536 pDest->num_options,
537 pDest->options );
538 if( pOpt )
539 m_bUseIncludeFeature = true;
541 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
542 int nPrinter = m_nDests;
544 // reset global default PPD options; these are queried on demand from CUPS
545 m_aGlobalDefaults.m_pParser = NULL;
546 m_aGlobalDefaults.m_aContext = PPDContext();
548 // add CUPS printers, should there be a printer
549 // with the same name as a CUPS printer, overwrite it
550 while( nPrinter-- )
552 pDest = ((cups_dest_t*)m_pDests)+nPrinter;
553 OUString aPrinterName = OStringToOUString( pDest->name, aEncoding );
554 if( pDest->instance && *pDest->instance )
556 OUStringBuffer aBuf( 256 );
557 aBuf.append( aPrinterName );
558 aBuf.append( sal_Unicode( '/' ) );
559 aBuf.append( OStringToOUString( pDest->instance, aEncoding ) );
560 aPrinterName = aBuf.makeStringAndClear();
563 // initialize printer with possible configuration from psprint.conf
564 bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end();
565 Printer aPrinter = m_aPrinters[ aPrinterName ];
566 if( bSetToGlobalDefaults )
567 aPrinter.m_aInfo = m_aGlobalDefaults;
568 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
569 if( pDest->is_default )
570 m_aDefaultPrinter = aPrinterName;
572 for( int k = 0; k < pDest->num_options; k++ )
574 if(!strcmp(pDest->options[k].name, "printer-info"))
575 aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding);
576 if(!strcmp(pDest->options[k].name, "printer-location"))
577 aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding);
581 OUStringBuffer aBuf( 256 );
582 aBuf.appendAscii( "CUPS:" );
583 aBuf.append( aPrinterName );
584 // note: the parser that goes with the PrinterInfo
585 // is created implicitly by the JobData::operator=()
586 // when it detects the NULL ptr m_pParser.
587 // if we wanted to fill in the parser here this
588 // would mean we'd have to download PPDs for each and
589 // every printer - which would be really bad runtime
590 // behaviour
591 aPrinter.m_aInfo.m_pParser = NULL;
592 aPrinter.m_aInfo.m_aContext.setParser( NULL );
593 std::hash_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName );
594 if( c_it != m_aDefaultContexts.end() )
596 aPrinter.m_aInfo.m_pParser = c_it->second.getParser();
597 aPrinter.m_aInfo.m_aContext = c_it->second;
599 aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear();
600 aPrinter.m_bModified = false;
602 m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter;
603 m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter;
606 // remove everything that is not a CUPS printer and not
607 // a special purpose printer (PDF, Fax)
608 std::list< OUString > aRemovePrinters;
609 for( std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin();
610 it != m_aPrinters.end(); ++it )
612 if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() )
613 continue;
615 if( it->second.m_aInfo.m_aFeatures.getLength() > 0 )
616 continue;
617 aRemovePrinters.push_back( it->first );
619 while( aRemovePrinters.begin() != aRemovePrinters.end() )
621 m_aPrinters.erase( aRemovePrinters.front() );
622 aRemovePrinters.pop_front();
625 m_pCUPSWrapper->cupsSetPasswordCB( setPasswordCallback );
626 #endif // ENABLE_CUPS
629 #ifdef ENABLE_CUPS
630 static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext )
632 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
633 for( int i = 0; i < pPPDGroup->num_options; i++ )
635 ppd_option_t* pOption = pPPDGroup->options + i;
636 for( int n = 0; n < pOption->num_choices; n++ )
638 ppd_choice_t* pChoice = pOption->choices + n;
639 if( pChoice->marked )
641 const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) );
642 if( pKey )
644 const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) );
645 if( pValue )
647 if( pValue != pKey->getDefaultValue() )
649 rContext.setValue( pKey, pValue, true );
650 #if OSL_DEBUG_LEVEL > 1
651 fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice );
652 #endif
655 #if OSL_DEBUG_LEVEL > 1
656 else
657 fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice );
658 #endif
660 #if OSL_DEBUG_LEVEL > 1
661 else
662 fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword );
663 #endif
665 #if OSL_DEBUG_LEVEL > 1
666 else
667 fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword );
668 #endif
673 // recurse through subgroups
674 for( int g = 0; g < pPPDGroup->num_subgroups; g++ )
676 updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext );
679 #endif // ENABLE_CUPS
681 const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter )
683 const PPDParser* pNewParser = NULL;
684 OUString aPrinter;
686 if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 )
687 aPrinter = rPrinter.copy( 5 );
688 else
689 aPrinter = rPrinter;
691 #ifdef ENABLE_CUPS
692 if( m_aCUPSMutex.tryToAcquire() )
694 if( m_nDests && m_pDests && ! isCUPSDisabled() )
696 std::hash_map< OUString, int, OUStringHash >::iterator dest_it =
697 m_aCUPSDestMap.find( aPrinter );
698 if( dest_it != m_aCUPSDestMap.end() )
700 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
701 OString aPPDFile = m_pCUPSWrapper->cupsGetPPD( pDest->name );
702 #if OSL_DEBUG_LEVEL > 1
703 fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() );
704 #endif
705 if( aPPDFile.getLength() )
707 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
708 OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) );
709 // update the printer info with context information
710 ppd_file_t* pPPD = m_pCUPSWrapper->ppdOpenFile( aPPDFile.getStr() );
711 if( pPPD )
713 // create the new parser
714 PPDParser* pCUPSParser = new PPDParser( aFileName );
715 pCUPSParser->m_aFile = rPrinter;
716 pNewParser = pCUPSParser;
718 /*int nConflicts =*/ m_pCUPSWrapper->cupsMarkOptions( pPPD, pDest->num_options, pDest->options );
719 #if OSL_DEBUG_LEVEL > 1
720 fprintf( stderr, "processing the following options for printer %s (instance %s):\n",
721 pDest->name, pDest->instance );
722 for( int k = 0; k < pDest->num_options; k++ )
723 fprintf( stderr, " \"%s\" = \"%s\"\n",
724 pDest->options[k].name,
725 pDest->options[k].value );
726 #endif
727 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
729 // remember the default context for later use
730 PPDContext& rContext = m_aDefaultContexts[ aPrinter ];
731 rContext.setParser( pNewParser );
732 // set system default paper; printer CUPS PPD options
733 // may overwrite it
734 setDefaultPaper( rContext );
735 for( int i = 0; i < pPPD->num_groups; i++ )
736 updatePrinterContextInfo( pPPD->groups + i, rContext );
738 rInfo.m_pParser = pNewParser;
739 rInfo.m_aContext = rContext;
741 // clean up the mess
742 m_pCUPSWrapper->ppdClose( pPPD );
744 #if OSL_DEBUG_LEVEL > 1
745 else
746 fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" );
747 #endif
749 // remove temporary PPD file
750 unlink( aPPDFile.getStr() );
752 #if OSL_DEBUG_LEVEL > 1
753 else
754 fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" );
755 #endif
757 #if OSL_DEBUG_LEVEL > 1
758 else
759 fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() );
760 #endif
762 m_aCUPSMutex.release();
764 #if OSL_DEBUG_LEVEL >1
765 else
766 fprintf( stderr, "could not acquire CUPS mutex !!!\n" );
767 #endif
768 #endif // ENABLE_CUPS
770 if( ! pNewParser )
772 // get the default PPD
773 pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) );
775 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
777 rInfo.m_pParser = pNewParser;
778 rInfo.m_aContext.setParser( pNewParser );
781 return pNewParser;
784 void CUPSManager::setupJobContextData(
785 JobData&
786 #ifdef ENABLE_CUPS
787 rData
788 #endif
791 #ifdef ENABLE_CUPS
792 std::hash_map< OUString, int, OUStringHash >::iterator dest_it =
793 m_aCUPSDestMap.find( rData.m_aPrinterName );
795 if( dest_it == m_aCUPSDestMap.end() )
796 return PrinterInfoManager::setupJobContextData( rData );
798 std::hash_map< OUString, Printer, OUStringHash >::iterator p_it =
799 m_aPrinters.find( rData.m_aPrinterName );
800 if( p_it == m_aPrinters.end() ) // huh ?
802 #if OSL_DEBUG_LEVEL > 1
803 fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() );
804 #endif
805 return;
808 if( p_it->second.m_aInfo.m_pParser == NULL )
810 // in turn calls createCUPSParser
811 // which updates the printer info
812 p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName );
814 if( p_it->second.m_aInfo.m_aContext.getParser() == NULL )
816 OUString aPrinter;
817 if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 )
818 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 );
819 else
820 aPrinter = p_it->second.m_aInfo.m_aDriverName;
822 p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ];
825 rData.m_pParser = p_it->second.m_aInfo.m_pParser;
826 rData.m_aContext = p_it->second.m_aInfo.m_aContext;
827 #endif
830 FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
832 if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
833 return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
835 #ifdef ENABLE_CUPS
836 OUString aTmpURL, aTmpFile;
837 osl_createTempFile( NULL, NULL, &aTmpURL.pData );
838 osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData );
839 OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() );
840 FILE* fp = fopen( aSysFile.getStr(), "w" );
841 if( fp )
842 m_aSpoolFiles[fp] = aSysFile;
844 return fp;
845 #else
846 return NULL;
847 #endif
850 struct less_ppd_key : public ::std::binary_function<double, double, bool>
852 bool operator()(const PPDKey* left, const PPDKey* right)
853 { return left->getOrderDependency() < right->getOrderDependency(); }
856 void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, int& rNumOptions, void** rOptions ) const
858 rNumOptions = 0;
859 *rOptions = NULL;
860 int i;
862 // emit features ordered to OrderDependency
863 // ignore features that are set to default
865 // sanity check
866 if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser )
868 int nKeys = rJob.m_aContext.countValuesModified();
869 ::std::vector< const PPDKey* > aKeys( nKeys );
870 for( i = 0; i < nKeys; i++ )
871 aKeys[i] = rJob.m_aContext.getModifiedKey( i );
872 ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
874 for( i = 0; i < nKeys; i++ )
876 const PPDKey* pKey = aKeys[i];
877 const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
878 if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() )
880 OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US );
881 OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US );
882 rNumOptions = m_pCUPSWrapper->cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions );
888 int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData )
890 int nJobID = 0;
892 osl::MutexGuard aGuard( m_aCUPSMutex );
894 std::hash_map< OUString, int, OUStringHash >::iterator dest_it =
895 m_aCUPSDestMap.find( rPrintername );
896 if( dest_it == m_aCUPSDestMap.end() )
897 return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData );
899 #ifdef ENABLE_CUPS
900 std::hash_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
901 if( it != m_aSpoolFiles.end() )
903 fclose( pFile );
904 rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
906 // setup cups options
907 int nNumOptions = 0;
908 cups_option_t* pOptions = NULL;
909 getOptionsFromDocumentSetup( rDocumentJobData, nNumOptions, (void**)&pOptions );
911 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
912 nJobID = m_pCUPSWrapper->cupsPrintFile( pDest->name,
913 it->second.getStr(),
914 OUStringToOString( rJobTitle, aEnc ).getStr(),
915 nNumOptions, pOptions );
916 #if OSL_DEBUG_LEVEL > 1
917 fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n",
918 pDest->name,
919 it->second.getStr(),
920 OUStringToOString( rJobTitle, aEnc ).getStr(),
921 nNumOptions,
922 pOptions,
923 nJobID
925 for( int n = 0; n < nNumOptions; n++ )
926 fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value );
927 OString aCmd( "cp " );
928 aCmd = aCmd + it->second;
929 aCmd = aCmd + OString( " $HOME/cupsprint.ps" );
930 system( aCmd.getStr() );
931 #endif
933 unlink( it->second.getStr() );
934 m_aSpoolFiles.erase( pFile );
935 if( pOptions )
936 m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions );
938 #endif // ENABLE_CUPS
940 return nJobID;
944 void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
946 PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo );
949 bool CUPSManager::checkPrintersChanged( bool bWait )
951 bool bChanged = false;
952 if( bWait )
954 if( m_aDestThread )
956 // initial asynchronous detection still running
957 #if OSL_DEBUG_LEVEL > 1
958 fprintf( stderr, "syncing cups discovery thread\n" );
959 #endif
960 osl_joinWithThread( m_aDestThread );
961 osl_destroyThread( m_aDestThread );
962 m_aDestThread = NULL;
963 #if OSL_DEBUG_LEVEL > 1
964 fprintf( stderr, "done: syncing cups discovery thread\n" );
965 #endif
967 else
969 // #i82321# check for cups printer updates
970 // with this change the whole asynchronous detection in a thread is
971 // almost useless. The only relevance left is for some stalled systems
972 // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION
973 // (see vcl/unx/source/gdi/salprnpsp.cxx)
974 // so that checkPrintersChanged( true ) will never be called
976 // there is no way to query CUPS whether the printer list has changed
977 // so get the dest list anew
978 if( m_nDests && m_pDests )
979 m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests );
980 m_nDests = 0;
981 m_pDests = NULL;
982 runDests();
985 if( m_aCUPSMutex.tryToAcquire() )
987 bChanged = m_bNewDests;
988 m_aCUPSMutex.release();
991 if( ! bChanged )
993 bChanged = PrinterInfoManager::checkPrintersChanged( bWait );
994 // #i54375# ensure new merging with CUPS list in :initialize
995 if( bChanged )
996 m_bNewDests = true;
999 if( bChanged )
1000 initialize();
1002 return bChanged;
1005 bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver )
1007 // don't touch the CUPS printers
1008 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ||
1009 rDriver.compareToAscii( "CUPS:", 5 ) == 0
1011 return false;
1012 return PrinterInfoManager::addPrinter( rName, rDriver );
1015 bool CUPSManager::removePrinter( const OUString& rName, bool bCheck )
1017 // don't touch the CUPS printers
1018 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() )
1019 return false;
1020 return PrinterInfoManager::removePrinter( rName, bCheck );
1023 bool CUPSManager::setDefaultPrinter( const OUString& rName )
1025 bool bSuccess = false;
1026 #ifdef ENABLE_CUPS
1027 std::hash_map< OUString, int, OUStringHash >::iterator nit =
1028 m_aCUPSDestMap.find( rName );
1029 if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() )
1031 cups_dest_t* pDests = (cups_dest_t*)m_pDests;
1032 for( int i = 0; i < m_nDests; i++ )
1033 pDests[i].is_default = 0;
1034 pDests[ nit->second ].is_default = 1;
1035 m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests );
1036 m_aDefaultPrinter = rName;
1037 m_aCUPSMutex.release();
1038 bSuccess = true;
1040 else
1041 #endif
1042 bSuccess = PrinterInfoManager::setDefaultPrinter( rName );
1044 return bSuccess;
1047 bool CUPSManager::writePrinterConfig()
1049 #ifdef ENABLE_CUPS
1050 bool bDestModified = false;
1051 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1053 for( std::hash_map< OUString, Printer, OUStringHash >::iterator prt =
1054 m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt )
1056 std::hash_map< OUString, int, OUStringHash >::iterator nit =
1057 m_aCUPSDestMap.find( prt->first );
1058 if( nit == m_aCUPSDestMap.end() )
1059 continue;
1061 if( ! prt->second.m_bModified )
1062 continue;
1064 if( m_aCUPSMutex.tryToAcquire() )
1066 bDestModified = true;
1067 cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second;
1068 PrinterInfo& rInfo = prt->second.m_aInfo;
1070 // create new option list
1071 int nNewOptions = 0;
1072 cups_option_t* pNewOptions = NULL;
1073 int nValues = rInfo.m_aContext.countValuesModified();
1074 for( int i = 0; i < nValues; i++ )
1076 const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i );
1077 const PPDValue* pValue = rInfo.m_aContext.getValue( pKey );
1078 if( pKey && pValue ) // sanity check
1080 OString aName = OUStringToOString( pKey->getKey(), aEncoding );
1081 OString aValue = OUStringToOString( pValue->m_aOption, aEncoding );
1082 nNewOptions = m_pCUPSWrapper->cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions );
1085 // set PPD options on CUPS dest
1086 m_pCUPSWrapper->cupsFreeOptions( pDest->num_options, pDest->options );
1087 pDest->num_options = nNewOptions;
1088 pDest->options = pNewOptions;
1089 m_aCUPSMutex.release();
1092 if( bDestModified && m_aCUPSMutex.tryToAcquire() )
1094 m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests );
1095 m_aCUPSMutex.release();
1097 #endif // ENABLE_CUPS
1099 return PrinterInfoManager::writePrinterConfig();
1102 bool CUPSManager::addOrRemovePossible() const
1104 return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible();
1107 #include <rtsname.hxx>
1109 const char* CUPSManager::authenticateUser( const char* /*pIn*/ )
1111 const char* pRet = NULL;
1113 #ifdef ENABLE_CUPS
1114 OUString aLib = OUString::createFromAscii( _XSALSET_LIBNAME );
1115 oslModule pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
1116 if( pLib )
1118 OUString aSym( RTL_CONSTASCII_USTRINGPARAM( "Sal_authenticateQuery" ) );
1119 bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) =
1120 (bool(*)(const OString&,OString&,OString&))osl_getFunctionSymbol( pLib, aSym.pData );
1121 if( getpw )
1123 osl::MutexGuard aGuard( m_aCUPSMutex );
1125 OString aUser = m_pCUPSWrapper->cupsUser();
1126 OString aServer = m_pCUPSWrapper->cupsServer();
1127 OString aPassword;
1128 if( getpw( aServer, aUser, aPassword ) )
1130 m_aPassword = aPassword;
1131 m_aUser = aUser;
1132 m_pCUPSWrapper->cupsSetUser( m_aUser.getStr() );
1133 pRet = m_aPassword.getStr();
1136 osl_unloadModule( pLib );
1138 #if OSL_DEBUG_LEVEL > 1
1139 else fprintf( stderr, "loading of module %s failed\n", OUStringToOString( aLib, osl_getThreadTextEncoding() ).getStr() );
1140 #endif
1141 #endif // ENABLE_CUPS
1143 return pRet;