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