Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <cups/cups.h>
22 : #include <cups/http.h>
23 : #include <cups/ipp.h>
24 : #include <cups/ppd.h>
25 :
26 : #include <unistd.h>
27 :
28 : #include "cupsmgr.hxx"
29 :
30 : #include "osl/thread.h"
31 : #include "osl/diagnose.h"
32 : #include "osl/conditn.hxx"
33 :
34 : #include "rtl/ustrbuf.hxx"
35 :
36 : #include <officecfg/Office/Common.hxx>
37 :
38 : #include <algorithm>
39 :
40 : using namespace psp;
41 : using namespace osl;
42 :
43 :
44 : struct GetPPDAttribs
45 : {
46 : osl::Condition m_aCondition;
47 : OString m_aParameter;
48 : OString m_aResult;
49 : int m_nRefs;
50 : bool* m_pResetRunning;
51 : osl::Mutex* m_pSyncMutex;
52 :
53 0 : GetPPDAttribs( const char * m_pParameter,
54 : bool* pResetRunning, osl::Mutex* pSyncMutex )
55 : : m_aParameter( m_pParameter ),
56 : m_pResetRunning( pResetRunning ),
57 0 : m_pSyncMutex( pSyncMutex )
58 : {
59 0 : m_nRefs = 2;
60 0 : m_aCondition.reset();
61 0 : }
62 :
63 0 : ~GetPPDAttribs()
64 0 : {
65 0 : if( !m_aResult.isEmpty() )
66 0 : unlink( m_aResult.getStr() );
67 0 : }
68 :
69 0 : void unref()
70 : {
71 0 : if( --m_nRefs == 0 )
72 : {
73 0 : *m_pResetRunning = false;
74 0 : delete this;
75 : }
76 0 : }
77 :
78 0 : void executeCall()
79 : {
80 : // This CUPS method is not at all thread-safe we need
81 : // to dup the pointer to a static buffer it returns ASAP
82 0 : OString aResult = cupsGetPPD(m_aParameter.getStr());
83 0 : MutexGuard aGuard( *m_pSyncMutex );
84 0 : m_aResult = aResult;
85 0 : m_aCondition.set();
86 0 : unref();
87 0 : }
88 :
89 0 : OString waitResult( TimeValue *pDelay )
90 : {
91 0 : m_pSyncMutex->release();
92 :
93 0 : if (m_aCondition.wait( pDelay ) != Condition::result_ok
94 : )
95 : {
96 : #if OSL_DEBUG_LEVEL > 1
97 : fprintf( stderr, "cupsGetPPD %s timed out\n", m_aParameter.getStr() );
98 : #endif
99 : }
100 0 : m_pSyncMutex->acquire();
101 :
102 0 : OString aRetval = m_aResult;
103 0 : m_aResult = OString();
104 0 : unref();
105 :
106 0 : return aRetval;
107 : }
108 : };
109 :
110 : extern "C" {
111 0 : static void getPPDWorker(void* pData)
112 : {
113 0 : GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData;
114 0 : pAttribs->executeCall();
115 0 : }
116 : }
117 :
118 0 : OString CUPSManager::threadedCupsGetPPD( const char* pPrinter )
119 : {
120 0 : OString aResult;
121 :
122 0 : m_aGetPPDMutex.acquire();
123 : // if one thread hangs in cupsGetPPD already, don't start another
124 0 : if( ! m_bPPDThreadRunning )
125 : {
126 0 : m_bPPDThreadRunning = true;
127 : GetPPDAttribs* pAttribs = new GetPPDAttribs( pPrinter,
128 : &m_bPPDThreadRunning,
129 0 : &m_aGetPPDMutex );
130 :
131 0 : oslThread aThread = osl_createThread( getPPDWorker, pAttribs );
132 :
133 : TimeValue aValue;
134 0 : aValue.Seconds = 5;
135 0 : aValue.Nanosec = 0;
136 :
137 : // NOTE: waitResult release and acquires the GetPPD mutex
138 0 : aResult = pAttribs->waitResult( &aValue );
139 0 : osl_destroyThread( aThread );
140 : }
141 0 : m_aGetPPDMutex.release();
142 :
143 0 : return aResult;
144 : }
145 :
146 0 : static const char* setPasswordCallback( const char* pIn )
147 : {
148 0 : const char* pRet = NULL;
149 :
150 0 : PrinterInfoManager& rMgr = PrinterInfoManager::get();
151 0 : if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check
152 0 : pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn );
153 0 : return pRet;
154 : }
155 :
156 : /*
157 : * CUPSManager class
158 : */
159 :
160 55 : CUPSManager* CUPSManager::tryLoadCUPS()
161 : {
162 55 : CUPSManager* pManager = NULL;
163 55 : static const char* pEnv = getenv("SAL_DISABLE_CUPS");
164 :
165 55 : if (!pEnv || !*pEnv)
166 55 : pManager = new CUPSManager();
167 55 : return pManager;
168 : }
169 :
170 : extern "C"
171 : {
172 55 : static void run_dest_thread_stub( void* pThis )
173 : {
174 55 : CUPSManager::runDestThread( pThis );
175 55 : }
176 : }
177 :
178 55 : CUPSManager::CUPSManager() :
179 : PrinterInfoManager( CUPS ),
180 : m_nDests( 0 ),
181 : m_pDests( NULL ),
182 : m_bNewDests( false ),
183 55 : m_bPPDThreadRunning( false )
184 : {
185 55 : m_aDestThread = osl_createThread( run_dest_thread_stub, this );
186 55 : }
187 :
188 0 : CUPSManager::~CUPSManager()
189 : {
190 0 : if( m_aDestThread )
191 : {
192 : // if the thread is still running here, then
193 : // cupsGetDests is hung; terminate the thread instead of joining
194 0 : osl_terminateThread( m_aDestThread );
195 0 : osl_destroyThread( m_aDestThread );
196 : }
197 :
198 0 : if (m_nDests && m_pDests)
199 0 : cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests );
200 0 : }
201 :
202 55 : void CUPSManager::runDestThread( void* pThis )
203 : {
204 55 : ((CUPSManager*)pThis)->runDests();
205 55 : }
206 :
207 62 : void CUPSManager::runDests()
208 : {
209 : #if OSL_DEBUG_LEVEL > 1
210 : fprintf( stderr, "starting cupsGetDests\n" );
211 : #endif
212 62 : cups_dest_t* pDests = NULL;
213 :
214 : // n#722902 - do a fast-failing check for cups working *at all* first
215 : http_t* p_http;
216 62 : if( (p_http=httpConnectEncrypt(
217 : cupsServer(),
218 : ippPort(),
219 62 : cupsEncryption())) != NULL )
220 : {
221 : // neat, cups is up, clean up the canary
222 62 : httpClose(p_http);
223 :
224 62 : int nDests = cupsGetDests( &pDests );
225 : #if OSL_DEBUG_LEVEL > 1
226 : fprintf( stderr, "came out of cupsGetDests\n" );
227 : #endif
228 :
229 62 : osl::MutexGuard aGuard( m_aCUPSMutex );
230 62 : m_nDests = nDests;
231 62 : m_pDests = pDests;
232 62 : m_bNewDests = true;
233 : #if OSL_DEBUG_LEVEL > 1
234 : fprintf( stderr, "finished cupsGetDests\n" );
235 : #endif
236 : }
237 62 : }
238 :
239 86 : void CUPSManager::initialize()
240 : {
241 : // get normal printers, clear printer list
242 86 : PrinterInfoManager::initialize();
243 :
244 : // check whether thread has completed
245 : // if not behave like old printing system
246 86 : osl::MutexGuard aGuard( m_aCUPSMutex );
247 :
248 86 : if( ! m_bNewDests )
249 27 : return;
250 :
251 : // dest thread has run, clean up
252 59 : if( m_aDestThread )
253 : {
254 28 : osl_joinWithThread( m_aDestThread );
255 28 : osl_destroyThread( m_aDestThread );
256 28 : m_aDestThread = NULL;
257 : }
258 59 : m_bNewDests = false;
259 :
260 : // clear old stuff
261 59 : m_aCUPSDestMap.clear();
262 :
263 59 : if( ! (m_nDests && m_pDests ) )
264 59 : return;
265 :
266 0 : if( isCUPSDisabled() )
267 0 : return;
268 :
269 : // check for CUPS server(?) > 1.2
270 : // since there is no API to query, check for options that were
271 : // introduced in dests with 1.2
272 : // this is needed to check for %%IncludeFeature support
273 : // (#i65684#, #i65491#)
274 0 : bool bUsePDF = false;
275 0 : cups_dest_t* pDest = ((cups_dest_t*)m_pDests);
276 : const char* pOpt = cupsGetOption( "printer-info",
277 : pDest->num_options,
278 0 : pDest->options );
279 0 : if( pOpt )
280 : {
281 0 : m_bUseIncludeFeature = true;
282 0 : bUsePDF = officecfg::Office::Common::Print::Option::Printer::PDFAsStandardPrintJobFormat::get();
283 : }
284 :
285 0 : m_aGlobalDefaults.setDefaultBackend(bUsePDF);
286 :
287 : // do not send include JobPatch; CUPS will insert that itself
288 : // TODO: currently unknown which versions of CUPS insert JobPatches
289 : // so currently it is assumed CUPS = don't insert JobPatch files
290 0 : m_bUseJobPatch = false;
291 :
292 0 : rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
293 0 : int nPrinter = m_nDests;
294 :
295 : // reset global default PPD options; these are queried on demand from CUPS
296 0 : m_aGlobalDefaults.m_pParser = NULL;
297 0 : m_aGlobalDefaults.m_aContext = PPDContext();
298 :
299 : // add CUPS printers, should there be a printer
300 : // with the same name as a CUPS printer, overwrite it
301 0 : while( nPrinter-- )
302 : {
303 0 : pDest = ((cups_dest_t*)m_pDests)+nPrinter;
304 0 : OUString aPrinterName = OStringToOUString( pDest->name, aEncoding );
305 0 : if( pDest->instance && *pDest->instance )
306 : {
307 0 : OUStringBuffer aBuf( 256 );
308 0 : aBuf.append( aPrinterName );
309 0 : aBuf.append( sal_Unicode( '/' ) );
310 0 : aBuf.append( OStringToOUString( pDest->instance, aEncoding ) );
311 0 : aPrinterName = aBuf.makeStringAndClear();
312 : }
313 :
314 : // initialize printer with possible configuration from psprint.conf
315 0 : bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end();
316 0 : Printer aPrinter = m_aPrinters[ aPrinterName ];
317 0 : if( bSetToGlobalDefaults )
318 0 : aPrinter.m_aInfo = m_aGlobalDefaults;
319 0 : aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
320 0 : if( pDest->is_default )
321 0 : m_aDefaultPrinter = aPrinterName;
322 :
323 0 : for( int k = 0; k < pDest->num_options; k++ )
324 : {
325 0 : if(!strcmp(pDest->options[k].name, "printer-info"))
326 0 : aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding);
327 0 : if(!strcmp(pDest->options[k].name, "printer-location"))
328 0 : aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding);
329 : }
330 :
331 :
332 0 : OUStringBuffer aBuf( 256 );
333 0 : aBuf.appendAscii( "CUPS:" );
334 0 : aBuf.append( aPrinterName );
335 : // note: the parser that goes with the PrinterInfo
336 : // is created implicitly by the JobData::operator=()
337 : // when it detects the NULL ptr m_pParser.
338 : // if we wanted to fill in the parser here this
339 : // would mean we'd have to download PPDs for each and
340 : // every printer - which would be really bad runtime
341 : // behaviour
342 0 : aPrinter.m_aInfo.m_pParser = NULL;
343 0 : aPrinter.m_aInfo.m_aContext.setParser( NULL );
344 0 : boost::unordered_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName );
345 0 : if( c_it != m_aDefaultContexts.end() )
346 : {
347 0 : aPrinter.m_aInfo.m_pParser = c_it->second.getParser();
348 0 : aPrinter.m_aInfo.m_aContext = c_it->second;
349 : }
350 0 : aPrinter.m_aInfo.setDefaultBackend(bUsePDF);
351 0 : aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear();
352 0 : aPrinter.m_bModified = false;
353 :
354 0 : m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter;
355 0 : m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter;
356 0 : }
357 :
358 : // remove everything that is not a CUPS printer and not
359 : // a special purpose printer (PDF, Fax)
360 0 : std::list< OUString > aRemovePrinters;
361 0 : for( boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin();
362 0 : it != m_aPrinters.end(); ++it )
363 : {
364 0 : if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() )
365 0 : continue;
366 :
367 0 : if( !it->second.m_aInfo.m_aFeatures.isEmpty() )
368 0 : continue;
369 0 : aRemovePrinters.push_back( it->first );
370 : }
371 0 : while( aRemovePrinters.begin() != aRemovePrinters.end() )
372 : {
373 0 : m_aPrinters.erase( aRemovePrinters.front() );
374 0 : aRemovePrinters.pop_front();
375 : }
376 :
377 0 : cupsSetPasswordCB( setPasswordCallback );
378 : }
379 :
380 0 : static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext )
381 : {
382 0 : rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
383 0 : for( int i = 0; i < pPPDGroup->num_options; i++ )
384 : {
385 0 : ppd_option_t* pOption = pPPDGroup->options + i;
386 0 : for( int n = 0; n < pOption->num_choices; n++ )
387 : {
388 0 : ppd_choice_t* pChoice = pOption->choices + n;
389 0 : if( pChoice->marked )
390 : {
391 0 : const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) );
392 0 : if( pKey )
393 : {
394 0 : const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) );
395 0 : if( pValue )
396 : {
397 0 : if( pValue != pKey->getDefaultValue() )
398 : {
399 0 : rContext.setValue( pKey, pValue, true );
400 : #if OSL_DEBUG_LEVEL > 1
401 : fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice );
402 : #endif
403 :
404 : }
405 : #if OSL_DEBUG_LEVEL > 1
406 : else
407 : fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice );
408 : #endif
409 : }
410 : #if OSL_DEBUG_LEVEL > 1
411 : else
412 : fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword );
413 : #endif
414 : }
415 : #if OSL_DEBUG_LEVEL > 1
416 : else
417 : fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword );
418 : #endif
419 : }
420 : }
421 : }
422 :
423 : // recurse through subgroups
424 0 : for( int g = 0; g < pPPDGroup->num_subgroups; g++ )
425 : {
426 0 : updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext );
427 : }
428 0 : }
429 :
430 0 : const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter )
431 : {
432 0 : const PPDParser* pNewParser = NULL;
433 0 : OUString aPrinter;
434 :
435 0 : if( rPrinter.startsWith("CUPS:") )
436 0 : aPrinter = rPrinter.copy( 5 );
437 : else
438 0 : aPrinter = rPrinter;
439 :
440 0 : if( m_aCUPSMutex.tryToAcquire() )
441 : {
442 0 : if( m_nDests && m_pDests && ! isCUPSDisabled() )
443 : {
444 : boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
445 0 : m_aCUPSDestMap.find( aPrinter );
446 0 : if( dest_it != m_aCUPSDestMap.end() )
447 : {
448 0 : cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
449 0 : OString aPPDFile = threadedCupsGetPPD( pDest->name );
450 : #if OSL_DEBUG_LEVEL > 1
451 : fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() );
452 : #endif
453 0 : if( !aPPDFile.isEmpty() )
454 : {
455 0 : rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
456 0 : OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) );
457 : // update the printer info with context information
458 0 : ppd_file_t* pPPD = ppdOpenFile( aPPDFile.getStr() );
459 0 : if( pPPD )
460 : {
461 : // create the new parser
462 0 : PPDParser* pCUPSParser = new PPDParser( aFileName );
463 0 : pCUPSParser->m_aFile = rPrinter;
464 0 : pNewParser = pCUPSParser;
465 :
466 0 : /*int nConflicts =*/ cupsMarkOptions( pPPD, pDest->num_options, pDest->options );
467 : #if OSL_DEBUG_LEVEL > 1
468 : fprintf( stderr, "processing the following options for printer %s (instance %s):\n",
469 : pDest->name, pDest->instance );
470 : for( int k = 0; k < pDest->num_options; k++ )
471 : fprintf( stderr, " \"%s\" = \"%s\"\n",
472 : pDest->options[k].name,
473 : pDest->options[k].value );
474 : #endif
475 0 : PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
476 :
477 : // remember the default context for later use
478 0 : PPDContext& rContext = m_aDefaultContexts[ aPrinter ];
479 0 : rContext.setParser( pNewParser );
480 : // set system default paper; printer CUPS PPD options
481 : // may overwrite it
482 0 : setDefaultPaper( rContext );
483 0 : for( int i = 0; i < pPPD->num_groups; i++ )
484 0 : updatePrinterContextInfo( pPPD->groups + i, rContext );
485 :
486 0 : rInfo.m_pParser = pNewParser;
487 0 : rInfo.m_aContext = rContext;
488 :
489 : // clean up the mess
490 0 : ppdClose( pPPD );
491 : }
492 : #if OSL_DEBUG_LEVEL > 1
493 : else
494 : fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" );
495 : #endif
496 :
497 : // remove temporary PPD file
498 0 : unlink( aPPDFile.getStr() );
499 0 : }
500 : #if OSL_DEBUG_LEVEL > 1
501 : else
502 : fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" );
503 : #endif
504 : }
505 : #if OSL_DEBUG_LEVEL > 1
506 : else
507 : fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() );
508 : #endif
509 : }
510 0 : m_aCUPSMutex.release();
511 : }
512 : #if OSL_DEBUG_LEVEL >1
513 : else
514 : fprintf( stderr, "could not acquire CUPS mutex !!!\n" );
515 : #endif
516 :
517 0 : if( ! pNewParser )
518 : {
519 : // get the default PPD
520 0 : pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) );
521 :
522 0 : PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
523 :
524 0 : rInfo.m_pParser = pNewParser;
525 0 : rInfo.m_aContext.setParser( pNewParser );
526 : }
527 :
528 0 : return pNewParser;
529 : }
530 :
531 0 : void CUPSManager::setupJobContextData( JobData& rData )
532 : {
533 : boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
534 0 : m_aCUPSDestMap.find( rData.m_aPrinterName );
535 :
536 0 : if( dest_it == m_aCUPSDestMap.end() )
537 0 : return PrinterInfoManager::setupJobContextData( rData );
538 :
539 : boost::unordered_map< OUString, Printer, OUStringHash >::iterator p_it =
540 0 : m_aPrinters.find( rData.m_aPrinterName );
541 0 : if( p_it == m_aPrinters.end() ) // huh ?
542 : {
543 : #if OSL_DEBUG_LEVEL > 1
544 : fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() );
545 : #endif
546 0 : return;
547 : }
548 :
549 0 : if( p_it->second.m_aInfo.m_pParser == NULL )
550 : {
551 : // in turn calls createCUPSParser
552 : // which updates the printer info
553 0 : p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName );
554 : }
555 0 : if( p_it->second.m_aInfo.m_aContext.getParser() == NULL )
556 : {
557 0 : OUString aPrinter;
558 0 : if( p_it->second.m_aInfo.m_aDriverName.startsWith("CUPS:") )
559 0 : aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 );
560 : else
561 0 : aPrinter = p_it->second.m_aInfo.m_aDriverName;
562 :
563 0 : p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ];
564 : }
565 :
566 0 : rData.m_pParser = p_it->second.m_aInfo.m_pParser;
567 0 : rData.m_aContext = p_it->second.m_aInfo.m_aContext;
568 : }
569 :
570 0 : FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
571 : {
572 : OSL_TRACE( "endSpool: %s, %s",
573 : OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(),
574 : bQuickCommand ? "true" : "false" );
575 :
576 0 : if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
577 : {
578 : OSL_TRACE( "defer to PrinterInfoManager::startSpool" );
579 0 : return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
580 : }
581 :
582 0 : OUString aTmpURL, aTmpFile;
583 0 : osl_createTempFile( NULL, NULL, &aTmpURL.pData );
584 0 : osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData );
585 0 : OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() );
586 0 : FILE* fp = fopen( aSysFile.getStr(), "w" );
587 0 : if( fp )
588 0 : m_aSpoolFiles[fp] = aSysFile;
589 :
590 0 : return fp;
591 : }
592 :
593 : struct less_ppd_key : public ::std::binary_function<double, double, bool>
594 : {
595 0 : bool operator()(const PPDKey* left, const PPDKey* right)
596 0 : { return left->getOrderDependency() < right->getOrderDependency(); }
597 : };
598 :
599 0 : void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const
600 : {
601 0 : rNumOptions = 0;
602 0 : *rOptions = NULL;
603 :
604 : // emit features ordered to OrderDependency
605 : // ignore features that are set to default
606 :
607 : // sanity check
608 0 : if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser )
609 : {
610 : int i;
611 0 : int nKeys = rJob.m_aContext.countValuesModified();
612 0 : ::std::vector< const PPDKey* > aKeys( nKeys );
613 0 : for( i = 0; i < nKeys; i++ )
614 0 : aKeys[i] = rJob.m_aContext.getModifiedKey( i );
615 0 : ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
616 :
617 0 : for( i = 0; i < nKeys; i++ )
618 : {
619 0 : const PPDKey* pKey = aKeys[i];
620 0 : const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
621 0 : if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() )
622 : {
623 0 : OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US );
624 0 : OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US );
625 0 : rNumOptions = cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions );
626 : }
627 0 : }
628 : }
629 :
630 0 : if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 )
631 : {
632 0 : OString aVal( OString::valueOf( sal_Int32( rJob.m_nCopies ) ) );
633 0 : rNumOptions = cupsAddOption( "copies", aVal.getStr(), rNumOptions, (cups_option_t**)rOptions );
634 : }
635 0 : if( ! bBanner )
636 : {
637 0 : rNumOptions = cupsAddOption( "job-sheets", "none", rNumOptions, (cups_option_t**)rOptions );
638 : }
639 0 : }
640 :
641 0 : int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner )
642 : {
643 : OSL_TRACE( "endSpool: %s, %s, copy count = %d",
644 : OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(),
645 : OUStringToOString( rJobTitle, RTL_TEXTENCODING_UTF8 ).getStr(),
646 : rDocumentJobData.m_nCopies
647 : );
648 :
649 0 : int nJobID = 0;
650 :
651 0 : osl::MutexGuard aGuard( m_aCUPSMutex );
652 :
653 : boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
654 0 : m_aCUPSDestMap.find( rPrintername );
655 0 : if( dest_it == m_aCUPSDestMap.end() )
656 : {
657 : OSL_TRACE( "defer to PrinterInfoManager::endSpool" );
658 0 : return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner );
659 : }
660 :
661 0 : boost::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
662 0 : if( it != m_aSpoolFiles.end() )
663 : {
664 0 : fclose( pFile );
665 0 : rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
666 :
667 : // setup cups options
668 0 : int nNumOptions = 0;
669 0 : cups_option_t* pOptions = NULL;
670 0 : getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions );
671 :
672 0 : cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
673 : nJobID = cupsPrintFile( pDest->name,
674 0 : it->second.getStr(),
675 : OUStringToOString( rJobTitle, aEnc ).getStr(),
676 0 : nNumOptions, pOptions );
677 : #if OSL_DEBUG_LEVEL > 1
678 : fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n",
679 : pDest->name,
680 : it->second.getStr(),
681 : OUStringToOString( rJobTitle, aEnc ).getStr(),
682 : nNumOptions,
683 : pOptions,
684 : nJobID
685 : );
686 : for( int n = 0; n < nNumOptions; n++ )
687 : fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value );
688 : OString aCmd( "cp " );
689 : aCmd = aCmd + it->second;
690 : aCmd = aCmd + OString( " $HOME/cupsprint.ps" );
691 : system( aCmd.getStr() );
692 : #endif
693 :
694 0 : unlink( it->second.getStr() );
695 0 : m_aSpoolFiles.erase( pFile );
696 0 : if( pOptions )
697 0 : cupsFreeOptions( nNumOptions, pOptions );
698 : }
699 :
700 0 : return nJobID;
701 : }
702 :
703 :
704 0 : void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
705 : {
706 0 : PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo );
707 0 : }
708 :
709 31 : bool CUPSManager::checkPrintersChanged( bool bWait )
710 : {
711 31 : bool bChanged = false;
712 31 : if( bWait )
713 : {
714 31 : if( m_aDestThread )
715 : {
716 : // initial asynchronous detection still running
717 : #if OSL_DEBUG_LEVEL > 1
718 : fprintf( stderr, "syncing cups discovery thread\n" );
719 : #endif
720 24 : osl_joinWithThread( m_aDestThread );
721 24 : osl_destroyThread( m_aDestThread );
722 24 : m_aDestThread = NULL;
723 : #if OSL_DEBUG_LEVEL > 1
724 : fprintf( stderr, "done: syncing cups discovery thread\n" );
725 : #endif
726 : }
727 : else
728 : {
729 : // #i82321# check for cups printer updates
730 : // with this change the whole asynchronous detection in a thread is
731 : // almost useless. The only relevance left is for some stalled systems
732 : // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION
733 : // (see vcl/unx/source/gdi/salprnpsp.cxx)
734 : // so that checkPrintersChanged( true ) will never be called
735 :
736 : // there is no way to query CUPS whether the printer list has changed
737 : // so get the dest list anew
738 7 : if( m_nDests && m_pDests )
739 0 : cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests );
740 7 : m_nDests = 0;
741 7 : m_pDests = NULL;
742 7 : runDests();
743 : }
744 : }
745 31 : if( m_aCUPSMutex.tryToAcquire() )
746 : {
747 31 : bChanged = m_bNewDests;
748 31 : m_aCUPSMutex.release();
749 : }
750 :
751 31 : if( ! bChanged )
752 : {
753 0 : bChanged = PrinterInfoManager::checkPrintersChanged( bWait );
754 : // #i54375# ensure new merging with CUPS list in :initialize
755 0 : if( bChanged )
756 0 : m_bNewDests = true;
757 : }
758 :
759 31 : if( bChanged )
760 31 : initialize();
761 :
762 31 : return bChanged;
763 : }
764 :
765 0 : bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver )
766 : {
767 : // don't touch the CUPS printers
768 0 : if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ||
769 0 : rDriver.startsWith("CUPS:")
770 : )
771 0 : return false;
772 0 : return PrinterInfoManager::addPrinter( rName, rDriver );
773 : }
774 :
775 0 : bool CUPSManager::removePrinter( const OUString& rName, bool bCheck )
776 : {
777 : // don't touch the CUPS printers
778 0 : if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() )
779 0 : return false;
780 0 : return PrinterInfoManager::removePrinter( rName, bCheck );
781 : }
782 :
783 0 : bool CUPSManager::setDefaultPrinter( const OUString& rName )
784 : {
785 0 : bool bSuccess = false;
786 : boost::unordered_map< OUString, int, OUStringHash >::iterator nit =
787 0 : m_aCUPSDestMap.find( rName );
788 0 : if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() )
789 : {
790 0 : cups_dest_t* pDests = (cups_dest_t*)m_pDests;
791 0 : for( int i = 0; i < m_nDests; i++ )
792 0 : pDests[i].is_default = 0;
793 0 : pDests[ nit->second ].is_default = 1;
794 0 : cupsSetDests( m_nDests, (cups_dest_t*)m_pDests );
795 0 : m_aDefaultPrinter = rName;
796 0 : m_aCUPSMutex.release();
797 0 : bSuccess = true;
798 : }
799 : else
800 0 : bSuccess = PrinterInfoManager::setDefaultPrinter( rName );
801 :
802 0 : return bSuccess;
803 : }
804 :
805 0 : bool CUPSManager::writePrinterConfig()
806 : {
807 0 : bool bDestModified = false;
808 0 : rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
809 :
810 0 : for( boost::unordered_map< OUString, Printer, OUStringHash >::iterator prt =
811 0 : m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt )
812 : {
813 : boost::unordered_map< OUString, int, OUStringHash >::iterator nit =
814 0 : m_aCUPSDestMap.find( prt->first );
815 0 : if( nit == m_aCUPSDestMap.end() )
816 0 : continue;
817 :
818 0 : if( ! prt->second.m_bModified )
819 0 : continue;
820 :
821 0 : if( m_aCUPSMutex.tryToAcquire() )
822 : {
823 0 : bDestModified = true;
824 0 : cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second;
825 0 : PrinterInfo& rInfo = prt->second.m_aInfo;
826 :
827 : // create new option list
828 0 : int nNewOptions = 0;
829 0 : cups_option_t* pNewOptions = NULL;
830 0 : int nValues = rInfo.m_aContext.countValuesModified();
831 0 : for( int i = 0; i < nValues; i++ )
832 : {
833 0 : const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i );
834 0 : const PPDValue* pValue = rInfo.m_aContext.getValue( pKey );
835 0 : if( pKey && pValue ) // sanity check
836 : {
837 0 : OString aName = OUStringToOString( pKey->getKey(), aEncoding );
838 0 : OString aValue = OUStringToOString( pValue->m_aOption, aEncoding );
839 0 : nNewOptions = cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions );
840 : }
841 : }
842 : // set PPD options on CUPS dest
843 0 : cupsFreeOptions( pDest->num_options, pDest->options );
844 0 : pDest->num_options = nNewOptions;
845 0 : pDest->options = pNewOptions;
846 0 : m_aCUPSMutex.release();
847 : }
848 : }
849 0 : if( bDestModified && m_aCUPSMutex.tryToAcquire() )
850 : {
851 0 : cupsSetDests( m_nDests, (cups_dest_t*)m_pDests );
852 0 : m_aCUPSMutex.release();
853 : }
854 :
855 0 : return PrinterInfoManager::writePrinterConfig();
856 : }
857 :
858 0 : bool CUPSManager::addOrRemovePossible() const
859 : {
860 0 : return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible();
861 : }
862 :
863 0 : const char* CUPSManager::authenticateUser( const char* /*pIn*/ )
864 : {
865 0 : const char* pRet = NULL;
866 0 : oslModule pLib = osl_loadModuleAscii( _XSALSET_LIBNAME, SAL_LOADMODULE_LAZY );
867 0 : if( pLib )
868 : {
869 0 : OUString aSym( "Sal_authenticateQuery" );
870 : bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) =
871 0 : (bool(*)(const OString&,OString&,OString&))osl_getFunctionSymbol( pLib, aSym.pData );
872 0 : if( getpw )
873 : {
874 0 : osl::MutexGuard aGuard( m_aCUPSMutex );
875 :
876 0 : OString aUser = cupsUser();
877 0 : OString aServer = cupsServer();
878 0 : OString aPassword;
879 0 : if( getpw( aServer, aUser, aPassword ) )
880 : {
881 0 : m_aPassword = aPassword;
882 0 : m_aUser = aUser;
883 0 : cupsSetUser( m_aUser.getStr() );
884 0 : pRet = m_aPassword.getStr();
885 0 : }
886 : }
887 0 : osl_unloadModule( pLib );
888 : }
889 : #if OSL_DEBUG_LEVEL > 1
890 : else
891 : {
892 : fprintf( stderr, "loading of module %s failed\n", _XSALSET_LIBNAME );
893 : }
894 : #endif
895 :
896 0 : return pRet;
897 : }
898 :
899 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|