Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / unx / generic / printer / cupsmgr.cxx
blob20a27c2403af9695595eee16db45b615eb8a252a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <cups/cups.h>
21 #include <cups/http.h>
22 #include <cups/ipp.h>
23 #include <cups/ppd.h>
25 #include <unistd.h>
27 #include "cupsmgr.hxx"
29 #include "osl/thread.h"
30 #include "osl/diagnose.h"
31 #include "osl/conditn.hxx"
33 #include "rtl/ustrbuf.hxx"
35 #include <officecfg/Office/Common.hxx>
37 #include <vcl/button.hxx>
38 #include <vcl/dialog.hxx>
39 #include <vcl/fixed.hxx>
41 #include <algorithm>
43 using namespace psp;
44 using namespace osl;
46 struct GetPPDAttribs
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;
55 GetPPDAttribs( const char * m_pParameter,
56 bool* pResetRunning, osl::Mutex* pSyncMutex )
57 : m_aParameter( m_pParameter ),
58 m_pResetRunning( pResetRunning ),
59 m_pSyncMutex( pSyncMutex )
61 m_nRefs = 2;
62 m_aCondition.reset();
65 ~GetPPDAttribs()
67 if( !m_aResult.isEmpty() )
68 unlink( m_aResult.getStr() );
71 void unref()
73 if( --m_nRefs == 0 )
75 *m_pResetRunning = false;
76 delete this;
80 void executeCall()
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 OString aResult = cupsGetPPD(m_aParameter.getStr());
85 MutexGuard aGuard( *m_pSyncMutex );
86 m_aResult = aResult;
87 m_aCondition.set();
88 unref();
91 OString waitResult( TimeValue *pDelay )
93 m_pSyncMutex->release();
95 if (m_aCondition.wait( pDelay ) != Condition::result_ok
98 SAL_WARN("vcl.unx.print",
99 "cupsGetPPD " << m_aParameter << " timed out");
101 m_pSyncMutex->acquire();
103 OString aRetval = m_aResult;
104 m_aResult.clear();
105 unref();
107 return aRetval;
111 extern "C" {
112 static void getPPDWorker(void* pData)
114 osl_setThreadName("CUPSManager getPPDWorker");
115 GetPPDAttribs* pAttribs = static_cast<GetPPDAttribs*>(pData);
116 pAttribs->executeCall();
120 OString CUPSManager::threadedCupsGetPPD( const char* pPrinter )
122 OString aResult;
124 m_aGetPPDMutex.acquire();
125 // if one thread hangs in cupsGetPPD already, don't start another
126 if( ! m_bPPDThreadRunning )
128 m_bPPDThreadRunning = true;
129 GetPPDAttribs* pAttribs = new GetPPDAttribs( pPrinter,
130 &m_bPPDThreadRunning,
131 &m_aGetPPDMutex );
133 oslThread aThread = osl_createThread( getPPDWorker, pAttribs );
135 TimeValue aValue;
136 aValue.Seconds = 5;
137 aValue.Nanosec = 0;
139 // NOTE: waitResult release and acquires the GetPPD mutex
140 aResult = pAttribs->waitResult( &aValue );
141 osl_destroyThread( aThread );
143 m_aGetPPDMutex.release();
145 return aResult;
148 static const char* setPasswordCallback( const char* pIn )
150 const char* pRet = NULL;
152 PrinterInfoManager& rMgr = PrinterInfoManager::get();
153 if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check
154 pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn );
155 return pRet;
159 * CUPSManager class
162 CUPSManager* CUPSManager::tryLoadCUPS()
164 CUPSManager* pManager = NULL;
165 static const char* pEnv = getenv("SAL_DISABLE_CUPS");
167 if (!pEnv || !*pEnv)
168 pManager = new CUPSManager();
169 return pManager;
172 extern "C"
174 static void run_dest_thread_stub( void* pThis )
176 osl_setThreadName("CUPSManager cupsGetDests");
177 CUPSManager::runDestThread( pThis );
181 CUPSManager::CUPSManager() :
182 PrinterInfoManager( CUPS ),
183 m_nDests( 0 ),
184 m_pDests( NULL ),
185 m_bNewDests( false ),
186 m_bPPDThreadRunning( false ),
187 batchMode( false )
189 m_aDestThread = osl_createThread( run_dest_thread_stub, this );
192 CUPSManager::~CUPSManager()
194 if( m_aDestThread )
196 // if the thread is still running here, then
197 // cupsGetDests is hung; terminate the thread instead of joining
198 osl_terminateThread( m_aDestThread );
199 osl_destroyThread( m_aDestThread );
202 if (m_nDests && m_pDests)
203 cupsFreeDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
206 void CUPSManager::runDestThread( void* pThis )
208 static_cast<CUPSManager*>(pThis)->runDests();
211 void CUPSManager::runDests()
213 SAL_INFO("vcl.unx.print", "starting cupsGetDests");
214 cups_dest_t* pDests = NULL;
216 // n#722902 - do a fast-failing check for cups working *at all* first
217 http_t* p_http;
218 if( (p_http=httpConnectEncrypt(
219 cupsServer(),
220 ippPort(),
221 cupsEncryption())) != NULL )
223 // neat, cups is up, clean up the canary
224 httpClose(p_http);
226 int nDests = cupsGetDests( &pDests );
227 SAL_INFO("vcl.unx.print", "came out of cupsGetDests");
229 osl::MutexGuard aGuard( m_aCUPSMutex );
230 m_nDests = nDests;
231 m_pDests = pDests;
232 m_bNewDests = true;
233 SAL_INFO("vcl.unx.print", "finished cupsGetDests");
237 void CUPSManager::initialize()
239 // get normal printers, clear printer list
240 PrinterInfoManager::initialize();
242 // check whether thread has completed
243 // if not behave like old printing system
244 osl::MutexGuard aGuard( m_aCUPSMutex );
246 if( ! m_bNewDests )
247 return;
249 // dest thread has run, clean up
250 if( m_aDestThread )
252 osl_joinWithThread( m_aDestThread );
253 osl_destroyThread( m_aDestThread );
254 m_aDestThread = NULL;
256 m_bNewDests = false;
258 // clear old stuff
259 m_aCUPSDestMap.clear();
261 if( ! (m_nDests && m_pDests ) )
262 return;
264 // check for CUPS server(?) > 1.2
265 // since there is no API to query, check for options that were
266 // introduced in dests with 1.2
267 // this is needed to check for %%IncludeFeature support
268 // (#i65684#, #i65491#)
269 bool bUsePDF = false;
270 cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests);
271 const char* pOpt = cupsGetOption( "printer-info",
272 pDest->num_options,
273 pDest->options );
274 if( pOpt )
276 m_bUseIncludeFeature = true;
277 bUsePDF = officecfg::Office::Common::Print::Option::Printer::PDFAsStandardPrintJobFormat::get();
280 m_aGlobalDefaults.setDefaultBackend(bUsePDF);
282 // do not send include JobPatch; CUPS will insert that itself
283 // TODO: currently unknown which versions of CUPS insert JobPatches
284 // so currently it is assumed CUPS = don't insert JobPatch files
285 m_bUseJobPatch = false;
287 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
288 int nPrinter = m_nDests;
290 // reset global default PPD options; these are queried on demand from CUPS
291 m_aGlobalDefaults.m_pParser = NULL;
292 m_aGlobalDefaults.m_aContext = PPDContext();
294 // add CUPS printers, should there be a printer
295 // with the same name as a CUPS printer, overwrite it
296 while( nPrinter-- )
298 pDest = static_cast<cups_dest_t*>(m_pDests)+nPrinter;
299 OUString aPrinterName = OStringToOUString( pDest->name, aEncoding );
300 if( pDest->instance && *pDest->instance )
302 OUStringBuffer aBuf( 256 );
303 aBuf.append( aPrinterName );
304 aBuf.append( '/' );
305 aBuf.append( OStringToOUString( pDest->instance, aEncoding ) );
306 aPrinterName = aBuf.makeStringAndClear();
309 // initialize printer with possible configuration from psprint.conf
310 bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end();
311 Printer aPrinter = m_aPrinters[ aPrinterName ];
312 if( bSetToGlobalDefaults )
313 aPrinter.m_aInfo = m_aGlobalDefaults;
314 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
315 if( pDest->is_default )
316 m_aDefaultPrinter = aPrinterName;
318 for( int k = 0; k < pDest->num_options; k++ )
320 if(!strcmp(pDest->options[k].name, "printer-info"))
321 aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding);
322 if(!strcmp(pDest->options[k].name, "printer-location"))
323 aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding);
326 OUStringBuffer aBuf( 256 );
327 aBuf.appendAscii( "CUPS:" );
328 aBuf.append( aPrinterName );
329 // note: the parser that goes with the PrinterInfo
330 // is created implicitly by the JobData::operator=()
331 // when it detects the NULL ptr m_pParser.
332 // if we wanted to fill in the parser here this
333 // would mean we'd have to download PPDs for each and
334 // every printer - which would be really bad runtime
335 // behaviour
336 aPrinter.m_aInfo.m_pParser = NULL;
337 aPrinter.m_aInfo.m_aContext.setParser( NULL );
338 std::unordered_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName );
339 if( c_it != m_aDefaultContexts.end() )
341 aPrinter.m_aInfo.m_pParser = c_it->second.getParser();
342 aPrinter.m_aInfo.m_aContext = c_it->second;
344 aPrinter.m_aInfo.setDefaultBackend(bUsePDF);
345 aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear();
346 aPrinter.m_bModified = false;
348 m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter;
349 m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter;
352 // remove everything that is not a CUPS printer and not
353 // a special purpose printer (PDF, Fax)
354 std::list< OUString > aRemovePrinters;
355 for( std::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin();
356 it != m_aPrinters.end(); ++it )
358 if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() )
359 continue;
361 if( !it->second.m_aInfo.m_aFeatures.isEmpty() )
362 continue;
363 aRemovePrinters.push_back( it->first );
365 while( aRemovePrinters.begin() != aRemovePrinters.end() )
367 m_aPrinters.erase( aRemovePrinters.front() );
368 aRemovePrinters.pop_front();
371 cupsSetPasswordCB( setPasswordCallback );
374 static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext )
376 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
377 for( int i = 0; i < pPPDGroup->num_options; i++ )
379 ppd_option_t* pOption = pPPDGroup->options + i;
380 for( int n = 0; n < pOption->num_choices; n++ )
382 ppd_choice_t* pChoice = pOption->choices + n;
383 if( pChoice->marked )
385 const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) );
386 if( pKey )
388 const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) );
389 if( pValue )
391 if( pValue != pKey->getDefaultValue() )
393 rContext.setValue( pKey, pValue, true );
394 SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is set to " << pChoice->choice);
397 else
398 SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is defaulted to " << pChoice->choice);
400 else
401 SAL_INFO("vcl.unx.print", "caution: value " << pChoice->choice << " not found in key " << pOption->keyword);
403 else
404 SAL_INFO("vcl.unx.print", "caution: key " << pOption->keyword << " not found in parser");
409 // recurse through subgroups
410 for( int g = 0; g < pPPDGroup->num_subgroups; g++ )
412 updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext );
416 const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter )
418 const PPDParser* pNewParser = NULL;
419 OUString aPrinter;
421 if( rPrinter.startsWith("CUPS:") )
422 aPrinter = rPrinter.copy( 5 );
423 else
424 aPrinter = rPrinter;
426 if( m_aCUPSMutex.tryToAcquire() )
428 if (m_nDests && m_pDests)
430 std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
431 m_aCUPSDestMap.find( aPrinter );
432 if( dest_it != m_aCUPSDestMap.end() )
434 cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + dest_it->second;
435 OString aPPDFile = threadedCupsGetPPD( pDest->name );
436 SAL_INFO("vcl.unx.print",
437 "PPD for " << aPrinter << " is " << aPPDFile);
438 if( !aPPDFile.isEmpty() )
440 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
441 OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) );
442 // update the printer info with context information
443 ppd_file_t* pPPD = ppdOpenFile( aPPDFile.getStr() );
444 if( pPPD )
446 // create the new parser
447 PPDParser* pCUPSParser = new PPDParser( aFileName );
448 pCUPSParser->m_aFile = rPrinter;
449 pNewParser = pCUPSParser;
451 /*int nConflicts =*/ cupsMarkOptions( pPPD, pDest->num_options, pDest->options );
452 SAL_INFO("vcl.unx.print", "processing the following options for printer " << pDest->name << " (instance " << pDest->instance << "):");
453 for( int k = 0; k < pDest->num_options; k++ )
454 SAL_INFO("vcl.unx.print",
455 " \"" << pDest->options[k].name <<
456 "\" = \"" << pDest->options[k].value << "\"");
457 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
459 // remember the default context for later use
460 PPDContext& rContext = m_aDefaultContexts[ aPrinter ];
461 rContext.setParser( pNewParser );
462 // set system default paper; printer CUPS PPD options
463 // may overwrite it
464 setDefaultPaper( rContext );
465 for( int i = 0; i < pPPD->num_groups; i++ )
466 updatePrinterContextInfo( pPPD->groups + i, rContext );
468 rInfo.m_pParser = pNewParser;
469 rInfo.m_aContext = rContext;
471 // clean up the mess
472 ppdClose( pPPD );
474 else
475 SAL_INFO("vcl.unx.print", "ppdOpenFile failed, falling back to generic driver");
477 // remove temporary PPD file
478 unlink( aPPDFile.getStr() );
480 else
481 SAL_INFO("vcl.unx.print", "cupsGetPPD failed, falling back to generic driver");
483 else
484 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter);
486 m_aCUPSMutex.release();
488 else
489 SAL_WARN("vcl.unx.print", "could not acquire CUPS mutex !!!" );
491 if( ! pNewParser )
493 // get the default PPD
494 pNewParser = PPDParser::getParser( OUString( "SGENPRT" ) );
496 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
498 rInfo.m_pParser = pNewParser;
499 rInfo.m_aContext.setParser( pNewParser );
502 return pNewParser;
505 void CUPSManager::setupJobContextData( JobData& rData )
507 std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
508 m_aCUPSDestMap.find( rData.m_aPrinterName );
510 if( dest_it == m_aCUPSDestMap.end() )
511 return PrinterInfoManager::setupJobContextData( rData );
513 std::unordered_map< OUString, Printer, OUStringHash >::iterator p_it =
514 m_aPrinters.find( rData.m_aPrinterName );
515 if( p_it == m_aPrinters.end() ) // huh ?
517 SAL_WARN("vcl.unx.print", "CUPS printer list in disorder, "
518 "no dest for printer " << rData.m_aPrinterName);
519 return;
522 if( p_it->second.m_aInfo.m_pParser == NULL )
524 // in turn calls createCUPSParser
525 // which updates the printer info
526 p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName );
528 if( p_it->second.m_aInfo.m_aContext.getParser() == NULL )
530 OUString aPrinter;
531 if( p_it->second.m_aInfo.m_aDriverName.startsWith("CUPS:") )
532 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 );
533 else
534 aPrinter = p_it->second.m_aInfo.m_aDriverName;
536 p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ];
539 rData.m_pParser = p_it->second.m_aInfo.m_pParser;
540 rData.m_aContext = p_it->second.m_aInfo.m_aContext;
543 FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
545 OSL_TRACE( "startSpool: %s, %s",
546 OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(),
547 bQuickCommand ? "true" : "false" );
549 if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
551 OSL_TRACE( "defer to PrinterInfoManager::startSpool" );
552 return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
555 OUString aTmpURL, aTmpFile;
556 osl_createTempFile( NULL, NULL, &aTmpURL.pData );
557 osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData );
558 OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() );
559 FILE* fp = fopen( aSysFile.getStr(), "w" );
560 if( fp )
561 m_aSpoolFiles[fp] = aSysFile;
563 return fp;
566 struct less_ppd_key : public ::std::binary_function<double, double, bool>
568 bool operator()(const PPDKey* left, const PPDKey* right)
569 { return left->getOrderDependency() < right->getOrderDependency(); }
572 void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions )
574 rNumOptions = 0;
575 *rOptions = NULL;
577 // emit features ordered to OrderDependency
578 // ignore features that are set to default
580 // sanity check
581 if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser )
583 int i;
584 int nKeys = rJob.m_aContext.countValuesModified();
585 ::std::vector< const PPDKey* > aKeys( nKeys );
586 for( i = 0; i < nKeys; i++ )
587 aKeys[i] = rJob.m_aContext.getModifiedKey( i );
588 ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
590 for( i = 0; i < nKeys; i++ )
592 const PPDKey* pKey = aKeys[i];
593 const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
594 OUString sPayLoad;
595 if (pValue && pValue->m_eType == eInvocation)
597 sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption;
600 if (!sPayLoad.isEmpty())
602 OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US );
603 OString aValue = OUStringToOString( sPayLoad, RTL_TEXTENCODING_ASCII_US );
604 rNumOptions = cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
609 if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 )
611 OString aVal( OString::number( rJob.m_nCopies ) );
612 rNumOptions = cupsAddOption( "copies", aVal.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
613 aVal = OString::boolean(rJob.m_bCollate);
614 rNumOptions = cupsAddOption( "collate", aVal.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
616 if( ! bBanner )
618 rNumOptions = cupsAddOption( "job-sheets", "none", rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
622 bool CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber )
624 OSL_TRACE( "endSpool: %s, %s, copy count = %d",
625 OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(),
626 OUStringToOString( rJobTitle, RTL_TEXTENCODING_UTF8 ).getStr(),
627 rDocumentJobData.m_nCopies
630 osl::MutexGuard aGuard( m_aCUPSMutex );
632 std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
633 m_aCUPSDestMap.find( rPrintername );
634 if( dest_it == m_aCUPSDestMap.end() )
636 OSL_TRACE( "defer to PrinterInfoManager::endSpool" );
637 return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner, rFaxNumber );
640 std::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
641 if( it == m_aSpoolFiles.end() )
642 return false;
643 fclose( pFile );
644 PendingJob job( rPrintername, rJobTitle, rDocumentJobData, bBanner, rFaxNumber, it->second );
645 m_aSpoolFiles.erase( pFile );
646 pendingJobs.push_back( job );
647 if( !batchMode ) // process immediately, otherwise will be handled by flushBatchPrint()
648 return processPendingJobs();
649 return true;
652 bool CUPSManager::startBatchPrint()
654 batchMode = true;
655 return true;
658 bool CUPSManager::supportsBatchPrint() const
660 return true;
663 bool CUPSManager::flushBatchPrint()
665 osl::MutexGuard aGuard( m_aCUPSMutex );
666 batchMode = false; // reset the batch print mode
667 return processPendingJobs();
670 bool CUPSManager::processPendingJobs()
672 // Print all jobs that have the same data using one CUPS call (i.e. merge all jobs that differ only in files to print).
673 PendingJob currentJobData;
674 bool first = true;
675 std::vector< OString > files;
676 bool ok = true;
677 while( !pendingJobs.empty())
679 if( first )
681 currentJobData = pendingJobs.front();
682 first = false;
684 else if( currentJobData.printerName != pendingJobs.front().printerName
685 || currentJobData.jobTitle != pendingJobs.front().jobTitle
686 || currentJobData.jobData != pendingJobs.front().jobData
687 || currentJobData.banner != pendingJobs.front().banner )
689 if( !printJobs( currentJobData, files ))
690 ok = false;
691 files.clear();
692 currentJobData = pendingJobs.front();
694 files.push_back( pendingJobs.front().file );
695 pendingJobs.pop_front();
697 if( !first )
699 if( !printJobs( currentJobData, files )) // print the last batch
700 ok = false;
702 return ok;
705 bool CUPSManager::printJobs( const PendingJob& job, const std::vector< OString >& files )
707 std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
708 m_aCUPSDestMap.find( job.printerName );
710 rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
712 // setup cups options
713 int nNumOptions = 0;
714 cups_option_t* pOptions = NULL;
715 getOptionsFromDocumentSetup( job.jobData, job.banner, nNumOptions, reinterpret_cast<void**>(&pOptions) );
717 OString sJobName(OUStringToOString(job.jobTitle, aEnc));
719 //fax4CUPS, "the job name will be dialled for you"
720 //so override the jobname with the desired number
721 if (!job.faxNumber.isEmpty())
723 sJobName = OUStringToOString(job.faxNumber, aEnc);
726 cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + dest_it->second;
728 std::vector< const char* > fnames;
729 for( std::vector< OString >::const_iterator it = files.begin();
730 it != files.end();
731 ++it )
732 fnames.push_back( it->getStr());
734 int nJobID = cupsPrintFiles(pDest->name,
735 fnames.size(),
736 fnames.data(),
737 sJobName.getStr(),
738 nNumOptions, pOptions);
739 SAL_INFO("vcl.unx.print", "cupsPrintFile( " << pDest->name << ", "
740 << ( fnames.size() == 1 ? files.front() : OString::number( fnames.size()) ).getStr() << ", " << sJobName << ", " << nNumOptions
741 << ", " << pOptions << " ) returns " << nJobID);
742 for( int n = 0; n < nNumOptions; n++ )
743 SAL_INFO("vcl.unx.print",
744 " option " << pOptions[n].name << "=" << pOptions[n].value);
745 #if OSL_DEBUG_LEVEL > 1
746 OString aCmd( "cp " );
747 aCmd = aCmd + files.front();
748 aCmd = aCmd + OString( " $HOME/cupsprint.ps" );
749 system( aCmd.getStr() );
750 #endif
752 for( std::vector< OString >::const_iterator it = files.begin();
753 it != files.end();
754 ++it )
755 unlink( it->getStr());
757 if( pOptions )
758 cupsFreeOptions( nNumOptions, pOptions );
760 return nJobID != 0;
763 void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
765 PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo );
768 bool CUPSManager::checkPrintersChanged( bool bWait )
770 bool bChanged = false;
771 if( bWait )
773 if( m_aDestThread )
775 // initial asynchronous detection still running
776 SAL_INFO("vcl.unx.print", "syncing cups discovery thread");
777 osl_joinWithThread( m_aDestThread );
778 osl_destroyThread( m_aDestThread );
779 m_aDestThread = NULL;
780 SAL_INFO("vcl.unx.print", "done: syncing cups discovery thread");
782 else
784 // #i82321# check for cups printer updates
785 // with this change the whole asynchronous detection in a thread is
786 // almost useless. The only relevance left is for some stalled systems
787 // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION
788 // (see vcl/unx/source/gdi/salprnpsp.cxx)
789 // so that checkPrintersChanged( true ) will never be called
791 // there is no way to query CUPS whether the printer list has changed
792 // so get the dest list anew
793 if( m_nDests && m_pDests )
794 cupsFreeDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
795 m_nDests = 0;
796 m_pDests = NULL;
797 runDests();
800 if( m_aCUPSMutex.tryToAcquire() )
802 bChanged = m_bNewDests;
803 m_aCUPSMutex.release();
806 if( ! bChanged )
808 bChanged = PrinterInfoManager::checkPrintersChanged( bWait );
809 // #i54375# ensure new merging with CUPS list in :initialize
810 if( bChanged )
811 m_bNewDests = true;
814 if( bChanged )
815 initialize();
817 return bChanged;
820 bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver )
822 // don't touch the CUPS printers
823 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ||
824 rDriver.startsWith("CUPS:")
826 return false;
827 return PrinterInfoManager::addPrinter( rName, rDriver );
830 bool CUPSManager::removePrinter( const OUString& rName, bool bCheck )
832 // don't touch the CUPS printers
833 if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() )
834 return false;
835 return PrinterInfoManager::removePrinter( rName, bCheck );
838 bool CUPSManager::setDefaultPrinter( const OUString& rName )
840 bool bSuccess = false;
841 std::unordered_map< OUString, int, OUStringHash >::iterator nit =
842 m_aCUPSDestMap.find( rName );
843 if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() )
845 cups_dest_t* pDests = static_cast<cups_dest_t*>(m_pDests);
846 for( int i = 0; i < m_nDests; i++ )
847 pDests[i].is_default = 0;
848 pDests[ nit->second ].is_default = 1;
849 cupsSetDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
850 m_aDefaultPrinter = rName;
851 m_aCUPSMutex.release();
852 bSuccess = true;
854 else
855 bSuccess = PrinterInfoManager::setDefaultPrinter( rName );
857 return bSuccess;
860 bool CUPSManager::writePrinterConfig()
862 bool bDestModified = false;
863 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
865 for( std::unordered_map< OUString, Printer, OUStringHash >::iterator prt =
866 m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt )
868 std::unordered_map< OUString, int, OUStringHash >::iterator nit =
869 m_aCUPSDestMap.find( prt->first );
870 if( nit == m_aCUPSDestMap.end() )
871 continue;
873 if( ! prt->second.m_bModified )
874 continue;
876 if( m_aCUPSMutex.tryToAcquire() )
878 bDestModified = true;
879 cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + nit->second;
880 PrinterInfo& rInfo = prt->second.m_aInfo;
882 // create new option list
883 int nNewOptions = 0;
884 cups_option_t* pNewOptions = NULL;
885 int nValues = rInfo.m_aContext.countValuesModified();
886 for( int i = 0; i < nValues; i++ )
888 const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i );
889 const PPDValue* pValue = rInfo.m_aContext.getValue( pKey );
890 if( pKey && pValue ) // sanity check
892 OString aName = OUStringToOString( pKey->getKey(), aEncoding );
893 OString aValue = OUStringToOString( pValue->m_aOption, aEncoding );
894 nNewOptions = cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions );
897 // set PPD options on CUPS dest
898 cupsFreeOptions( pDest->num_options, pDest->options );
899 pDest->num_options = nNewOptions;
900 pDest->options = pNewOptions;
901 m_aCUPSMutex.release();
904 if( bDestModified && m_aCUPSMutex.tryToAcquire() )
906 cupsSetDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
907 m_aCUPSMutex.release();
910 return PrinterInfoManager::writePrinterConfig();
913 namespace
915 class RTSPWDialog : public ModalDialog
917 VclPtr<FixedText> m_pText;
918 VclPtr<Edit> m_pUserEdit;
919 VclPtr<Edit> m_pPassEdit;
921 public:
922 RTSPWDialog(const OString& rServer, const OString& rUserName, vcl::Window* pParent);
923 virtual ~RTSPWDialog();
924 virtual void dispose() SAL_OVERRIDE;
925 OString getUserName() const;
926 OString getPassword() const;
929 RTSPWDialog::RTSPWDialog( const OString& rServer, const OString& rUserName, vcl::Window* pParent )
930 : ModalDialog(pParent, "CUPSPasswordDialog",
931 "vcl/ui/cupspassworddialog.ui")
933 get(m_pText, "text");
934 get(m_pUserEdit, "user");
935 get(m_pPassEdit, "pass");
937 OUString aText(m_pText->GetText());
938 aText = aText.replaceFirst("%s", OStringToOUString(rServer, osl_getThreadTextEncoding()));
939 m_pText->SetText(aText);
940 m_pUserEdit->SetText( OStringToOUString(rUserName, osl_getThreadTextEncoding()));
943 RTSPWDialog::~RTSPWDialog()
945 disposeOnce();
948 void RTSPWDialog::dispose()
950 m_pText.clear();
951 m_pUserEdit.clear();
952 m_pPassEdit.clear();
953 ModalDialog::dispose();
956 OString RTSPWDialog::getUserName() const
958 return OUStringToOString( m_pUserEdit->GetText(), osl_getThreadTextEncoding() );
961 OString RTSPWDialog::getPassword() const
963 return OUStringToOString( m_pPassEdit->GetText(), osl_getThreadTextEncoding() );
966 bool AuthenticateQuery(const OString& rServer, OString& rUserName, OString& rPassword)
968 bool bRet = false;
970 ScopedVclPtrInstance<RTSPWDialog> aDialog(rServer, rUserName, nullptr);
971 if (aDialog->Execute())
973 rUserName = aDialog->getUserName();
974 rPassword = aDialog->getPassword();
975 bRet = true;
978 return bRet;
982 const char* CUPSManager::authenticateUser( const char* /*pIn*/ )
984 const char* pRet = NULL;
986 osl::MutexGuard aGuard( m_aCUPSMutex );
988 OString aUser = cupsUser();
989 OString aServer = cupsServer();
990 OString aPassword;
991 if (AuthenticateQuery(aServer, aUser, aPassword))
993 m_aPassword = aPassword;
994 m_aUser = aUser;
995 cupsSetUser( m_aUser.getStr() );
996 pRet = m_aPassword.getStr();
999 return pRet;
1002 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */