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