1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
48 osl::Condition m_aCondition
;
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
)
67 if( !m_aResult
.isEmpty() )
68 unlink( m_aResult
.getStr() );
75 *m_pResetRunning
= false;
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
);
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
;
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
)
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
,
133 oslThread aThread
= osl_createThread( getPPDWorker
, pAttribs
);
139 // NOTE: waitResult release and acquires the GetPPD mutex
140 aResult
= pAttribs
->waitResult( &aValue
);
141 osl_destroyThread( aThread
);
143 m_aGetPPDMutex
.release();
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
);
162 CUPSManager
* CUPSManager::tryLoadCUPS()
164 CUPSManager
* pManager
= NULL
;
165 static const char* pEnv
= getenv("SAL_DISABLE_CUPS");
168 pManager
= new CUPSManager();
174 static void run_dest_thread_stub( void* pThis
)
176 osl_setThreadName("CUPSManager cupsGetDests");
177 CUPSManager::runDestThread( pThis
);
181 CUPSManager::CUPSManager() :
182 PrinterInfoManager( CUPS
),
185 m_bNewDests( false ),
186 m_bPPDThreadRunning( false ),
189 m_aDestThread
= osl_createThread( run_dest_thread_stub
, this );
192 CUPSManager::~CUPSManager()
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
218 if( (p_http
=httpConnectEncrypt(
221 cupsEncryption())) != NULL
)
223 // neat, cups is up, clean up the canary
226 int nDests
= cupsGetDests( &pDests
);
227 SAL_INFO("vcl.unx.print", "came out of cupsGetDests");
229 osl::MutexGuard
aGuard( m_aCUPSMutex
);
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
);
249 // dest thread has run, clean up
252 osl_joinWithThread( m_aDestThread
);
253 osl_destroyThread( m_aDestThread
);
254 m_aDestThread
= NULL
;
259 m_aCUPSDestMap
.clear();
261 if( ! (m_nDests
&& m_pDests
) )
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",
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
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
);
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
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() )
361 if( !it
->second
.m_aInfo
.m_aFeatures
.isEmpty() )
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
) );
388 const PPDValue
* pValue
= pKey
->getValue( OStringToOUString( pChoice
->choice
, aEncoding
) );
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
);
398 SAL_INFO("vcl.unx.print", "key " << pOption
->keyword
<< " is defaulted to " << pChoice
->choice
);
401 SAL_INFO("vcl.unx.print", "caution: value " << pChoice
->choice
<< " not found in key " << pOption
->keyword
);
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
;
421 if( rPrinter
.startsWith("CUPS:") )
422 aPrinter
= rPrinter
.copy( 5 );
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() );
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
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
;
475 SAL_INFO("vcl.unx.print", "ppdOpenFile failed, falling back to generic driver");
477 // remove temporary PPD file
478 unlink( aPPDFile
.getStr() );
481 SAL_INFO("vcl.unx.print", "cupsGetPPD failed, falling back to generic driver");
484 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter
);
486 m_aCUPSMutex
.release();
489 SAL_WARN("vcl.unx.print", "could not acquire CUPS mutex !!!" );
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
);
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
);
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
)
531 if( p_it
->second
.m_aInfo
.m_aDriverName
.startsWith("CUPS:") )
532 aPrinter
= p_it
->second
.m_aInfo
.m_aDriverName
.copy( 5 );
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" );
561 m_aSpoolFiles
[fp
] = aSysFile
;
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
)
577 // emit features ordered to OrderDependency
578 // ignore features that are set to default
581 if( rJob
.m_pParser
== rJob
.m_aContext
.getParser() && rJob
.m_pParser
)
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
);
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
) );
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() )
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();
652 bool CUPSManager::startBatchPrint()
658 bool CUPSManager::supportsBatchPrint() const
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
;
675 std::vector
< OString
> files
;
677 while( !pendingJobs
.empty())
681 currentJobData
= pendingJobs
.front();
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
))
692 currentJobData
= pendingJobs
.front();
694 files
.push_back( pendingJobs
.front().file
);
695 pendingJobs
.pop_front();
699 if( !printJobs( currentJobData
, files
)) // print the last batch
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
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();
732 fnames
.push_back( it
->getStr());
734 int nJobID
= cupsPrintFiles(pDest
->name
,
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() );
752 for( std::vector
< OString
>::const_iterator it
= files
.begin();
755 unlink( it
->getStr());
758 cupsFreeOptions( nNumOptions
, pOptions
);
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;
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");
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
) );
800 if( m_aCUPSMutex
.tryToAcquire() )
802 bChanged
= m_bNewDests
;
803 m_aCUPSMutex
.release();
808 bChanged
= PrinterInfoManager::checkPrintersChanged( bWait
);
809 // #i54375# ensure new merging with CUPS list in :initialize
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:")
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() )
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();
855 bSuccess
= PrinterInfoManager::setDefaultPrinter( rName
);
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() )
873 if( ! prt
->second
.m_bModified
)
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
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();
915 class RTSPWDialog
: public ModalDialog
917 VclPtr
<FixedText
> m_pText
;
918 VclPtr
<Edit
> m_pUserEdit
;
919 VclPtr
<Edit
> m_pPassEdit
;
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()
948 void RTSPWDialog::dispose()
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
)
970 ScopedVclPtrInstance
<RTSPWDialog
> aDialog(rServer
, rUserName
, nullptr);
971 if (aDialog
->Execute())
973 rUserName
= aDialog
->getUserName();
974 rPassword
= aDialog
->getPassword();
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();
991 if (AuthenticateQuery(aServer
, aUser
, aPassword
))
993 m_aPassword
= aPassword
;
995 cupsSetUser( m_aUser
.getStr() );
996 pRet
= m_aPassword
.getStr();
1002 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */