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 unknwon 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