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