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_vcl.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 "cupsmgr.hxx"
48 #include "osl/thread.h"
49 #include "osl/diagnose.h"
50 #include "osl/conditn.hxx"
52 #include "rtl/ustrbuf.hxx"
58 #define CUPS_LIB_NAME "libcups.so.2"
65 osl::Mutex m_aGetPPDMutex
;
66 bool m_bPPDThreadRunning
;
68 int (*m_pcupsPrintFile
)(const char*, const char*, const char*, int, cups_option_t
*);
69 int (*m_pcupsGetDests
)(cups_dest_t
**);
70 void (*m_pcupsSetDests
)(int,cups_dest_t
*);
71 void (*m_pcupsFreeDests
)(int,cups_dest_t
*);
72 const char* (*m_pcupsGetPPD
)(const char*);
73 int (*m_pcupsMarkOptions
)(ppd_file_t
*,int,cups_option_t
*);
74 int (*m_pcupsAddOption
)(const char*,const char*,int,cups_option_t
**);
75 void (*m_pcupsFreeOptions
)(int,cups_option_t
*);
76 ppd_file_t
* (*m_pppdOpenFile
)(const char* pFile
);
77 void (*m_pppdClose
)(ppd_file_t
*);
78 const char* (*m_pcupsServer
)();
79 void (*m_pcupsSetPasswordCB
)(const char*(cb
)(const char*));
80 const char* (*m_pcupsUser
)();
81 void (*m_pcupsSetUser
)(const char*);
82 const char* (*m_pcupsGetOption
)(const char*,int,cups_option_t
*);
84 oslGenericFunction
loadSymbol( const char* );
91 int cupsGetDests(cups_dest_t
** pDests
)
92 { return m_pcupsGetDests(pDests
); }
94 void cupsSetDests( int nDests
, cups_dest_t
* pDests
)
95 { m_pcupsSetDests( nDests
, pDests
); }
97 void cupsFreeDests(int nDests
, cups_dest_t
* pDests
)
98 { m_pcupsFreeDests(nDests
, pDests
); }
100 int cupsPrintFile( const char* pPrinter
,
101 const char* pFileName
,
104 cups_option_t
* pOptions
)
105 { return m_pcupsPrintFile( pPrinter
, pFileName
, pTitle
, nOptions
, pOptions
); }
107 rtl::OString
cupsGetPPD( const char* pPrinter
);
109 int cupsMarkOptions(ppd_file_t
* pPPD
, int nOptions
, cups_option_t
* pOptions
)
110 { return m_pcupsMarkOptions(pPPD
, nOptions
, pOptions
); }
112 int cupsAddOption( const char* pName
, const char* pValue
, int nOptions
, cups_option_t
** pOptions
)
113 { return m_pcupsAddOption( pName
, pValue
, nOptions
, pOptions
); }
115 void cupsFreeOptions( int nOptions
, cups_option_t
* pOptions
)
116 { m_pcupsFreeOptions( nOptions
, pOptions
); }
118 ppd_file_t
* ppdOpenFile( const char* pFileName
)
119 { return m_pppdOpenFile( pFileName
); }
121 void ppdClose( ppd_file_t
* pPPD
)
122 { m_pppdClose( pPPD
); }
124 const char *cupsServer(void)
125 { return m_pcupsServer(); }
127 const char *cupsUser(void)
128 { return m_pcupsUser(); }
130 void cupsSetPasswordCB(const char *(*cb
)(const char *))
131 { m_pcupsSetPasswordCB( cb
); }
133 void cupsSetUser(const char *user
)
134 { m_pcupsSetUser( user
); }
136 const char* cupsGetOption(const char* name
, int num_options
, cups_option_t
* options
)
137 { return m_pcupsGetOption( name
, num_options
, options
); }
150 oslGenericFunction
CUPSWrapper::loadSymbol( const char* pSymbol
)
152 OUString
aSym( OUString::createFromAscii( pSymbol
) );
153 oslGenericFunction pSym
= osl_getFunctionSymbol( m_pLib
, aSym
.pData
);
154 #if OSL_DEBUG_LEVEL > 1
155 fprintf( stderr
, "%s %s\n", pSymbol
, pSym
? "found" : "not found" );
160 CUPSWrapper::CUPSWrapper()
162 m_bPPDThreadRunning( false )
165 OUString
aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME
) );
166 m_pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
169 aLib
= OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) );
170 m_pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
176 #if OSL_DEBUG_LEVEL > 1
177 fprintf( stderr
, "no cups library found\n" );
182 m_pcupsPrintFile
= (int(*)(const char*,const char*,const char*,int,cups_option_t
*))
183 loadSymbol( "cupsPrintFile" );
184 m_pcupsGetDests
= (int(*)(cups_dest_t
**))
185 loadSymbol( "cupsGetDests" );
186 m_pcupsSetDests
= (void(*)(int,cups_dest_t
*))
187 loadSymbol( "cupsSetDests" );
188 m_pcupsFreeDests
= (void(*)(int,cups_dest_t
*))
189 loadSymbol( "cupsFreeDests" );
190 m_pcupsGetPPD
= (const char*(*)(const char*))
191 loadSymbol( "cupsGetPPD" );
192 m_pcupsMarkOptions
= (int(*)(ppd_file_t
*,int,cups_option_t
*))
193 loadSymbol( "cupsMarkOptions" );
194 m_pcupsAddOption
= (int(*)(const char*,const char*,int,cups_option_t
**))
195 loadSymbol( "cupsAddOption" );
196 m_pcupsFreeOptions
= (void(*)(int,cups_option_t
*))
197 loadSymbol( "cupsFreeOptions" );
198 m_pppdOpenFile
= (ppd_file_t
*(*)(const char*))
199 loadSymbol( "ppdOpenFile" );
200 m_pppdClose
= (void(*)(ppd_file_t
*))
201 loadSymbol( "ppdClose" );
202 m_pcupsServer
= (const char*(*)())
203 loadSymbol( "cupsServer" );
204 m_pcupsUser
= (const char*(*)())
205 loadSymbol( "cupsUser" );
206 m_pcupsSetPasswordCB
= (void(*)(const char*(*)(const char*)))
207 loadSymbol( "cupsSetPasswordCB" );
208 m_pcupsSetUser
= (void(*)(const char*))
209 loadSymbol( "cupsSetUser" );
210 m_pcupsGetOption
= (const char*(*)(const char*,int,cups_option_t
*))
211 loadSymbol( "cupsGetOption" );
219 m_pcupsMarkOptions
&&
223 m_pcupsSetPasswordCB
&&
225 m_pcupsFreeOptions
&&
231 osl_unloadModule( m_pLib
);
236 CUPSWrapper::~CUPSWrapper()
239 osl_unloadModule( m_pLib
);
242 bool CUPSWrapper::isValid()
244 return m_pLib
!= NULL
;
247 typedef const char*(*PPDFunction
)(const char*);
250 PPDFunction m_pFunction
;
251 osl::Condition m_aCondition
;
252 OString m_aParameter
;
256 bool* m_pResetRunning
;
257 osl::Mutex
* m_pSyncMutex
;
259 GetPPDAttribs( PPDFunction pFn
, const char * m_pParameter
,
260 bool* pResetRunning
, osl::Mutex
* pSyncMutex
)
261 : m_pFunction( pFn
),
262 m_aParameter( m_pParameter
),
263 m_pResetRunning( pResetRunning
),
264 m_pSyncMutex( pSyncMutex
)
267 m_aCondition
.reset();
272 if( m_aResult
.getLength() )
273 unlink( m_aResult
.getStr() );
280 *m_pResetRunning
= false;
287 // This CUPS method is not at all thread-safe we need
288 // to dup the pointer to a static buffer it returns ASAP
289 OString aResult
= m_pFunction( m_aParameter
);
290 MutexGuard
aGuard( *m_pSyncMutex
);
296 OString
waitResult( TimeValue
*pDelay
)
298 m_pSyncMutex
->release();
300 if (m_aCondition
.wait( pDelay
) != Condition::result_ok
303 #if OSL_DEBUG_LEVEL > 1
304 fprintf( stderr
, "cupsGetPPD %s timed out\n",
305 (const sal_Char
*) m_aParameter
309 m_pSyncMutex
->acquire();
311 OString aRetval
= m_aResult
;
312 m_aResult
= OString();
320 static void getPPDWorker(void* pData
)
322 GetPPDAttribs
* pAttribs
= (GetPPDAttribs
*)pData
;
323 pAttribs
->executeCall();
327 OString
CUPSWrapper::cupsGetPPD( const char* pPrinter
)
331 m_aGetPPDMutex
.acquire();
332 // if one thread hangs in cupsGetPPD already, don't start another
333 if( ! m_bPPDThreadRunning
)
335 m_bPPDThreadRunning
= true;
336 GetPPDAttribs
* pAttribs
= new GetPPDAttribs( m_pcupsGetPPD
,
338 &m_bPPDThreadRunning
,
341 oslThread aThread
= osl_createThread( getPPDWorker
, pAttribs
);
347 // NOTE: waitResult release and acquires the GetPPD mutex
348 aResult
= pAttribs
->waitResult( &aValue
);
349 osl_destroyThread( aThread
);
351 m_aGetPPDMutex
.release();
357 static const char* setPasswordCallback( const char* pIn
)
359 const char* pRet
= NULL
;
361 PrinterInfoManager
& rMgr
= PrinterInfoManager::get();
362 if( rMgr
.getType() == PrinterInfoManager::CUPS
) // sanity check
363 pRet
= static_cast<CUPSManager
&>(rMgr
).authenticateUser( pIn
);
372 CUPSManager
* CUPSManager::tryLoadCUPS()
374 CUPSManager
* pManager
= NULL
;
376 static const char* pEnv
= getenv( "SAL_DISABLE_CUPS" );
378 if( ! pEnv
|| ! *pEnv
)
381 CUPSWrapper
* pWrapper
= new CUPSWrapper();
382 if( pWrapper
->isValid() )
383 pManager
= new CUPSManager( pWrapper
);
393 static void run_dest_thread_stub( void* pThis
)
395 CUPSManager::runDestThread( pThis
);
399 CUPSManager::CUPSManager( CUPSWrapper
* pWrapper
) :
400 PrinterInfoManager( CUPS
),
401 m_pCUPSWrapper( pWrapper
),
406 m_aDestThread
= osl_createThread( run_dest_thread_stub
, this );
409 CUPSManager::~CUPSManager()
413 // if the thread is still running here, then
414 // cupsGetDests is hung; terminate the thread instead of joining
415 osl_terminateThread( m_aDestThread
);
416 osl_destroyThread( m_aDestThread
);
419 if( m_nDests
&& m_pDests
)
420 m_pCUPSWrapper
->cupsFreeDests( m_nDests
, (cups_dest_t
*)m_pDests
);
421 delete m_pCUPSWrapper
;
424 void CUPSManager::runDestThread( void* pThis
)
426 ((CUPSManager
*)pThis
)->runDests();
429 static sigjmp_buf aViolationBuffer
;
433 static void lcl_signal_action(int nSignal
)
435 fprintf( stderr
, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal
);
436 siglongjmp( aViolationBuffer
, 1 );
440 void CUPSManager::runDests()
442 #if OSL_DEBUG_LEVEL > 1
443 fprintf( stderr
, "starting cupsGetDests\n" );
446 cups_dest_t
* pDests
= NULL
;
448 // #i86306# prepare against really broken CUPS installations / missing servers
450 // install signal handler for SEGV, BUS and ABRT
451 struct sigaction act
;
452 struct sigaction oact
[3];
454 act
.sa_handler
= lcl_signal_action
;
456 sigemptyset(&(act
.sa_mask
));
458 int nSegvSignalInstalled
= sigaction(SIGSEGV
, &act
, &oact
[0]);
459 int nBusSignalInstalled
= sigaction(SIGBUS
, &act
, &oact
[1]);
460 int nAbortSignalInstalled
= sigaction(SIGABRT
, &act
, &oact
[2]);
462 // prepare against a signal during FcInit or FcConfigGetCurrent
463 if( sigsetjmp( aViolationBuffer
, ~0 ) == 0 )
465 nDests
= m_pCUPSWrapper
->cupsGetDests( &pDests
);
466 #if OSL_DEBUG_LEVEL > 1
467 fprintf( stderr
, "came out of cupsGetDests\n" );
470 osl::MutexGuard
aGuard( m_aCUPSMutex
);
474 #if OSL_DEBUG_LEVEL > 1
475 fprintf( stderr
, "finished cupsGetDests\n" );
480 #if OSL_DEBUG_LEVEL > 1
481 fprintf( stderr
, "cupsGetDests crashed, not using CUPS\n" );
485 // restore old signal handlers
486 if( nSegvSignalInstalled
== 0 )
487 sigaction( SIGSEGV
, &oact
[0], NULL
);
488 if( nBusSignalInstalled
== 0 )
489 sigaction( SIGBUS
, &oact
[1], NULL
);
490 if( nAbortSignalInstalled
== 0 )
491 sigaction( SIGABRT
, &oact
[2], NULL
);
494 void CUPSManager::initialize()
496 // get normal printers, clear printer list
497 PrinterInfoManager::initialize();
500 // check whether thread has completed
501 // if not behave like old printing system
502 osl::MutexGuard
aGuard( m_aCUPSMutex
);
507 // dest thread has run, clean up
510 osl_joinWithThread( m_aDestThread
);
511 osl_destroyThread( m_aDestThread
);
512 m_aDestThread
= NULL
;
517 m_aCUPSDestMap
.clear();
519 if( ! (m_nDests
&& m_pDests
) )
522 if( isCUPSDisabled() )
525 // check for CUPS server(?) > 1.2
526 // since there is no API to query, check for options that were
527 // introduced in dests with 1.2
528 // this is needed to check for %%IncludeFeature support
529 // (#i65684#, #i65491#)
530 cups_dest_t
* pDest
= ((cups_dest_t
*)m_pDests
);
531 const char* pOpt
= m_pCUPSWrapper
->cupsGetOption( "printer-info",
535 m_bUseIncludeFeature
= true;
537 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
538 int nPrinter
= m_nDests
;
540 // reset global default PPD options; these are queried on demand from CUPS
541 m_aGlobalDefaults
.m_pParser
= NULL
;
542 m_aGlobalDefaults
.m_aContext
= PPDContext();
544 // add CUPS printers, should there be a printer
545 // with the same name as a CUPS printer, overwrite it
548 pDest
= ((cups_dest_t
*)m_pDests
)+nPrinter
;
549 OUString aPrinterName
= OStringToOUString( pDest
->name
, aEncoding
);
550 if( pDest
->instance
&& *pDest
->instance
)
552 OUStringBuffer
aBuf( 256 );
553 aBuf
.append( aPrinterName
);
554 aBuf
.append( sal_Unicode( '/' ) );
555 aBuf
.append( OStringToOUString( pDest
->instance
, aEncoding
) );
556 aPrinterName
= aBuf
.makeStringAndClear();
559 // initialize printer with possible configuration from psprint.conf
560 bool bSetToGlobalDefaults
= m_aPrinters
.find( aPrinterName
) == m_aPrinters
.end();
561 Printer aPrinter
= m_aPrinters
[ aPrinterName
];
562 if( bSetToGlobalDefaults
)
563 aPrinter
.m_aInfo
= m_aGlobalDefaults
;
564 aPrinter
.m_aInfo
.m_aPrinterName
= aPrinterName
;
565 if( pDest
->is_default
)
566 m_aDefaultPrinter
= aPrinterName
;
568 for( int k
= 0; k
< pDest
->num_options
; k
++ )
570 if(!strcmp(pDest
->options
[k
].name
, "printer-info"))
571 aPrinter
.m_aInfo
.m_aComment
=OStringToOUString(pDest
->options
[k
].value
, aEncoding
);
572 if(!strcmp(pDest
->options
[k
].name
, "printer-location"))
573 aPrinter
.m_aInfo
.m_aLocation
=OStringToOUString(pDest
->options
[k
].value
, aEncoding
);
577 OUStringBuffer
aBuf( 256 );
578 aBuf
.appendAscii( "CUPS:" );
579 aBuf
.append( aPrinterName
);
580 // note: the parser that goes with the PrinterInfo
581 // is created implicitly by the JobData::operator=()
582 // when it detects the NULL ptr m_pParser.
583 // if we wanted to fill in the parser here this
584 // would mean we'd have to download PPDs for each and
585 // every printer - which would be really bad runtime
587 aPrinter
.m_aInfo
.m_pParser
= NULL
;
588 aPrinter
.m_aInfo
.m_aContext
.setParser( NULL
);
589 std::hash_map
< OUString
, PPDContext
, OUStringHash
>::const_iterator c_it
= m_aDefaultContexts
.find( aPrinterName
);
590 if( c_it
!= m_aDefaultContexts
.end() )
592 aPrinter
.m_aInfo
.m_pParser
= c_it
->second
.getParser();
593 aPrinter
.m_aInfo
.m_aContext
= c_it
->second
;
595 aPrinter
.m_aInfo
.m_aDriverName
= aBuf
.makeStringAndClear();
596 aPrinter
.m_bModified
= false;
598 m_aPrinters
[ aPrinter
.m_aInfo
.m_aPrinterName
] = aPrinter
;
599 m_aCUPSDestMap
[ aPrinter
.m_aInfo
.m_aPrinterName
] = nPrinter
;
602 // remove everything that is not a CUPS printer and not
603 // a special purpose printer (PDF, Fax)
604 std::list
< OUString
> aRemovePrinters
;
605 for( std::hash_map
< OUString
, Printer
, OUStringHash
>::iterator it
= m_aPrinters
.begin();
606 it
!= m_aPrinters
.end(); ++it
)
608 if( m_aCUPSDestMap
.find( it
->first
) != m_aCUPSDestMap
.end() )
611 if( it
->second
.m_aInfo
.m_aFeatures
.getLength() > 0 )
613 aRemovePrinters
.push_back( it
->first
);
615 while( aRemovePrinters
.begin() != aRemovePrinters
.end() )
617 m_aPrinters
.erase( aRemovePrinters
.front() );
618 aRemovePrinters
.pop_front();
621 m_pCUPSWrapper
->cupsSetPasswordCB( setPasswordCallback
);
622 #endif // ENABLE_CUPS
626 static void updatePrinterContextInfo( ppd_group_t
* pPPDGroup
, PPDContext
& rContext
)
628 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
629 for( int i
= 0; i
< pPPDGroup
->num_options
; i
++ )
631 ppd_option_t
* pOption
= pPPDGroup
->options
+ i
;
632 for( int n
= 0; n
< pOption
->num_choices
; n
++ )
634 ppd_choice_t
* pChoice
= pOption
->choices
+ n
;
635 if( pChoice
->marked
)
637 const PPDKey
* pKey
= rContext
.getParser()->getKey( OStringToOUString( pOption
->keyword
, aEncoding
) );
640 const PPDValue
* pValue
= pKey
->getValue( OStringToOUString( pChoice
->choice
, aEncoding
) );
643 if( pValue
!= pKey
->getDefaultValue() )
645 rContext
.setValue( pKey
, pValue
, true );
646 #if OSL_DEBUG_LEVEL > 1
647 fprintf( stderr
, "key %s is set to %s\n", pOption
->keyword
, pChoice
->choice
);
651 #if OSL_DEBUG_LEVEL > 1
653 fprintf( stderr
, "key %s is defaulted to %s\n", pOption
->keyword
, pChoice
->choice
);
656 #if OSL_DEBUG_LEVEL > 1
658 fprintf( stderr
, "caution: value %s not found in key %s\n", pChoice
->choice
, pOption
->keyword
);
661 #if OSL_DEBUG_LEVEL > 1
663 fprintf( stderr
, "caution: key %s not found in parser\n", pOption
->keyword
);
669 // recurse through subgroups
670 for( int g
= 0; g
< pPPDGroup
->num_subgroups
; g
++ )
672 updatePrinterContextInfo( pPPDGroup
->subgroups
+ g
, rContext
);
675 #endif // ENABLE_CUPS
677 const PPDParser
* CUPSManager::createCUPSParser( const OUString
& rPrinter
)
679 const PPDParser
* pNewParser
= NULL
;
682 if( rPrinter
.compareToAscii( "CUPS:", 5 ) == 0 )
683 aPrinter
= rPrinter
.copy( 5 );
688 if( m_aCUPSMutex
.tryToAcquire() )
690 if( m_nDests
&& m_pDests
&& ! isCUPSDisabled() )
692 std::hash_map
< OUString
, int, OUStringHash
>::iterator dest_it
=
693 m_aCUPSDestMap
.find( aPrinter
);
694 if( dest_it
!= m_aCUPSDestMap
.end() )
696 cups_dest_t
* pDest
= ((cups_dest_t
*)m_pDests
) + dest_it
->second
;
697 OString aPPDFile
= m_pCUPSWrapper
->cupsGetPPD( pDest
->name
);
698 #if OSL_DEBUG_LEVEL > 1
699 fprintf( stderr
, "PPD for %s is %s\n", OUStringToOString( aPrinter
, osl_getThreadTextEncoding() ).getStr(), aPPDFile
.getStr() );
701 if( aPPDFile
.getLength() )
703 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
704 OUString
aFileName( OStringToOUString( aPPDFile
, aEncoding
) );
705 // update the printer info with context information
706 ppd_file_t
* pPPD
= m_pCUPSWrapper
->ppdOpenFile( aPPDFile
.getStr() );
709 // create the new parser
710 PPDParser
* pCUPSParser
= new PPDParser( aFileName
);
711 pCUPSParser
->m_aFile
= rPrinter
;
712 pNewParser
= pCUPSParser
;
714 /*int nConflicts =*/ m_pCUPSWrapper
->cupsMarkOptions( pPPD
, pDest
->num_options
, pDest
->options
);
715 #if OSL_DEBUG_LEVEL > 1
716 fprintf( stderr
, "processing the following options for printer %s (instance %s):\n",
717 pDest
->name
, pDest
->instance
);
718 for( int k
= 0; k
< pDest
->num_options
; k
++ )
719 fprintf( stderr
, " \"%s\" = \"%s\"\n",
720 pDest
->options
[k
].name
,
721 pDest
->options
[k
].value
);
723 PrinterInfo
& rInfo
= m_aPrinters
[ aPrinter
].m_aInfo
;
725 // remember the default context for later use
726 PPDContext
& rContext
= m_aDefaultContexts
[ aPrinter
];
727 rContext
.setParser( pNewParser
);
728 // set system default paper; printer CUPS PPD options
730 setDefaultPaper( rContext
);
731 for( int i
= 0; i
< pPPD
->num_groups
; i
++ )
732 updatePrinterContextInfo( pPPD
->groups
+ i
, rContext
);
734 rInfo
.m_pParser
= pNewParser
;
735 rInfo
.m_aContext
= rContext
;
738 m_pCUPSWrapper
->ppdClose( pPPD
);
740 #if OSL_DEBUG_LEVEL > 1
742 fprintf( stderr
, "ppdOpenFile failed, falling back to generic driver\n" );
745 // remove temporary PPD file
746 unlink( aPPDFile
.getStr() );
748 #if OSL_DEBUG_LEVEL > 1
750 fprintf( stderr
, "cupsGetPPD failed, falling back to generic driver\n" );
753 #if OSL_DEBUG_LEVEL > 1
755 fprintf( stderr
, "no dest found for printer %s\n", OUStringToOString( aPrinter
, osl_getThreadTextEncoding() ).getStr() );
758 m_aCUPSMutex
.release();
760 #if OSL_DEBUG_LEVEL >1
762 fprintf( stderr
, "could not acquire CUPS mutex !!!\n" );
764 #endif // ENABLE_CUPS
768 // get the default PPD
769 pNewParser
= PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) );
771 PrinterInfo
& rInfo
= m_aPrinters
[ aPrinter
].m_aInfo
;
773 rInfo
.m_pParser
= pNewParser
;
774 rInfo
.m_aContext
.setParser( pNewParser
);
780 void CUPSManager::setupJobContextData(
788 std::hash_map
< OUString
, int, OUStringHash
>::iterator dest_it
=
789 m_aCUPSDestMap
.find( rData
.m_aPrinterName
);
791 if( dest_it
== m_aCUPSDestMap
.end() )
792 return PrinterInfoManager::setupJobContextData( rData
);
794 std::hash_map
< OUString
, Printer
, OUStringHash
>::iterator p_it
=
795 m_aPrinters
.find( rData
.m_aPrinterName
);
796 if( p_it
== m_aPrinters
.end() ) // huh ?
798 #if OSL_DEBUG_LEVEL > 1
799 fprintf( stderr
, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData
.m_aPrinterName
, osl_getThreadTextEncoding() ).getStr() );
804 if( p_it
->second
.m_aInfo
.m_pParser
== NULL
)
806 // in turn calls createCUPSParser
807 // which updates the printer info
808 p_it
->second
.m_aInfo
.m_pParser
= PPDParser::getParser( p_it
->second
.m_aInfo
.m_aDriverName
);
810 if( p_it
->second
.m_aInfo
.m_aContext
.getParser() == NULL
)
813 if( p_it
->second
.m_aInfo
.m_aDriverName
.compareToAscii( "CUPS:", 5 ) == 0 )
814 aPrinter
= p_it
->second
.m_aInfo
.m_aDriverName
.copy( 5 );
816 aPrinter
= p_it
->second
.m_aInfo
.m_aDriverName
;
818 p_it
->second
.m_aInfo
.m_aContext
= m_aDefaultContexts
[ aPrinter
];
821 rData
.m_pParser
= p_it
->second
.m_aInfo
.m_pParser
;
822 rData
.m_aContext
= p_it
->second
.m_aInfo
.m_aContext
;
826 FILE* CUPSManager::startSpool( const OUString
& rPrintername
, bool bQuickCommand
)
828 if( m_aCUPSDestMap
.find( rPrintername
) == m_aCUPSDestMap
.end() )
829 return PrinterInfoManager::startSpool( rPrintername
, bQuickCommand
);
832 OUString aTmpURL
, aTmpFile
;
833 osl_createTempFile( NULL
, NULL
, &aTmpURL
.pData
);
834 osl_getSystemPathFromFileURL( aTmpURL
.pData
, &aTmpFile
.pData
);
835 OString aSysFile
= OUStringToOString( aTmpFile
, osl_getThreadTextEncoding() );
836 FILE* fp
= fopen( aSysFile
.getStr(), "w" );
838 m_aSpoolFiles
[fp
] = aSysFile
;
846 struct less_ppd_key
: public ::std::binary_function
<double, double, bool>
848 bool operator()(const PPDKey
* left
, const PPDKey
* right
)
849 { return left
->getOrderDependency() < right
->getOrderDependency(); }
852 void CUPSManager::getOptionsFromDocumentSetup( const JobData
& rJob
, int& rNumOptions
, void** rOptions
) const
858 // emit features ordered to OrderDependency
859 // ignore features that are set to default
862 if( rJob
.m_pParser
== rJob
.m_aContext
.getParser() && rJob
.m_pParser
)
864 int nKeys
= rJob
.m_aContext
.countValuesModified();
865 ::std::vector
< const PPDKey
* > aKeys( nKeys
);
866 for( i
= 0; i
< nKeys
; i
++ )
867 aKeys
[i
] = rJob
.m_aContext
.getModifiedKey( i
);
868 ::std::sort( aKeys
.begin(), aKeys
.end(), less_ppd_key() );
870 for( i
= 0; i
< nKeys
; i
++ )
872 const PPDKey
* pKey
= aKeys
[i
];
873 const PPDValue
* pValue
= rJob
.m_aContext
.getValue( pKey
);
874 if(pValue
&& pValue
->m_eType
== eInvocation
&& pValue
->m_aValue
.Len() )
876 OString aKey
= OUStringToOString( pKey
->getKey(), RTL_TEXTENCODING_ASCII_US
);
877 OString aValue
= OUStringToOString( pValue
->m_aOption
, RTL_TEXTENCODING_ASCII_US
);
878 rNumOptions
= m_pCUPSWrapper
->cupsAddOption( aKey
.getStr(), aValue
.getStr(), rNumOptions
, (cups_option_t
**)rOptions
);
884 int CUPSManager::endSpool( const OUString
& rPrintername
, const OUString
& rJobTitle
, FILE* pFile
, const JobData
& rDocumentJobData
)
888 osl::MutexGuard
aGuard( m_aCUPSMutex
);
890 std::hash_map
< OUString
, int, OUStringHash
>::iterator dest_it
=
891 m_aCUPSDestMap
.find( rPrintername
);
892 if( dest_it
== m_aCUPSDestMap
.end() )
893 return PrinterInfoManager::endSpool( rPrintername
, rJobTitle
, pFile
, rDocumentJobData
);
896 std::hash_map
< FILE*, OString
, FPtrHash
>::const_iterator it
= m_aSpoolFiles
.find( pFile
);
897 if( it
!= m_aSpoolFiles
.end() )
900 rtl_TextEncoding aEnc
= osl_getThreadTextEncoding();
902 // setup cups options
904 cups_option_t
* pOptions
= NULL
;
905 getOptionsFromDocumentSetup( rDocumentJobData
, nNumOptions
, (void**)&pOptions
);
907 cups_dest_t
* pDest
= ((cups_dest_t
*)m_pDests
) + dest_it
->second
;
908 nJobID
= m_pCUPSWrapper
->cupsPrintFile( pDest
->name
,
910 OUStringToOString( rJobTitle
, aEnc
).getStr(),
911 nNumOptions
, pOptions
);
912 #if OSL_DEBUG_LEVEL > 1
913 fprintf( stderr
, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n",
916 OUStringToOString( rJobTitle
, aEnc
).getStr(),
921 for( int n
= 0; n
< nNumOptions
; n
++ )
922 fprintf( stderr
, " option %s=%s\n", pOptions
[n
].name
, pOptions
[n
].value
);
923 OString
aCmd( "cp " );
924 aCmd
= aCmd
+ it
->second
;
925 aCmd
= aCmd
+ OString( " $HOME/cupsprint.ps" );
926 system( aCmd
.getStr() );
929 unlink( it
->second
.getStr() );
930 m_aSpoolFiles
.erase( pFile
);
932 m_pCUPSWrapper
->cupsFreeOptions( nNumOptions
, pOptions
);
934 #endif // ENABLE_CUPS
940 void CUPSManager::changePrinterInfo( const OUString
& rPrinter
, const PrinterInfo
& rNewInfo
)
942 PrinterInfoManager::changePrinterInfo( rPrinter
, rNewInfo
);
945 bool CUPSManager::checkPrintersChanged( bool bWait
)
947 bool bChanged
= false;
952 // initial asynchronous detection still running
953 #if OSL_DEBUG_LEVEL > 1
954 fprintf( stderr
, "syncing cups discovery thread\n" );
956 osl_joinWithThread( m_aDestThread
);
957 osl_destroyThread( m_aDestThread
);
958 m_aDestThread
= NULL
;
959 #if OSL_DEBUG_LEVEL > 1
960 fprintf( stderr
, "done: syncing cups discovery thread\n" );
965 // #i82321# check for cups printer updates
966 // with this change the whole asynchronous detection in a thread is
967 // almost useless. The only relevance left is for some stalled systems
968 // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION
969 // (see vcl/unx/source/gdi/salprnpsp.cxx)
970 // so that checkPrintersChanged( true ) will never be called
972 // there is no way to query CUPS whether the printer list has changed
973 // so get the dest list anew
974 if( m_nDests
&& m_pDests
)
975 m_pCUPSWrapper
->cupsFreeDests( m_nDests
, (cups_dest_t
*)m_pDests
);
981 if( m_aCUPSMutex
.tryToAcquire() )
983 bChanged
= m_bNewDests
;
984 m_aCUPSMutex
.release();
989 bChanged
= PrinterInfoManager::checkPrintersChanged( bWait
);
990 // #i54375# ensure new merging with CUPS list in :initialize
1001 bool CUPSManager::addPrinter( const OUString
& rName
, const OUString
& rDriver
)
1003 // don't touch the CUPS printers
1004 if( m_aCUPSDestMap
.find( rName
) != m_aCUPSDestMap
.end() ||
1005 rDriver
.compareToAscii( "CUPS:", 5 ) == 0
1008 return PrinterInfoManager::addPrinter( rName
, rDriver
);
1011 bool CUPSManager::removePrinter( const OUString
& rName
, bool bCheck
)
1013 // don't touch the CUPS printers
1014 if( m_aCUPSDestMap
.find( rName
) != m_aCUPSDestMap
.end() )
1016 return PrinterInfoManager::removePrinter( rName
, bCheck
);
1019 bool CUPSManager::setDefaultPrinter( const OUString
& rName
)
1021 bool bSuccess
= false;
1023 std::hash_map
< OUString
, int, OUStringHash
>::iterator nit
=
1024 m_aCUPSDestMap
.find( rName
);
1025 if( nit
!= m_aCUPSDestMap
.end() && m_aCUPSMutex
.tryToAcquire() )
1027 cups_dest_t
* pDests
= (cups_dest_t
*)m_pDests
;
1028 for( int i
= 0; i
< m_nDests
; i
++ )
1029 pDests
[i
].is_default
= 0;
1030 pDests
[ nit
->second
].is_default
= 1;
1031 m_pCUPSWrapper
->cupsSetDests( m_nDests
, (cups_dest_t
*)m_pDests
);
1032 m_aDefaultPrinter
= rName
;
1033 m_aCUPSMutex
.release();
1038 bSuccess
= PrinterInfoManager::setDefaultPrinter( rName
);
1043 bool CUPSManager::writePrinterConfig()
1046 bool bDestModified
= false;
1047 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
1049 for( std::hash_map
< OUString
, Printer
, OUStringHash
>::iterator prt
=
1050 m_aPrinters
.begin(); prt
!= m_aPrinters
.end(); ++prt
)
1052 std::hash_map
< OUString
, int, OUStringHash
>::iterator nit
=
1053 m_aCUPSDestMap
.find( prt
->first
);
1054 if( nit
== m_aCUPSDestMap
.end() )
1057 if( ! prt
->second
.m_bModified
)
1060 if( m_aCUPSMutex
.tryToAcquire() )
1062 bDestModified
= true;
1063 cups_dest_t
* pDest
= ((cups_dest_t
*)m_pDests
) + nit
->second
;
1064 PrinterInfo
& rInfo
= prt
->second
.m_aInfo
;
1066 // create new option list
1067 int nNewOptions
= 0;
1068 cups_option_t
* pNewOptions
= NULL
;
1069 int nValues
= rInfo
.m_aContext
.countValuesModified();
1070 for( int i
= 0; i
< nValues
; i
++ )
1072 const PPDKey
* pKey
= rInfo
.m_aContext
.getModifiedKey( i
);
1073 const PPDValue
* pValue
= rInfo
.m_aContext
.getValue( pKey
);
1074 if( pKey
&& pValue
) // sanity check
1076 OString aName
= OUStringToOString( pKey
->getKey(), aEncoding
);
1077 OString aValue
= OUStringToOString( pValue
->m_aOption
, aEncoding
);
1078 nNewOptions
= m_pCUPSWrapper
->cupsAddOption( aName
.getStr(), aValue
.getStr(), nNewOptions
, &pNewOptions
);
1081 // set PPD options on CUPS dest
1082 m_pCUPSWrapper
->cupsFreeOptions( pDest
->num_options
, pDest
->options
);
1083 pDest
->num_options
= nNewOptions
;
1084 pDest
->options
= pNewOptions
;
1085 m_aCUPSMutex
.release();
1088 if( bDestModified
&& m_aCUPSMutex
.tryToAcquire() )
1090 m_pCUPSWrapper
->cupsSetDests( m_nDests
, (cups_dest_t
*)m_pDests
);
1091 m_aCUPSMutex
.release();
1093 #endif // ENABLE_CUPS
1095 return PrinterInfoManager::writePrinterConfig();
1098 bool CUPSManager::addOrRemovePossible() const
1100 return (m_nDests
&& m_pDests
&& ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible();
1103 #include <rtsname.hxx>
1105 const char* CUPSManager::authenticateUser( const char* /*pIn*/ )
1107 const char* pRet
= NULL
;
1110 OUString aLib
= OUString::createFromAscii( _XSALSET_LIBNAME
);
1111 oslModule pLib
= osl_loadModule( aLib
.pData
, SAL_LOADMODULE_LAZY
);
1114 OUString
aSym( RTL_CONSTASCII_USTRINGPARAM( "Sal_authenticateQuery" ) );
1115 bool (*getpw
)( const OString
& rServer
, OString
& rUser
, OString
& rPw
) =
1116 (bool(*)(const OString
&,OString
&,OString
&))osl_getFunctionSymbol( pLib
, aSym
.pData
);
1119 osl::MutexGuard
aGuard( m_aCUPSMutex
);
1121 OString aUser
= m_pCUPSWrapper
->cupsUser();
1122 OString aServer
= m_pCUPSWrapper
->cupsServer();
1124 if( getpw( aServer
, aUser
, aPassword
) )
1126 m_aPassword
= aPassword
;
1128 m_pCUPSWrapper
->cupsSetUser( m_aUser
.getStr() );
1129 pRet
= m_aPassword
.getStr();
1132 osl_unloadModule( pLib
);
1134 #if OSL_DEBUG_LEVEL > 1
1135 else fprintf( stderr
, "loading of module %s failed\n", OUStringToOString( aLib
, osl_getThreadTextEncoding() ).getStr() );
1137 #endif // ENABLE_CUPS