merge the formfield patch from ooo-build
[ooovba.git] / vcl / unx / source / printer / cupsmgr.cxx
blob0f44c061c9a791c3a606e24397352b8e723b4243
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cupsmgr.cxx,v $
10 * $Revision: 1.28 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #ifdef ENABLE_CUPS
35 #include <cups/cups.h>
36 #include <cups/ppd.h>
38 #else // !ENABLE_CUPS
39 typedef void ppd_file_t;
40 typedef void cups_dest_t;
41 typedef void cups_option_t;
42 #endif
44 #include <unistd.h>
46 #include "cupsmgr.hxx"
48 #include "osl/thread.h"
49 #include "osl/diagnose.h"
50 #include "osl/conditn.hxx"
52 #include "rtl/ustrbuf.hxx"
54 #include <algorithm>
55 #include <setjmp.h>
56 #include <signal.h>
58 #define CUPS_LIB_NAME "libcups.so.2"
60 namespace psp
62 class CUPSWrapper
64 oslModule m_pLib;
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* );
85 public:
86 CUPSWrapper();
87 ~CUPSWrapper();
89 bool isValid();
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,
102 const char* pTitle,
103 int nOptions,
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 ); }
142 using namespace psp;
143 using namespace osl;
144 using namespace rtl;
147 * CUPSWrapper class
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" );
156 #endif
157 return pSym;
160 CUPSWrapper::CUPSWrapper()
161 : m_pLib( NULL ),
162 m_bPPDThreadRunning( false )
164 #ifdef ENABLE_CUPS
165 OUString aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME ) );
166 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
167 if( ! m_pLib )
169 aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) );
170 m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
172 #endif
174 if( ! m_pLib )
176 #if OSL_DEBUG_LEVEL > 1
177 fprintf( stderr, "no cups library found\n" );
178 #endif
179 return;
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" );
213 if( ! (
214 m_pcupsPrintFile &&
215 m_pcupsGetDests &&
216 m_pcupsSetDests &&
217 m_pcupsFreeDests &&
218 m_pcupsGetPPD &&
219 m_pcupsMarkOptions &&
220 m_pcupsAddOption &&
221 m_pcupsServer &&
222 m_pcupsUser &&
223 m_pcupsSetPasswordCB &&
224 m_pcupsSetUser &&
225 m_pcupsFreeOptions &&
226 m_pppdOpenFile &&
227 m_pppdClose &&
228 m_pcupsGetOption
231 osl_unloadModule( m_pLib );
232 m_pLib = NULL;
236 CUPSWrapper::~CUPSWrapper()
238 if( m_pLib )
239 osl_unloadModule( m_pLib );
242 bool CUPSWrapper::isValid()
244 return m_pLib != NULL;
247 typedef const char*(*PPDFunction)(const char*);
248 struct GetPPDAttribs
250 PPDFunction m_pFunction;
251 osl::Condition m_aCondition;
252 OString m_aParameter;
253 OString m_aResult;
254 oslThread m_aThread;
255 int m_nRefs;
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 )
266 m_nRefs = 2;
267 m_aCondition.reset();
270 ~GetPPDAttribs()
272 if( m_aResult.getLength() )
273 unlink( m_aResult.getStr() );
276 void unref()
278 if( --m_nRefs == 0 )
280 *m_pResetRunning = false;
281 delete this;
285 void executeCall()
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 );
291 m_aResult = aResult;
292 m_aCondition.set();
293 unref();
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
307 #endif
309 m_pSyncMutex->acquire();
311 OString aRetval = m_aResult;
312 m_aResult = OString();
313 unref();
315 return aRetval;
319 extern "C" {
320 static void getPPDWorker(void* pData)
322 GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData;
323 pAttribs->executeCall();
327 OString CUPSWrapper::cupsGetPPD( const char* pPrinter )
329 OString aResult;
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,
337 pPrinter,
338 &m_bPPDThreadRunning,
339 &m_aGetPPDMutex );
341 oslThread aThread = osl_createThread( getPPDWorker, pAttribs );
343 TimeValue aValue;
344 aValue.Seconds = 5;
345 aValue.Nanosec = 0;
347 // NOTE: waitResult release and acquires the GetPPD mutex
348 aResult = pAttribs->waitResult( &aValue );
349 osl_destroyThread( aThread );
351 m_aGetPPDMutex.release();
353 return aResult;
356 #ifdef ENABLE_CUPS
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 );
364 return pRet;
366 #endif
369 * CUPSManager class
372 CUPSManager* CUPSManager::tryLoadCUPS()
374 CUPSManager* pManager = NULL;
375 #ifdef ENABLE_CUPS
376 static const char* pEnv = getenv( "SAL_DISABLE_CUPS" );
378 if( ! pEnv || ! *pEnv )
380 // try to load CUPS
381 CUPSWrapper* pWrapper = new CUPSWrapper();
382 if( pWrapper->isValid() )
383 pManager = new CUPSManager( pWrapper );
384 else
385 delete pWrapper;
387 #endif
388 return pManager;
391 extern "C"
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 ),
402 m_nDests( 0 ),
403 m_pDests( NULL ),
404 m_bNewDests( false )
406 m_aDestThread = osl_createThread( run_dest_thread_stub, this );
409 CUPSManager::~CUPSManager()
411 if( m_aDestThread )
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;
431 extern "C"
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" );
444 #endif
445 int nDests = 0;
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;
455 act.sa_flags = 0;
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" );
468 #endif
470 osl::MutexGuard aGuard( m_aCUPSMutex );
471 m_nDests = nDests;
472 m_pDests = pDests;
473 m_bNewDests = true;
474 #if OSL_DEBUG_LEVEL > 1
475 fprintf( stderr, "finished cupsGetDests\n" );
476 #endif
478 else
480 #if OSL_DEBUG_LEVEL > 1
481 fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" );
482 #endif
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();
499 #ifdef ENABLE_CUPS
500 // check whether thread has completed
501 // if not behave like old printing system
502 osl::MutexGuard aGuard( m_aCUPSMutex );
504 if( ! m_bNewDests )
505 return;
507 // dest thread has run, clean up
508 if( m_aDestThread )
510 osl_joinWithThread( m_aDestThread );
511 osl_destroyThread( m_aDestThread );
512 m_aDestThread = NULL;
514 m_bNewDests = false;
516 // clear old stuff
517 m_aCUPSDestMap.clear();
519 if( ! (m_nDests && m_pDests ) )
520 return;
522 if( isCUPSDisabled() )
523 return;
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",
532 pDest->num_options,
533 pDest->options );
534 if( pOpt )
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
546 while( nPrinter-- )
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
586 // behaviour
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() )
609 continue;
611 if( it->second.m_aInfo.m_aFeatures.getLength() > 0 )
612 continue;
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
625 #ifdef 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 ) );
638 if( pKey )
640 const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) );
641 if( pValue )
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 );
648 #endif
651 #if OSL_DEBUG_LEVEL > 1
652 else
653 fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice );
654 #endif
656 #if OSL_DEBUG_LEVEL > 1
657 else
658 fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword );
659 #endif
661 #if OSL_DEBUG_LEVEL > 1
662 else
663 fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword );
664 #endif
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;
680 OUString aPrinter;
682 if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 )
683 aPrinter = rPrinter.copy( 5 );
684 else
685 aPrinter = rPrinter;
687 #ifdef ENABLE_CUPS
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() );
700 #endif
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() );
707 if( pPPD )
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 );
722 #endif
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
729 // may overwrite it
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;
737 // clean up the mess
738 m_pCUPSWrapper->ppdClose( pPPD );
740 #if OSL_DEBUG_LEVEL > 1
741 else
742 fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" );
743 #endif
745 // remove temporary PPD file
746 unlink( aPPDFile.getStr() );
748 #if OSL_DEBUG_LEVEL > 1
749 else
750 fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" );
751 #endif
753 #if OSL_DEBUG_LEVEL > 1
754 else
755 fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() );
756 #endif
758 m_aCUPSMutex.release();
760 #if OSL_DEBUG_LEVEL >1
761 else
762 fprintf( stderr, "could not acquire CUPS mutex !!!\n" );
763 #endif
764 #endif // ENABLE_CUPS
766 if( ! pNewParser )
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 );
777 return pNewParser;
780 void CUPSManager::setupJobContextData(
781 JobData&
782 #ifdef ENABLE_CUPS
783 rData
784 #endif
787 #ifdef ENABLE_CUPS
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() );
800 #endif
801 return;
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 )
812 OUString aPrinter;
813 if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 )
814 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 );
815 else
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;
823 #endif
826 FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
828 if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
829 return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
831 #ifdef ENABLE_CUPS
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" );
837 if( fp )
838 m_aSpoolFiles[fp] = aSysFile;
840 return fp;
841 #else
842 return NULL;
843 #endif
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
854 rNumOptions = 0;
855 *rOptions = NULL;
856 int i;
858 // emit features ordered to OrderDependency
859 // ignore features that are set to default
861 // sanity check
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 )
886 int nJobID = 0;
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 );
895 #ifdef ENABLE_CUPS
896 std::hash_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
897 if( it != m_aSpoolFiles.end() )
899 fclose( pFile );
900 rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
902 // setup cups options
903 int nNumOptions = 0;
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,
909 it->second.getStr(),
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",
914 pDest->name,
915 it->second.getStr(),
916 OUStringToOString( rJobTitle, aEnc ).getStr(),
917 nNumOptions,
918 pOptions,
919 nJobID
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() );
927 #endif
929 unlink( it->second.getStr() );
930 m_aSpoolFiles.erase( pFile );
931 if( pOptions )
932 m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions );
934 #endif // ENABLE_CUPS
936 return nJobID;
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;
948 if( bWait )
950 if( m_aDestThread )
952 // initial asynchronous detection still running
953 #if OSL_DEBUG_LEVEL > 1
954 fprintf( stderr, "syncing cups discovery thread\n" );
955 #endif
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" );
961 #endif
963 else
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 );
976 m_nDests = 0;
977 m_pDests = NULL;
978 runDests();
981 if( m_aCUPSMutex.tryToAcquire() )
983 bChanged = m_bNewDests;
984 m_aCUPSMutex.release();
987 if( ! bChanged )
989 bChanged = PrinterInfoManager::checkPrintersChanged( bWait );
990 // #i54375# ensure new merging with CUPS list in :initialize
991 if( bChanged )
992 m_bNewDests = true;
995 if( bChanged )
996 initialize();
998 return bChanged;
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
1007 return false;
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() )
1015 return false;
1016 return PrinterInfoManager::removePrinter( rName, bCheck );
1019 bool CUPSManager::setDefaultPrinter( const OUString& rName )
1021 bool bSuccess = false;
1022 #ifdef ENABLE_CUPS
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();
1034 bSuccess = true;
1036 else
1037 #endif
1038 bSuccess = PrinterInfoManager::setDefaultPrinter( rName );
1040 return bSuccess;
1043 bool CUPSManager::writePrinterConfig()
1045 #ifdef ENABLE_CUPS
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() )
1055 continue;
1057 if( ! prt->second.m_bModified )
1058 continue;
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;
1109 #ifdef ENABLE_CUPS
1110 OUString aLib = OUString::createFromAscii( _XSALSET_LIBNAME );
1111 oslModule pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY );
1112 if( pLib )
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 );
1117 if( getpw )
1119 osl::MutexGuard aGuard( m_aCUPSMutex );
1121 OString aUser = m_pCUPSWrapper->cupsUser();
1122 OString aServer = m_pCUPSWrapper->cupsServer();
1123 OString aPassword;
1124 if( getpw( aServer, aUser, aPassword ) )
1126 m_aPassword = aPassword;
1127 m_aUser = aUser;
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() );
1136 #endif
1137 #endif // ENABLE_CUPS
1139 return pRet;