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 <unx/cpdmgr.hxx>
21 #include <unx/cupsmgr.hxx>
22 #include <unx/helper.hxx>
23
24 #include <saldatabasic.hxx>
25
26 #include <tools/urlobj.hxx>
27 #include <tools/config.hxx>
28
29 #include <i18nutil/paper.hxx>
30 #include <rtl/strbuf.hxx>
31 #include <sal/log.hxx>
32
33 #include <osl/file.hxx>
34 #include <osl/thread.hxx>
35 #include <osl/mutex.hxx>
36
37 // filename of configuration files
38 #define PRINT_FILENAME "psprint.conf"
39 // the group of the global defaults
40 #define GLOBAL_DEFAULTS_GROUP "__Global_Printer_Defaults__"
41
42 #include <unordered_set>
43
44 using namespace psp;
45 using namespace osl;
46
47 namespace psp
48 {
49 class SystemQueueInfo final : public Thread
50 {
51 mutable Mutex m_aMutex;
52 bool m_bChanged;
53 std::vector< PrinterInfoManager::SystemPrintQueue >
54 m_aQueues;
55 OUString m_aCommand;
56
57 virtual void SAL_CALL run() override;
58
59 public:
60 SystemQueueInfo();
61 virtual ~SystemQueueInfo() override;
62
63 bool hasChanged() const;
64 OUString getCommand() const;
65
66 // sets changed status to false; therefore not const
67 void getSystemQueues( std::vector< PrinterInfoManager::SystemPrintQueue >& rQueues );
68 };
69 } // namespace
70
71 /*
72 * class PrinterInfoManager
73 */
74
get()75 PrinterInfoManager& PrinterInfoManager::get()
76 {
77 SalData* pSalData = GetSalData();
78
79 if( ! pSalData->m_pPIManager )
80 {
81 pSalData->m_pPIManager = CPDManager::tryLoadCPD();
82 if( ! pSalData->m_pPIManager )
83 pSalData->m_pPIManager = CUPSManager::tryLoadCUPS();
84 if( ! pSalData->m_pPIManager )
85 pSalData->m_pPIManager = new PrinterInfoManager();
86
87 pSalData->m_pPIManager->initialize();
88 #if OSL_DEBUG_LEVEL > 1
89 fprintf( stderr, "PrinterInfoManager::get create Manager of type %d\n", pSalData->m_pPIManager->getType() );
90 #endif
91 }
92
93 return *pSalData->m_pPIManager;
94 }
95
release()96 void PrinterInfoManager::release()
97 {
98 SalData* pSalData = GetSalData();
99 delete pSalData->m_pPIManager;
100 pSalData->m_pPIManager = nullptr;
101 }
102
PrinterInfoManager(Type eType)103 PrinterInfoManager::PrinterInfoManager( Type eType ) :
104 m_eType( eType ),
105 m_bUseIncludeFeature( false ),
106 m_bUseJobPatch( true ),
107 m_aSystemDefaultPaper( "A4" )
108 {
109 if( eType == Type::Default )
110 m_pQueueInfo.reset( new SystemQueueInfo );
111
112 m_aSystemDefaultPaper = OStringToOUString(
113 PaperInfo::toPSName(PaperInfo::getSystemDefaultPaper().getPaper()),
114 RTL_TEXTENCODING_UTF8);
115 }
116
~PrinterInfoManager()117 PrinterInfoManager::~PrinterInfoManager()
118 {
119 #if OSL_DEBUG_LEVEL > 1
120 fprintf( stderr, "PrinterInfoManager: destroyed Manager of type %d\n", getType() );
121 #endif
122 }
123
checkPrintersChanged(bool bWait)124 bool PrinterInfoManager::checkPrintersChanged( bool bWait )
125 {
126 // check if files were created, deleted or modified since initialize()
127 bool bChanged = false;
128 for (auto const& watchFile : m_aWatchFiles)
129 {
130 DirectoryItem aItem;
131 if( DirectoryItem::get( watchFile.m_aFilePath, aItem ) )
132 {
133 if( watchFile.m_aModified.Seconds != 0 )
134 {
135 bChanged = true; // file probably has vanished
136 break;
137 }
138 }
139 else
140 {
141 FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
142 if( aItem.getFileStatus( aStatus ) )
143 {
144 bChanged = true; // unlikely but not impossible
145 break;
146 }
147 else
148 {
149 TimeValue aModified = aStatus.getModifyTime();
150 if( aModified.Seconds != watchFile.m_aModified.Seconds )
151 {
152 bChanged = true;
153 break;
154 }
155 }
156 }
157 }
158
159 if( bWait && m_pQueueInfo )
160 {
161 #if OSL_DEBUG_LEVEL > 1
162 fprintf( stderr, "syncing printer discovery thread\n" );
163 #endif
164 m_pQueueInfo->join();
165 #if OSL_DEBUG_LEVEL > 1
166 fprintf( stderr, "done: syncing printer discovery thread\n" );
167 #endif
168 }
169
170 if( ! bChanged && m_pQueueInfo )
171 bChanged = m_pQueueInfo->hasChanged();
172 if( bChanged )
173 {
174 initialize();
175 }
176
177 return bChanged;
178 }
179
initialize()180 void PrinterInfoManager::initialize()
181 {
182 m_bUseIncludeFeature = false;
183 m_aPrinters.clear();
184 m_aWatchFiles.clear();
185 OUString aDefaultPrinter;
186
187 // first initialize the global defaults
188 // have to iterate over all possible files
189 // there should be only one global setup section in all
190 // available config files
191 m_aGlobalDefaults = PrinterInfo();
192
193 // need a parser for the PPDContext. generic printer should do.
194 m_aGlobalDefaults.m_pParser = PPDParser::getParser( "SGENPRT" );
195 m_aGlobalDefaults.m_aContext.setParser( m_aGlobalDefaults.m_pParser );
196
197 if( ! m_aGlobalDefaults.m_pParser )
198 {
199 #if OSL_DEBUG_LEVEL > 1
200 fprintf( stderr, "Error: no default PPD file SGENPRT available, shutting down psprint...\n" );
201 #endif
202 return;
203 }
204
205 std::vector< OUString > aDirList;
206 psp::getPrinterPathList( aDirList, nullptr );
207 for (auto const& printDir : aDirList)
208 {
209 INetURLObject aFile( printDir, INetProtocol::File, INetURLObject::EncodeMechanism::All );
210 aFile.Append( PRINT_FILENAME );
211 Config aConfig( aFile.PathToFileName() );
212 if( aConfig.HasGroup( GLOBAL_DEFAULTS_GROUP ) )
213 {
214 #if OSL_DEBUG_LEVEL > 1
215 fprintf( stderr, "found global defaults in %s\n", OUStringToOString( aFile.PathToFileName(), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
216 #endif
217 aConfig.SetGroup( GLOBAL_DEFAULTS_GROUP );
218
219 OString aValue( aConfig.ReadKey( "Copies" ) );
220 if (!aValue.isEmpty())
221 m_aGlobalDefaults.m_nCopies = aValue.toInt32();
222
223 aValue = aConfig.ReadKey( "Orientation" );
224 if (!aValue.isEmpty())
225 m_aGlobalDefaults.m_eOrientation = aValue.equalsIgnoreAsciiCase("Landscape") ? orientation::Landscape : orientation::Portrait;
226
227 aValue = aConfig.ReadKey( "MarginAdjust" );
228 if (!aValue.isEmpty())
229 {
230 sal_Int32 nIdx {0};
231 m_aGlobalDefaults.m_nLeftMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
232 m_aGlobalDefaults.m_nRightMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
233 m_aGlobalDefaults.m_nTopMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
234 m_aGlobalDefaults.m_nBottomMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
235 }
236
237 aValue = aConfig.ReadKey( "ColorDepth", "24" );
238 if (!aValue.isEmpty())
239 m_aGlobalDefaults.m_nColorDepth = aValue.toInt32();
240
241 aValue = aConfig.ReadKey( "ColorDevice" );
242 if (!aValue.isEmpty())
243 m_aGlobalDefaults.m_nColorDevice = aValue.toInt32();
244
245 aValue = aConfig.ReadKey( "PSLevel" );
246 if (!aValue.isEmpty())
247 m_aGlobalDefaults.m_nPSLevel = aValue.toInt32();
248
249 aValue = aConfig.ReadKey( "PDFDevice" );
250 if (!aValue.isEmpty())
251 m_aGlobalDefaults.m_nPDFDevice = aValue.toInt32();
252
253 // get the PPDContext of global JobData
254 for( int nKey = 0; nKey < aConfig.GetKeyCount(); ++nKey )
255 {
256 OString aKey( aConfig.GetKeyName( nKey ) );
257 if (aKey.startsWith("PPD_"))
258 {
259 aValue = aConfig.ReadKey( aKey );
260 const PPDKey* pKey = m_aGlobalDefaults.m_pParser->getKey(OStringToOUString(aKey.copy(4), RTL_TEXTENCODING_ISO_8859_1));
261 if( pKey )
262 {
263 m_aGlobalDefaults.m_aContext.
264 setValue( pKey,
265 aValue == "*nil" ? nullptr : pKey->getValue(OStringToOUString(aValue, RTL_TEXTENCODING_ISO_8859_1)),
266 true );
267 }
268 }
269 }
270 }
271 }
272 setDefaultPaper( m_aGlobalDefaults.m_aContext );
273
274 // now collect all available printers
275 for (auto const& printDir : aDirList)
276 {
277 INetURLObject aDir( printDir, INetProtocol::File, INetURLObject::EncodeMechanism::All );
278 INetURLObject aFile( aDir );
279 aFile.Append( PRINT_FILENAME );
280
281 // check directory validity
282 OUString aUniPath;
283 FileBase::getFileURLFromSystemPath( aDir.PathToFileName(), aUniPath );
284 Directory aDirectory( aUniPath );
285 if( aDirectory.open() )
286 continue;
287 aDirectory.close();
288
289 FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aUniPath );
290 FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
291 DirectoryItem aItem;
292
293 // setup WatchFile list
294 WatchFile aWatchFile;
295 aWatchFile.m_aFilePath = aUniPath;
296 if( ! DirectoryItem::get( aUniPath, aItem ) &&
297 ! aItem.getFileStatus( aStatus ) )
298 {
299 aWatchFile.m_aModified = aStatus.getModifyTime();
300 }
301 else
302 {
303 aWatchFile.m_aModified.Seconds = 0;
304 aWatchFile.m_aModified.Nanosec = 0;
305 }
306 m_aWatchFiles.push_back( aWatchFile );
307
308 Config aConfig( aFile.PathToFileName() );
309 for( int nGroup = 0; nGroup < aConfig.GetGroupCount(); nGroup++ )
310 {
311 aConfig.SetGroup( aConfig.GetGroupName( nGroup ) );
312 OString aValue = aConfig.ReadKey( "Printer" );
313 if (!aValue.isEmpty())
314 {
315 OUString aPrinterName;
316
317 sal_Int32 nNamePos = aValue.indexOf('/');
318 // check for valid value of "Printer"
319 if (nNamePos == -1)
320 continue;
321
322 Printer aPrinter;
323 // initialize to global defaults
324 aPrinter.m_aInfo = m_aGlobalDefaults;
325
326 aPrinterName = OStringToOUString(aValue.copy(nNamePos+1),
327 RTL_TEXTENCODING_UTF8);
328 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
329 aPrinter.m_aInfo.m_aDriverName = OStringToOUString(aValue.copy(0, nNamePos), RTL_TEXTENCODING_UTF8);
330
331 // set parser, merge settings
332 // don't do this for CUPS printers as this is done
333 // by the CUPS system itself
334 if( !aPrinter.m_aInfo.m_aDriverName.startsWith( "CUPS:" ) )
335 {
336 aPrinter.m_aInfo.m_pParser = PPDParser::getParser( aPrinter.m_aInfo.m_aDriverName );
337 aPrinter.m_aInfo.m_aContext.setParser( aPrinter.m_aInfo.m_pParser );
338 // note: setParser also purges the context
339
340 // ignore this printer if its driver is not found
341 if( ! aPrinter.m_aInfo.m_pParser )
342 continue;
343
344 // merge the ppd context keys if the printer has the same keys and values
345 // this is a bit tricky, since it involves mixing two PPDs
346 // without constraints which might end up badly
347 // this feature should be use with caution
348 // it is mainly to select default paper sizes for new printers
349 for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ )
350 {
351 const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified );
352 const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey );
353 const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : nullptr;
354 if( pDefKey && pPrinterKey )
355 // at least the options exist in both PPDs
356 {
357 if( pDefValue )
358 {
359 const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption );
360 if( pPrinterValue )
361 // the printer has a corresponding option for the key
362 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue );
363 }
364 else
365 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, nullptr );
366 }
367 }
368
369 aValue = aConfig.ReadKey( "Command" );
370 // no printer without a command
371 if (aValue.isEmpty())
372 {
373 /* TODO:
374 * porters: please append your platform to the Solaris
375 * case if your platform has SystemV printing per default.
376 */
377 #if defined __sun
378 aValue = "lp";
379 #else
380 aValue = "lpr";
381 #endif
382 }
383 aPrinter.m_aInfo.m_aCommand = OStringToOUString(aValue, RTL_TEXTENCODING_UTF8);
384 }
385
386 aValue = aConfig.ReadKey( "QuickCommand" );
387 aPrinter.m_aInfo.m_aQuickCommand = OStringToOUString(aValue, RTL_TEXTENCODING_UTF8);
388
389 aValue = aConfig.ReadKey( "Features" );
390 aPrinter.m_aInfo.m_aFeatures = OStringToOUString(aValue, RTL_TEXTENCODING_UTF8);
391
392 // override the settings in m_aGlobalDefaults if keys exist
393 aValue = aConfig.ReadKey( "DefaultPrinter" );
394 if (aValue != "0" && !aValue.equalsIgnoreAsciiCase("false"))
395 aDefaultPrinter = aPrinterName;
396
397 aValue = aConfig.ReadKey( "Location" );
398 aPrinter.m_aInfo.m_aLocation = OStringToOUString(aValue, RTL_TEXTENCODING_UTF8);
399
400 aValue = aConfig.ReadKey( "Comment" );
401 aPrinter.m_aInfo.m_aComment = OStringToOUString(aValue, RTL_TEXTENCODING_UTF8);
402
403 aValue = aConfig.ReadKey( "Copies" );
404 if (!aValue.isEmpty())
405 aPrinter.m_aInfo.m_nCopies = aValue.toInt32();
406
407 aValue = aConfig.ReadKey( "Orientation" );
408 if (!aValue.isEmpty())
409 aPrinter.m_aInfo.m_eOrientation = aValue.equalsIgnoreAsciiCase("Landscape") ? orientation::Landscape : orientation::Portrait;
410
411 aValue = aConfig.ReadKey( "MarginAdjust" );
412 if (!aValue.isEmpty())
413 {
414 sal_Int32 nIdx {0};
415 aPrinter.m_aInfo.m_nLeftMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
416 aPrinter.m_aInfo.m_nRightMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
417 aPrinter.m_aInfo.m_nTopMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
418 aPrinter.m_aInfo.m_nBottomMarginAdjust = aValue.getToken(0, ',', nIdx).toInt32();
419 }
420
421 aValue = aConfig.ReadKey( "ColorDepth" );
422 if (!aValue.isEmpty())
423 aPrinter.m_aInfo.m_nColorDepth = aValue.toInt32();
424
425 aValue = aConfig.ReadKey( "ColorDevice" );
426 if (!aValue.isEmpty())
427 aPrinter.m_aInfo.m_nColorDevice = aValue.toInt32();
428
429 aValue = aConfig.ReadKey( "PSLevel" );
430 if (!aValue.isEmpty())
431 aPrinter.m_aInfo.m_nPSLevel = aValue.toInt32();
432
433 aValue = aConfig.ReadKey( "PDFDevice" );
434 if (!aValue.isEmpty())
435 aPrinter.m_aInfo.m_nPDFDevice = aValue.toInt32();
436
437 // now iterate over all keys to extract multi key information:
438 // 1. PPDContext information
439 for( int nKey = 0; nKey < aConfig.GetKeyCount(); ++nKey )
440 {
441 OString aKey( aConfig.GetKeyName( nKey ) );
442 if( aKey.startsWith("PPD_") && aPrinter.m_aInfo.m_pParser )
443 {
444 aValue = aConfig.ReadKey( aKey );
445 const PPDKey* pKey = aPrinter.m_aInfo.m_pParser->getKey(OStringToOUString(aKey.copy(4), RTL_TEXTENCODING_ISO_8859_1));
446 if( pKey )
447 {
448 aPrinter.m_aInfo.m_aContext.
449 setValue( pKey,
450 aValue == "*nil" ? nullptr : pKey->getValue(OStringToOUString(aValue, RTL_TEXTENCODING_ISO_8859_1)),
451 true );
452 }
453 }
454 }
455
456 setDefaultPaper( aPrinter.m_aInfo.m_aContext );
457
458 // if it's a "Generic Printer", apply defaults from config...
459 aPrinter.m_aInfo.resolveDefaultBackend();
460
461 // finally insert printer
462 FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aPrinter.m_aFile );
463 std::unordered_map< OUString, Printer >::const_iterator find_it =
464 m_aPrinters.find( aPrinterName );
465 if( find_it != m_aPrinters.end() )
466 {
467 aPrinter.m_aAlternateFiles = find_it->second.m_aAlternateFiles;
468 aPrinter.m_aAlternateFiles.insert( find_it->second.m_aFile );
469 }
470 m_aPrinters[ aPrinterName ] = aPrinter;
471 }
472 }
473 }
474
475 // set default printer
476 if( !m_aPrinters.empty() )
477 {
478 if( m_aPrinters.find( aDefaultPrinter ) == m_aPrinters.end() )
479 aDefaultPrinter = m_aPrinters.begin()->first;
480 }
481 else
482 aDefaultPrinter.clear();
483 m_aDefaultPrinter = aDefaultPrinter;
484
485 if( m_eType != Type::Default )
486 return;
487
488 // add a default printer for every available print queue
489 // merge paper default printer, all else from global defaults
490 PrinterInfo aMergeInfo( m_aGlobalDefaults );
491 aMergeInfo.m_aDriverName = "SGENPRT";
492 aMergeInfo.m_aFeatures = "autoqueue";
493
494 if( !m_aDefaultPrinter.isEmpty() )
495 {
496 PrinterInfo aDefaultInfo( getPrinterInfo( m_aDefaultPrinter ) );
497
498 const PPDKey* pDefKey = aDefaultInfo.m_pParser->getKey( "PageSize" );
499 const PPDKey* pMergeKey = aMergeInfo.m_pParser->getKey( "PageSize" );
500 const PPDValue* pDefValue = aDefaultInfo.m_aContext.getValue( pDefKey );
501 const PPDValue* pMergeValue = pMergeKey ? pMergeKey->getValue( pDefValue->m_aOption ) : nullptr;
502 if( pMergeKey && pMergeValue )
503 aMergeInfo.m_aContext.setValue( pMergeKey, pMergeValue );
504 }
505
506 if( m_pQueueInfo && m_pQueueInfo->hasChanged() )
507 {
508 m_aSystemPrintCommand = m_pQueueInfo->getCommand();
509 m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues );
510 m_pQueueInfo.reset();
511 }
512 for (auto const& printQueue : m_aSystemPrintQueues)
513 {
514 OUString aPrinterName = "<" + printQueue.m_aQueue + ">";
515
516 if( m_aPrinters.find( aPrinterName ) != m_aPrinters.end() )
517 // probably user made this one permanent
518 continue;
519
520 OUString aCmd( m_aSystemPrintCommand );
521 aCmd = aCmd.replaceAll( "(PRINTER)", printQueue.m_aQueue );
522
523 Printer aPrinter;
524
525 // initialize to merged defaults
526 aPrinter.m_aInfo = aMergeInfo;
527 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
528 aPrinter.m_aInfo.m_aCommand = aCmd;
529 aPrinter.m_aInfo.m_aComment = printQueue.m_aComment;
530 aPrinter.m_aInfo.m_aLocation = printQueue.m_aLocation;
531
532 m_aPrinters[ aPrinterName ] = aPrinter;
533 }
534 }
535
listPrinters(::std::vector<OUString> & rVector) const536 void PrinterInfoManager::listPrinters( ::std::vector< OUString >& rVector ) const
537 {
538 rVector.clear();
539 for (auto const& printer : m_aPrinters)
540 rVector.push_back(printer.first);
541 }
542
getPrinterInfo(const OUString & rPrinter) const543 const PrinterInfo& PrinterInfoManager::getPrinterInfo( const OUString& rPrinter ) const
544 {
545 static PrinterInfo aEmptyInfo;
546 std::unordered_map< OUString, Printer >::const_iterator it = m_aPrinters.find( rPrinter );
547
548 SAL_WARN_IF( it == m_aPrinters.end(), "vcl", "Do not ask for info about nonexistent printers" );
549
550 return it != m_aPrinters.end() ? it->second.m_aInfo : aEmptyInfo;
551 }
552
checkFeatureToken(const OUString & rPrinterName,const char * pToken) const553 bool PrinterInfoManager::checkFeatureToken( const OUString& rPrinterName, const char* pToken ) const
554 {
555 const PrinterInfo& rPrinterInfo( getPrinterInfo( rPrinterName ) );
556 sal_Int32 nIndex = 0;
557 while( nIndex != -1 )
558 {
559 OUString aOuterToken = rPrinterInfo.m_aFeatures.getToken( 0, ',', nIndex );
560 if( aOuterToken.getToken( 0, '=' ).equalsIgnoreAsciiCaseAscii( pToken ) )
561 return true;
562 }
563 return false;
564 }
565
startSpool(const OUString & rPrintername,bool bQuickCommand)566 FILE* PrinterInfoManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
567 {
568 const PrinterInfo& rPrinterInfo = getPrinterInfo (rPrintername);
569 const OUString& rCommand = (bQuickCommand && !rPrinterInfo.m_aQuickCommand.isEmpty() ) ?
570 rPrinterInfo.m_aQuickCommand : rPrinterInfo.m_aCommand;
571 OString aShellCommand = OUStringToOString (rCommand, RTL_TEXTENCODING_ISO_8859_1) +
572 " 2>/dev/null";
573
574 return popen (aShellCommand.getStr(), "w");
575 }
576
endSpool(const OUString &,const OUString &,FILE * pFile,const JobData &,bool,const OUString &)577 bool PrinterInfoManager::endSpool( const OUString& /*rPrintername*/, const OUString& /*rJobTitle*/, FILE* pFile, const JobData& /*rDocumentJobData*/, bool /*bBanner*/, const OUString& /*rFaxNumber*/ )
578 {
579 return (0 == pclose( pFile ));
580 }
581
setupJobContextData(JobData & rData)582 void PrinterInfoManager::setupJobContextData( JobData& rData )
583 {
584 std::unordered_map< OUString, Printer >::iterator it =
585 m_aPrinters.find( rData.m_aPrinterName );
586 if( it != m_aPrinters.end() )
587 {
588 rData.m_pParser = it->second.m_aInfo.m_pParser;
589 rData.m_aContext = it->second.m_aInfo.m_aContext;
590 }
591 }
592
setDefaultPaper(PPDContext & rContext) const593 void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const
594 {
595 if( ! rContext.getParser() )
596 return;
597
598 const PPDKey* pPageSizeKey = rContext.getParser()->getKey( "PageSize" );
599 if( ! pPageSizeKey )
600 return;
601
602 int nModified = rContext.countValuesModified();
603 while( nModified-- &&
604 rContext.getModifiedKey( nModified ) != pPageSizeKey )
605 ;
606
607 if( nModified >= 0 ) // paper was set already, do not modify
608 {
609 #if OSL_DEBUG_LEVEL > 1
610 fprintf( stderr, "not setting default paper, already set %s\n",
611 OUStringToOString( rContext.getValue( pPageSizeKey )->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
612 #endif
613 return;
614 }
615
616 // paper not set, fill in default value
617 const PPDValue* pPaperVal = nullptr;
618 int nValues = pPageSizeKey->countValues();
619 for( int i = 0; i < nValues && ! pPaperVal; i++ )
620 {
621 const PPDValue* pVal = pPageSizeKey->getValue( i );
622 if( pVal->m_aOption.equalsIgnoreAsciiCase( m_aSystemDefaultPaper ) )
623 pPaperVal = pVal;
624 }
625 if( pPaperVal )
626 {
627 #if OSL_DEBUG_LEVEL > 1
628 fprintf( stderr, "setting default paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
629 #endif
630 rContext.setValue( pPageSizeKey, pPaperVal );
631 #if OSL_DEBUG_LEVEL > 1
632 pPaperVal = rContext.getValue( pPageSizeKey );
633 fprintf( stderr, "-> got paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
634 #endif
635 }
636 }
637
SystemQueueInfo()638 SystemQueueInfo::SystemQueueInfo() :
639 m_bChanged( false )
640 {
641 create();
642 }
643
~SystemQueueInfo()644 SystemQueueInfo::~SystemQueueInfo()
645 {
646 static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
647 if( ! pNoSyncDetection || !*pNoSyncDetection )
648 join();
649 else
650 terminate();
651 }
652
hasChanged() const653 bool SystemQueueInfo::hasChanged() const
654 {
655 MutexGuard aGuard( m_aMutex );
656 bool bChanged = m_bChanged;
657 return bChanged;
658 }
659
getSystemQueues(std::vector<PrinterInfoManager::SystemPrintQueue> & rQueues)660 void SystemQueueInfo::getSystemQueues( std::vector< PrinterInfoManager::SystemPrintQueue >& rQueues )
661 {
662 MutexGuard aGuard( m_aMutex );
663 rQueues = m_aQueues;
664 m_bChanged = false;
665 }
666
getCommand() const667 OUString SystemQueueInfo::getCommand() const
668 {
669 MutexGuard aGuard( m_aMutex );
670 OUString aRet = m_aCommand;
671 return aRet;
672 }
673
674 struct SystemCommandParameters;
675 typedef void(* tokenHandler)(const std::vector< OString >&,
676 std::vector< PrinterInfoManager::SystemPrintQueue >&,
677 const SystemCommandParameters*);
678
679 struct SystemCommandParameters
680 {
681 const char* pQueueCommand;
682 const char* pPrintCommand;
683 const char* pForeToken;
684 const char* pAftToken;
685 unsigned int const nForeTokenCount;
686 tokenHandler const pHandler;
687 };
688
689 #if ! (defined(LINUX) || defined(NETBSD) || defined(DRAGONFLY) || defined(OPENBSD))
lpgetSysQueueTokenHandler(const std::vector<OString> & i_rLines,std::vector<PrinterInfoManager::SystemPrintQueue> & o_rQueues,const SystemCommandParameters *)690 static void lpgetSysQueueTokenHandler(
691 const std::vector< OString >& i_rLines,
692 std::vector< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
693 const SystemCommandParameters* )
694 {
695 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
696 std::unordered_set< OUString > aUniqueSet;
697 std::unordered_set< OUString > aOnlySet;
698 aUniqueSet.insert( OUString( "_all" ) );
699 aUniqueSet.insert( OUString( "_default" ) );
700
701 // the eventual "all" attribute of the "_all" queue tells us, which
702 // printers are to be used for this user at all
703
704 // find _all: line
705 OString aAllLine( "_all:" );
706 OString aAllAttr( "all=" );
707 auto it = std::find_if(i_rLines.begin(), i_rLines.end(),
708 [&aAllLine](const OString& rLine) { return rLine.indexOf( aAllLine, 0 ) == 0; });
709 if( it != i_rLines.end() )
710 {
711 // now find the "all" attribute
712 ++it;
713 it = std::find_if(it, i_rLines.end(),
714 [&aAllAttr](const OString& rLine) { return WhitespaceToSpace( rLine ).startsWith( aAllAttr ); });
715 if( it != i_rLines.end() )
716 {
717 // insert the comma separated entries into the set of printers to use
718 OString aClean( WhitespaceToSpace( *it ) );
719 sal_Int32 nPos = aAllAttr.getLength();
720 while( nPos != -1 )
721 {
722 OString aTok( aClean.getToken( 0, ',', nPos ) );
723 if( !aTok.isEmpty() )
724 aOnlySet.insert( OStringToOUString( aTok, aEncoding ) );
725 }
726 }
727 }
728
729 bool bInsertAttribute = false;
730 OString aDescrStr( "description=" );
731 OString aLocStr( "location=" );
732 for (auto const& line : i_rLines)
733 {
734 sal_Int32 nPos = 0;
735 // find the begin of a new printer section
736 nPos = line.indexOf( ':', 0 );
737 if( nPos != -1 )
738 {
739 OUString aSysQueue( OStringToOUString( line.copy( 0, nPos ), aEncoding ) );
740 // do not insert duplicates (e.g. lpstat tends to produce such lines)
741 // in case there was a "_all" section, insert only those printer explicitly
742 // set in the "all" attribute
743 if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() &&
744 ( aOnlySet.empty() || aOnlySet.find( aSysQueue ) != aOnlySet.end() )
745 )
746 {
747 o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() );
748 o_rQueues.back().m_aQueue = aSysQueue;
749 o_rQueues.back().m_aLocation = aSysQueue;
750 aUniqueSet.insert( aSysQueue );
751 bInsertAttribute = true;
752 }
753 else
754 bInsertAttribute = false;
755 continue;
756 }
757 if( bInsertAttribute && ! o_rQueues.empty() )
758 {
759 // look for "description" attribute, insert as comment
760 nPos = line.indexOf( aDescrStr, 0 );
761 if( nPos != -1 )
762 {
763 OString aComment( WhitespaceToSpace( line.copy(nPos+12) ) );
764 if( !aComment.isEmpty() )
765 o_rQueues.back().m_aComment = OStringToOUString(aComment, aEncoding);
766 continue;
767 }
768 // look for "location" attribute, inser as location
769 nPos = line.indexOf( aLocStr, 0 );
770 if( nPos != -1 )
771 {
772 OString aLoc( WhitespaceToSpace( line.copy(nPos+9) ) );
773 if( !aLoc.isEmpty() )
774 o_rQueues.back().m_aLocation = OStringToOUString(aLoc, aEncoding);
775 continue;
776 }
777 }
778 }
779 }
780 #endif
standardSysQueueTokenHandler(const std::vector<OString> & i_rLines,std::vector<PrinterInfoManager::SystemPrintQueue> & o_rQueues,const SystemCommandParameters * i_pParms)781 static void standardSysQueueTokenHandler(
782 const std::vector< OString >& i_rLines,
783 std::vector< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
784 const SystemCommandParameters* i_pParms)
785 {
786 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
787 std::unordered_set< OUString > aUniqueSet;
788 OString aForeToken( i_pParms->pForeToken );
789 OString aAftToken( i_pParms->pAftToken );
790 /* Normal Unix print queue discovery, also used for Darwin 5 LPR printing
791 */
792 for (auto const& line : i_rLines)
793 {
794 sal_Int32 nPos = 0;
795
796 // search for a line describing a printer:
797 // find if there are enough tokens before the name
798 for( unsigned int i = 0; i < i_pParms->nForeTokenCount && nPos != -1; i++ )
799 {
800 nPos = line.indexOf( aForeToken, nPos );
801 if( nPos != -1 && line.getLength() >= nPos+aForeToken.getLength() )
802 nPos += aForeToken.getLength();
803 }
804 if( nPos != -1 )
805 {
806 // find if there is the token after the queue
807 sal_Int32 nAftPos = line.indexOf( aAftToken, nPos );
808 if( nAftPos != -1 )
809 {
810 // get the queue name between fore and aft tokens
811 OUString aSysQueue( OStringToOUString( line.copy( nPos, nAftPos - nPos ), aEncoding ) );
812 // do not insert duplicates (e.g. lpstat tends to produce such lines)
813 if( aUniqueSet.insert( aSysQueue ).second )
814 {
815 o_rQueues.emplace_back( );
816 o_rQueues.back().m_aQueue = aSysQueue;
817 o_rQueues.back().m_aLocation = aSysQueue;
818 }
819 }
820 }
821 }
822 }
823
824 static const struct SystemCommandParameters aParms[] =
825 {
826 #if defined(LINUX) || defined(NETBSD) || defined(DRAGONFLY) || defined(OPENBSD)
827 { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
828 { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
829 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler }
830 #else
831 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpget list", "lp -d \"(PRINTER)\"", "", ":", 0, lpgetSysQueueTokenHandler },
832 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler },
833 { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
834 { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }
835 #endif
836 };
837
run()838 void SystemQueueInfo::run()
839 {
840 osl_setThreadName("LPR psp::SystemQueueInfo");
841
842 char pBuffer[1024];
843 std::vector< OString > aLines;
844
845 /* Discover which command we can use to get a list of all printer queues */
846 for(const auto & rParm : aParms)
847 {
848 aLines.clear();
849 #if OSL_DEBUG_LEVEL > 1
850 fprintf( stderr, "trying print queue command \"%s\" ... ", rParm.pQueueCommand );
851 #endif
852 OString aCmdLine = rParm.pQueueCommand + OStringLiteral(" 2>/dev/null");
853 FILE *pPipe;
854 if( (pPipe = popen( aCmdLine.getStr(), "r" )) )
855 {
856 while( fgets( pBuffer, 1024, pPipe ) )
857 aLines.emplace_back( pBuffer );
858 if( ! pclose( pPipe ) )
859 {
860 std::vector< PrinterInfoManager::SystemPrintQueue > aSysPrintQueues;
861 rParm.pHandler( aLines, aSysPrintQueues, &rParm );
862 MutexGuard aGuard( m_aMutex );
863 m_bChanged = true;
864 m_aQueues = aSysPrintQueues;
865 m_aCommand = OUString::createFromAscii( rParm.pPrintCommand );
866 #if OSL_DEBUG_LEVEL > 1
867 fprintf( stderr, "success\n" );
868 #endif
869 break;
870 }
871 }
872 #if OSL_DEBUG_LEVEL > 1
873 fprintf( stderr, "failed\n" );
874 #endif
875 }
876 }
877
878 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
879