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