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