1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cupsmgr.cxx,v $
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"
35 #include <cups/cups.h>
39 typedef void ppd_file_t
;
40 typedef void cups_dest_t
;
41 typedef void cups_option_t
;
46 #include <osl/thread.h>
47 #include <osl/diagnose.h>
48 #include <osl/conditn.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <cupsmgr.hxx>
57 // FIXME: SAL_MODULENAME_WITH_VERSION needs to be fixed on OS X
59 #define CUPS_LIB_NAME "libcups.2.dylib"
61 #define CUPS_LIB_NAME "libcups.so.2"
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* );
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
,
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
); }
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" );
164 CUPSWrapper::CUPSWrapper()
166 m_bPPDThreadRunning( false )
169 OUString
aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME
) );
170 m_pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
173 aLib
= OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) );
174 m_pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
180 #if OSL_DEBUG_LEVEL > 1
181 fprintf( stderr
, "no cups library found\n" );
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" );
223 m_pcupsMarkOptions
&&
227 m_pcupsSetPasswordCB
&&
229 m_pcupsFreeOptions
&&
235 osl_unloadModule( m_pLib
);
240 CUPSWrapper::~CUPSWrapper()
243 osl_unloadModule( m_pLib
);
246 bool CUPSWrapper::isValid()
248 return m_pLib
!= NULL
;
251 typedef const char*(*PPDFunction
)(const char*);
254 PPDFunction m_pFunction
;
255 osl::Condition m_aCondition
;
256 OString m_aParameter
;
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
)
271 m_aCondition
.reset();
276 if( m_aResult
.getLength() )
277 unlink( m_aResult
.getStr() );
284 *m_pResetRunning
= false;
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
);
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
313 m_pSyncMutex
->acquire();
315 OString aRetval
= m_aResult
;
316 m_aResult
= OString();
324 static void getPPDWorker(void* pData
)
326 GetPPDAttribs
* pAttribs
= (GetPPDAttribs
*)pData
;
327 pAttribs
->executeCall();
331 OString
CUPSWrapper::cupsGetPPD( const char* pPrinter
)
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
,
342 &m_bPPDThreadRunning
,
345 oslThread aThread
= osl_createThread( getPPDWorker
, pAttribs
);
351 // NOTE: waitResult release and acquires the GetPPD mutex
352 aResult
= pAttribs
->waitResult( &aValue
);
353 osl_destroyThread( aThread
);
355 m_aGetPPDMutex
.release();
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
);
376 CUPSManager
* CUPSManager::tryLoadCUPS()
378 CUPSManager
* pManager
= NULL
;
380 static const char* pEnv
= getenv( "SAL_DISABLE_CUPS" );
382 if( ! pEnv
|| ! *pEnv
)
385 CUPSWrapper
* pWrapper
= new CUPSWrapper();
386 if( pWrapper
->isValid() )
387 pManager
= new CUPSManager( pWrapper
);
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
),
410 m_aDestThread
= osl_createThread( run_dest_thread_stub
, this );
413 CUPSManager::~CUPSManager()
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
;
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" );
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
;
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" );
474 osl::MutexGuard
aGuard( m_aCUPSMutex
);
478 #if OSL_DEBUG_LEVEL > 1
479 fprintf( stderr
, "finished cupsGetDests\n" );
484 #if OSL_DEBUG_LEVEL > 1
485 fprintf( stderr
, "cupsGetDests crashed, not using CUPS\n" );
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();
504 // check whether thread has completed
505 // if not behave like old printing system
506 osl::MutexGuard
aGuard( m_aCUPSMutex
);
511 // dest thread has run, clean up
514 osl_joinWithThread( m_aDestThread
);
515 osl_destroyThread( m_aDestThread
);
516 m_aDestThread
= NULL
;
521 m_aCUPSDestMap
.clear();
523 if( ! (m_nDests
&& m_pDests
) )
526 if( isCUPSDisabled() )
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",
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
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
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() )
615 if( it
->second
.m_aInfo
.m_aFeatures
.getLength() > 0 )
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
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
) );
644 const PPDValue
* pValue
= pKey
->getValue( OStringToOUString( pChoice
->choice
, aEncoding
) );
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
);
655 #if OSL_DEBUG_LEVEL > 1
657 fprintf( stderr
, "key %s is defaulted to %s\n", pOption
->keyword
, pChoice
->choice
);
660 #if OSL_DEBUG_LEVEL > 1
662 fprintf( stderr
, "caution: value %s not found in key %s\n", pChoice
->choice
, pOption
->keyword
);
665 #if OSL_DEBUG_LEVEL > 1
667 fprintf( stderr
, "caution: key %s not found in parser\n", pOption
->keyword
);
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
;
686 if( rPrinter
.compareToAscii( "CUPS:", 5 ) == 0 )
687 aPrinter
= rPrinter
.copy( 5 );
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() );
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() );
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
);
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
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
;
742 m_pCUPSWrapper
->ppdClose( pPPD
);
744 #if OSL_DEBUG_LEVEL > 1
746 fprintf( stderr
, "ppdOpenFile failed, falling back to generic driver\n" );
749 // remove temporary PPD file
750 unlink( aPPDFile
.getStr() );
752 #if OSL_DEBUG_LEVEL > 1
754 fprintf( stderr
, "cupsGetPPD failed, falling back to generic driver\n" );
757 #if OSL_DEBUG_LEVEL > 1
759 fprintf( stderr
, "no dest found for printer %s\n", OUStringToOString( aPrinter
, osl_getThreadTextEncoding() ).getStr() );
762 m_aCUPSMutex
.release();
764 #if OSL_DEBUG_LEVEL >1
766 fprintf( stderr
, "could not acquire CUPS mutex !!!\n" );
768 #endif // ENABLE_CUPS
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
);
784 void CUPSManager::setupJobContextData(
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() );
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
)
817 if( p_it
->second
.m_aInfo
.m_aDriverName
.compareToAscii( "CUPS:", 5 ) == 0 )
818 aPrinter
= p_it
->second
.m_aInfo
.m_aDriverName
.copy( 5 );
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
;
830 FILE* CUPSManager::startSpool( const OUString
& rPrintername
, bool bQuickCommand
)
832 if( m_aCUPSDestMap
.find( rPrintername
) == m_aCUPSDestMap
.end() )
833 return PrinterInfoManager::startSpool( rPrintername
, bQuickCommand
);
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" );
842 m_aSpoolFiles
[fp
] = aSysFile
;
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
862 // emit features ordered to OrderDependency
863 // ignore features that are set to default
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
)
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
);
900 std::hash_map
< FILE*, OString
, FPtrHash
>::const_iterator it
= m_aSpoolFiles
.find( pFile
);
901 if( it
!= m_aSpoolFiles
.end() )
904 rtl_TextEncoding aEnc
= osl_getThreadTextEncoding();
906 // setup cups options
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
,
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",
920 OUStringToOString( rJobTitle
, aEnc
).getStr(),
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() );
933 unlink( it
->second
.getStr() );
934 m_aSpoolFiles
.erase( pFile
);
936 m_pCUPSWrapper
->cupsFreeOptions( nNumOptions
, pOptions
);
938 #endif // ENABLE_CUPS
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;
956 // initial asynchronous detection still running
957 #if OSL_DEBUG_LEVEL > 1
958 fprintf( stderr
, "syncing cups discovery thread\n" );
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" );
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
);
985 if( m_aCUPSMutex
.tryToAcquire() )
987 bChanged
= m_bNewDests
;
988 m_aCUPSMutex
.release();
993 bChanged
= PrinterInfoManager::checkPrintersChanged( bWait
);
994 // #i54375# ensure new merging with CUPS list in :initialize
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
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() )
1020 return PrinterInfoManager::removePrinter( rName
, bCheck
);
1023 bool CUPSManager::setDefaultPrinter( const OUString
& rName
)
1025 bool bSuccess
= false;
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();
1042 bSuccess
= PrinterInfoManager::setDefaultPrinter( rName
);
1047 bool CUPSManager::writePrinterConfig()
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() )
1061 if( ! prt
->second
.m_bModified
)
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
;
1114 OUString aLib
= OUString::createFromAscii( _XSALSET_LIBNAME
);
1115 oslModule pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
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
);
1123 osl::MutexGuard
aGuard( m_aCUPSMutex
);
1125 OString aUser
= m_pCUPSWrapper
->cupsUser();
1126 OString aServer
= m_pCUPSWrapper
->cupsServer();
1128 if( getpw( aServer
, aUser
, aPassword
) )
1130 m_aPassword
= aPassword
;
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() );
1141 #endif // ENABLE_CUPS