LCOV - code coverage report
Current view: top level - vcl/unx/generic/printer - cupsmgr.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 43 471 9.1 %
Date: 2015-06-13 12:38:46 Functions: 9 44 20.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <cups/cups.h>
      21             : #include <cups/http.h>
      22             : #include <cups/ipp.h>
      23             : #include <cups/ppd.h>
      24             : 
      25             : #include <unistd.h>
      26             : 
      27             : #include "cupsmgr.hxx"
      28             : 
      29             : #include "osl/thread.h"
      30             : #include "osl/diagnose.h"
      31             : #include "osl/conditn.hxx"
      32             : 
      33             : #include "rtl/ustrbuf.hxx"
      34             : 
      35             : #include <officecfg/Office/Common.hxx>
      36             : 
      37             : #include <vcl/button.hxx>
      38             : #include <vcl/dialog.hxx>
      39             : #include <vcl/fixed.hxx>
      40             : 
      41             : #include <algorithm>
      42             : 
      43             : using namespace psp;
      44             : using namespace osl;
      45             : 
      46             : struct GetPPDAttribs
      47             : {
      48             :     osl::Condition      m_aCondition;
      49             :     OString             m_aParameter;
      50             :     OString             m_aResult;
      51             :     int                 m_nRefs;
      52             :     bool*               m_pResetRunning;
      53             :     osl::Mutex*         m_pSyncMutex;
      54             : 
      55           0 :     GetPPDAttribs( const char * m_pParameter,
      56             :                    bool* pResetRunning, osl::Mutex* pSyncMutex )
      57             :             : m_aParameter( m_pParameter ),
      58             :               m_pResetRunning( pResetRunning ),
      59           0 :               m_pSyncMutex( pSyncMutex )
      60             :     {
      61           0 :         m_nRefs = 2;
      62           0 :         m_aCondition.reset();
      63           0 :     }
      64             : 
      65           0 :     ~GetPPDAttribs()
      66           0 :     {
      67           0 :         if( !m_aResult.isEmpty() )
      68           0 :             unlink( m_aResult.getStr() );
      69           0 :     }
      70             : 
      71           0 :     void unref()
      72             :     {
      73           0 :         if( --m_nRefs == 0 )
      74             :         {
      75           0 :             *m_pResetRunning = false;
      76           0 :             delete this;
      77             :         }
      78           0 :     }
      79             : 
      80           0 :     void executeCall()
      81             :     {
      82             :         // This CUPS method is not at all thread-safe we need
      83             :         // to dup the pointer to a static buffer it returns ASAP
      84           0 :         OString aResult = cupsGetPPD(m_aParameter.getStr());
      85           0 :         MutexGuard aGuard( *m_pSyncMutex );
      86           0 :         m_aResult = aResult;
      87           0 :         m_aCondition.set();
      88           0 :         unref();
      89           0 :     }
      90             : 
      91           0 :     OString waitResult( TimeValue *pDelay )
      92             :     {
      93           0 :         m_pSyncMutex->release();
      94             : 
      95           0 :         if (m_aCondition.wait( pDelay ) != Condition::result_ok
      96             :             )
      97             :         {
      98             :             SAL_WARN("vcl.unx.print",
      99             :                     "cupsGetPPD " << m_aParameter << " timed out");
     100             :         }
     101           0 :         m_pSyncMutex->acquire();
     102             : 
     103           0 :         OString aRetval = m_aResult;
     104           0 :         m_aResult.clear();
     105           0 :         unref();
     106             : 
     107           0 :         return aRetval;
     108             :     }
     109             : };
     110             : 
     111             : extern "C" {
     112           0 :     static void getPPDWorker(void* pData)
     113             :     {
     114           0 :         osl_setThreadName("CUPSManager getPPDWorker");
     115           0 :         GetPPDAttribs* pAttribs = static_cast<GetPPDAttribs*>(pData);
     116           0 :         pAttribs->executeCall();
     117           0 :     }
     118             : }
     119             : 
     120           0 : OString CUPSManager::threadedCupsGetPPD( const char* pPrinter )
     121             : {
     122           0 :     OString aResult;
     123             : 
     124           0 :     m_aGetPPDMutex.acquire();
     125             :     // if one thread hangs in cupsGetPPD already, don't start another
     126           0 :     if( ! m_bPPDThreadRunning )
     127             :     {
     128           0 :         m_bPPDThreadRunning = true;
     129             :         GetPPDAttribs* pAttribs = new GetPPDAttribs( pPrinter,
     130             :                                                      &m_bPPDThreadRunning,
     131           0 :                                                      &m_aGetPPDMutex );
     132             : 
     133           0 :         oslThread aThread = osl_createThread( getPPDWorker, pAttribs );
     134             : 
     135             :         TimeValue aValue;
     136           0 :         aValue.Seconds = 5;
     137           0 :         aValue.Nanosec = 0;
     138             : 
     139             :         // NOTE: waitResult release and acquires the GetPPD mutex
     140           0 :         aResult = pAttribs->waitResult( &aValue );
     141           0 :         osl_destroyThread( aThread );
     142             :     }
     143           0 :     m_aGetPPDMutex.release();
     144             : 
     145           0 :     return aResult;
     146             : }
     147             : 
     148           0 : static const char* setPasswordCallback( const char* pIn )
     149             : {
     150           0 :     const char* pRet = NULL;
     151             : 
     152           0 :     PrinterInfoManager& rMgr = PrinterInfoManager::get();
     153           0 :     if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check
     154           0 :         pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn );
     155           0 :     return pRet;
     156             : }
     157             : 
     158             : /*
     159             :  *  CUPSManager class
     160             :  */
     161             : 
     162          79 : CUPSManager* CUPSManager::tryLoadCUPS()
     163             : {
     164          79 :     CUPSManager* pManager = NULL;
     165          79 :     static const char* pEnv = getenv("SAL_DISABLE_CUPS");
     166             : 
     167          79 :     if (!pEnv || !*pEnv)
     168          79 :         pManager = new CUPSManager();
     169          79 :     return pManager;
     170             : }
     171             : 
     172             : extern "C"
     173             : {
     174          79 : static void run_dest_thread_stub( void* pThis )
     175             : {
     176          79 :     osl_setThreadName("CUPSManager cupsGetDests");
     177          79 :     CUPSManager::runDestThread( pThis );
     178          79 : }
     179             : }
     180             : 
     181          79 : CUPSManager::CUPSManager() :
     182             :         PrinterInfoManager( CUPS ),
     183             :         m_nDests( 0 ),
     184             :         m_pDests( NULL ),
     185             :         m_bNewDests( false ),
     186             :         m_bPPDThreadRunning( false ),
     187          79 :         batchMode( false )
     188             : {
     189          79 :     m_aDestThread = osl_createThread( run_dest_thread_stub, this );
     190          79 : }
     191             : 
     192           0 : CUPSManager::~CUPSManager()
     193             : {
     194           0 :     if( m_aDestThread )
     195             :     {
     196             :         // if the thread is still running here, then
     197             :         // cupsGetDests is hung; terminate the thread instead of joining
     198           0 :         osl_terminateThread( m_aDestThread );
     199           0 :         osl_destroyThread( m_aDestThread );
     200             :     }
     201             : 
     202           0 :     if (m_nDests && m_pDests)
     203           0 :         cupsFreeDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
     204           0 : }
     205             : 
     206          79 : void CUPSManager::runDestThread( void* pThis )
     207             : {
     208          79 :     static_cast<CUPSManager*>(pThis)->runDests();
     209          79 : }
     210             : 
     211          79 : void CUPSManager::runDests()
     212             : {
     213             :     SAL_INFO("vcl.unx.print", "starting cupsGetDests");
     214          79 :     cups_dest_t* pDests = NULL;
     215             : 
     216             :     // n#722902 - do a fast-failing check for cups working *at all* first
     217             :     http_t* p_http;
     218          79 :     if( (p_http=httpConnectEncrypt(
     219             :              cupsServer(),
     220             :              ippPort(),
     221          79 :              cupsEncryption())) != NULL )
     222             :     {
     223             :         // neat, cups is up, clean up the canary
     224           0 :         httpClose(p_http);
     225             : 
     226           0 :         int nDests = cupsGetDests( &pDests );
     227             :         SAL_INFO("vcl.unx.print", "came out of cupsGetDests");
     228             : 
     229           0 :         osl::MutexGuard aGuard( m_aCUPSMutex );
     230           0 :         m_nDests = nDests;
     231           0 :         m_pDests = pDests;
     232           0 :         m_bNewDests = true;
     233           0 :         SAL_INFO("vcl.unx.print", "finished cupsGetDests");
     234             :     }
     235          79 : }
     236             : 
     237          79 : void CUPSManager::initialize()
     238             : {
     239             :     // get normal printers, clear printer list
     240          79 :     PrinterInfoManager::initialize();
     241             : 
     242             :     // check whether thread has completed
     243             :     // if not behave like old printing system
     244          79 :     osl::MutexGuard aGuard( m_aCUPSMutex );
     245             : 
     246          79 :     if( ! m_bNewDests )
     247          79 :         return;
     248             : 
     249             :     // dest thread has run, clean up
     250           0 :     if( m_aDestThread )
     251             :     {
     252           0 :         osl_joinWithThread( m_aDestThread );
     253           0 :         osl_destroyThread( m_aDestThread );
     254           0 :         m_aDestThread = NULL;
     255             :     }
     256           0 :     m_bNewDests = false;
     257             : 
     258             :     // clear old stuff
     259           0 :     m_aCUPSDestMap.clear();
     260             : 
     261           0 :     if( ! (m_nDests && m_pDests ) )
     262           0 :         return;
     263             : 
     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           0 :     bool bUsePDF = false;
     270           0 :     cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests);
     271             :     const char* pOpt = cupsGetOption( "printer-info",
     272             :                                                       pDest->num_options,
     273           0 :                                                       pDest->options );
     274           0 :     if( pOpt )
     275             :     {
     276           0 :         m_bUseIncludeFeature = true;
     277           0 :         bUsePDF = officecfg::Office::Common::Print::Option::Printer::PDFAsStandardPrintJobFormat::get();
     278             :     }
     279             : 
     280           0 :     m_aGlobalDefaults.setDefaultBackend(bUsePDF);
     281             : 
     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           0 :     m_bUseJobPatch = false;
     286             : 
     287           0 :     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
     288           0 :     int nPrinter = m_nDests;
     289             : 
     290             :     // reset global default PPD options; these are queried on demand from CUPS
     291           0 :     m_aGlobalDefaults.m_pParser = NULL;
     292           0 :     m_aGlobalDefaults.m_aContext = PPDContext();
     293             : 
     294             :     // add CUPS printers, should there be a printer
     295             :     // with the same name as a CUPS printer, overwrite it
     296           0 :     while( nPrinter-- )
     297             :     {
     298           0 :         pDest = static_cast<cups_dest_t*>(m_pDests)+nPrinter;
     299           0 :         OUString aPrinterName = OStringToOUString( pDest->name, aEncoding );
     300           0 :         if( pDest->instance && *pDest->instance )
     301             :         {
     302           0 :             OUStringBuffer aBuf( 256 );
     303           0 :             aBuf.append( aPrinterName );
     304           0 :             aBuf.append( '/' );
     305           0 :             aBuf.append( OStringToOUString( pDest->instance, aEncoding ) );
     306           0 :             aPrinterName = aBuf.makeStringAndClear();
     307             :         }
     308             : 
     309             :         // initialize printer with possible configuration from psprint.conf
     310           0 :         bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end();
     311           0 :         Printer aPrinter = m_aPrinters[ aPrinterName ];
     312           0 :         if( bSetToGlobalDefaults )
     313           0 :             aPrinter.m_aInfo = m_aGlobalDefaults;
     314           0 :         aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
     315           0 :         if( pDest->is_default )
     316           0 :             m_aDefaultPrinter = aPrinterName;
     317             : 
     318           0 :         for( int k = 0; k < pDest->num_options; k++ )
     319             :         {
     320           0 :             if(!strcmp(pDest->options[k].name, "printer-info"))
     321           0 :                 aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding);
     322           0 :             if(!strcmp(pDest->options[k].name, "printer-location"))
     323           0 :                 aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding);
     324             :         }
     325             : 
     326           0 :         OUStringBuffer aBuf( 256 );
     327           0 :         aBuf.appendAscii( "CUPS:" );
     328           0 :         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           0 :         aPrinter.m_aInfo.m_pParser = NULL;
     337           0 :         aPrinter.m_aInfo.m_aContext.setParser( NULL );
     338           0 :         std::unordered_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName );
     339           0 :         if( c_it != m_aDefaultContexts.end() )
     340             :         {
     341           0 :             aPrinter.m_aInfo.m_pParser = c_it->second.getParser();
     342           0 :             aPrinter.m_aInfo.m_aContext = c_it->second;
     343             :         }
     344           0 :         aPrinter.m_aInfo.setDefaultBackend(bUsePDF);
     345           0 :         aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear();
     346           0 :         aPrinter.m_bModified = false;
     347             : 
     348           0 :         m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter;
     349           0 :         m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter;
     350           0 :     }
     351             : 
     352             :     // remove everything that is not a CUPS printer and not
     353             :     // a special purpose printer (PDF, Fax)
     354           0 :     std::list< OUString > aRemovePrinters;
     355           0 :     for( std::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin();
     356           0 :          it != m_aPrinters.end(); ++it )
     357             :     {
     358           0 :         if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() )
     359           0 :             continue;
     360             : 
     361           0 :         if( !it->second.m_aInfo.m_aFeatures.isEmpty() )
     362           0 :             continue;
     363           0 :         aRemovePrinters.push_back( it->first );
     364             :     }
     365           0 :     while( aRemovePrinters.begin() != aRemovePrinters.end() )
     366             :     {
     367           0 :         m_aPrinters.erase( aRemovePrinters.front() );
     368           0 :         aRemovePrinters.pop_front();
     369             :     }
     370             : 
     371           0 :     cupsSetPasswordCB( setPasswordCallback );
     372             : }
     373             : 
     374           0 : static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext )
     375             : {
     376           0 :     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
     377           0 :     for( int i = 0; i < pPPDGroup->num_options; i++ )
     378             :     {
     379           0 :         ppd_option_t* pOption = pPPDGroup->options + i;
     380           0 :         for( int n = 0; n < pOption->num_choices; n++ )
     381             :         {
     382           0 :             ppd_choice_t* pChoice = pOption->choices + n;
     383           0 :             if( pChoice->marked )
     384             :             {
     385           0 :                 const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) );
     386           0 :                 if( pKey )
     387             :                 {
     388           0 :                     const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) );
     389           0 :                     if( pValue )
     390             :                     {
     391           0 :                         if( pValue != pKey->getDefaultValue() )
     392             :                         {
     393           0 :                             rContext.setValue( pKey, pValue, true );
     394             :                             SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is set to " << pChoice->choice);
     395             : 
     396             :                         }
     397             :                         else
     398             :                             SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is defaulted to " << pChoice->choice);
     399             :                     }
     400             :                     else
     401             :                         SAL_INFO("vcl.unx.print", "caution: value " << pChoice->choice << " not found in key " << pOption->keyword);
     402             :                 }
     403             :                 else
     404             :                     SAL_INFO("vcl.unx.print", "caution: key " << pOption->keyword << " not found in parser");
     405             :             }
     406             :         }
     407             :     }
     408             : 
     409             :     // recurse through subgroups
     410           0 :     for( int g = 0; g < pPPDGroup->num_subgroups; g++ )
     411             :     {
     412           0 :         updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext );
     413             :     }
     414           0 : }
     415             : 
     416           0 : const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter )
     417             : {
     418           0 :     const PPDParser* pNewParser = NULL;
     419           0 :     OUString aPrinter;
     420             : 
     421           0 :     if( rPrinter.startsWith("CUPS:") )
     422           0 :         aPrinter = rPrinter.copy( 5 );
     423             :     else
     424           0 :         aPrinter = rPrinter;
     425             : 
     426           0 :     if( m_aCUPSMutex.tryToAcquire() )
     427             :     {
     428           0 :         if (m_nDests && m_pDests)
     429             :         {
     430             :             std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
     431           0 :             m_aCUPSDestMap.find( aPrinter );
     432           0 :             if( dest_it != m_aCUPSDestMap.end() )
     433             :             {
     434           0 :                 cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + dest_it->second;
     435           0 :                 OString aPPDFile = threadedCupsGetPPD( pDest->name );
     436             :                 SAL_INFO("vcl.unx.print",
     437             :                         "PPD for " << aPrinter << " is " << aPPDFile);
     438           0 :                 if( !aPPDFile.isEmpty() )
     439             :                 {
     440           0 :                     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
     441           0 :                     OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) );
     442             :                     // update the printer info with context information
     443           0 :                     ppd_file_t* pPPD = ppdOpenFile( aPPDFile.getStr() );
     444           0 :                     if( pPPD )
     445             :                     {
     446             :                         // create the new parser
     447           0 :                         PPDParser* pCUPSParser = new PPDParser( aFileName );
     448           0 :                         pCUPSParser->m_aFile = rPrinter;
     449           0 :                         pNewParser = pCUPSParser;
     450             : 
     451           0 :                         /*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           0 :                         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           0 :                         PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
     458             : 
     459             :                         // remember the default context for later use
     460           0 :                         PPDContext& rContext = m_aDefaultContexts[ aPrinter ];
     461           0 :                         rContext.setParser( pNewParser );
     462             :                         // set system default paper; printer CUPS PPD options
     463             :                         // may overwrite it
     464           0 :                         setDefaultPaper( rContext );
     465           0 :                         for( int i = 0; i < pPPD->num_groups; i++ )
     466           0 :                             updatePrinterContextInfo( pPPD->groups + i, rContext );
     467             : 
     468           0 :                         rInfo.m_pParser = pNewParser;
     469           0 :                         rInfo.m_aContext = rContext;
     470             : 
     471             :                         // clean up the mess
     472           0 :                         ppdClose( pPPD );
     473             :                     }
     474             :                     else
     475             :                         SAL_INFO("vcl.unx.print", "ppdOpenFile failed, falling back to generic driver");
     476             : 
     477             :                     // remove temporary PPD file
     478           0 :                     unlink( aPPDFile.getStr() );
     479             :                 }
     480             :                 else
     481           0 :                     SAL_INFO("vcl.unx.print", "cupsGetPPD failed, falling back to generic driver");
     482             :             }
     483             :             else
     484             :                 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter);
     485             :         }
     486           0 :         m_aCUPSMutex.release();
     487             :     }
     488             :     else
     489             :         SAL_WARN("vcl.unx.print", "could not acquire CUPS mutex !!!" );
     490             : 
     491           0 :     if( ! pNewParser )
     492             :     {
     493             :         // get the default PPD
     494           0 :         pNewParser = PPDParser::getParser( OUString( "SGENPRT" ) );
     495             : 
     496           0 :         PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
     497             : 
     498           0 :         rInfo.m_pParser = pNewParser;
     499           0 :         rInfo.m_aContext.setParser( pNewParser );
     500             :     }
     501             : 
     502           0 :     return pNewParser;
     503             : }
     504             : 
     505           0 : void CUPSManager::setupJobContextData( JobData& rData )
     506             : {
     507             :     std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
     508           0 :         m_aCUPSDestMap.find( rData.m_aPrinterName );
     509             : 
     510           0 :     if( dest_it == m_aCUPSDestMap.end() )
     511           0 :         return PrinterInfoManager::setupJobContextData( rData );
     512             : 
     513             :     std::unordered_map< OUString, Printer, OUStringHash >::iterator p_it =
     514           0 :         m_aPrinters.find( rData.m_aPrinterName );
     515           0 :     if( p_it == m_aPrinters.end() ) // huh ?
     516             :     {
     517             :         SAL_WARN("vcl.unx.print", "CUPS printer list in disorder, "
     518             :                 "no dest for printer " << rData.m_aPrinterName);
     519           0 :         return;
     520             :     }
     521             : 
     522           0 :     if( p_it->second.m_aInfo.m_pParser == NULL )
     523             :     {
     524             :         // in turn calls createCUPSParser
     525             :         // which updates the printer info
     526           0 :         p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName );
     527             :     }
     528           0 :     if( p_it->second.m_aInfo.m_aContext.getParser() == NULL )
     529             :     {
     530           0 :         OUString aPrinter;
     531           0 :         if( p_it->second.m_aInfo.m_aDriverName.startsWith("CUPS:") )
     532           0 :             aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 );
     533             :         else
     534           0 :             aPrinter = p_it->second.m_aInfo.m_aDriverName;
     535             : 
     536           0 :         p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ];
     537             :     }
     538             : 
     539           0 :     rData.m_pParser     = p_it->second.m_aInfo.m_pParser;
     540           0 :     rData.m_aContext    = p_it->second.m_aInfo.m_aContext;
     541             : }
     542             : 
     543           0 : FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
     544             : {
     545             :     OSL_TRACE( "startSpool: %s, %s",
     546             :                OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(),
     547             :               bQuickCommand ? "true" : "false" );
     548             : 
     549           0 :     if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
     550             :     {
     551             :         OSL_TRACE( "defer to PrinterInfoManager::startSpool" );
     552           0 :         return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
     553             :     }
     554             : 
     555           0 :     OUString aTmpURL, aTmpFile;
     556           0 :     osl_createTempFile( NULL, NULL, &aTmpURL.pData );
     557           0 :     osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData );
     558           0 :     OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() );
     559           0 :     FILE* fp = fopen( aSysFile.getStr(), "w" );
     560           0 :     if( fp )
     561           0 :         m_aSpoolFiles[fp] = aSysFile;
     562             : 
     563           0 :     return fp;
     564             : }
     565             : 
     566             : struct less_ppd_key : public ::std::binary_function<double, double, bool>
     567             : {
     568           0 :     bool operator()(const PPDKey* left, const PPDKey* right)
     569           0 :     { return left->getOrderDependency() < right->getOrderDependency(); }
     570             : };
     571             : 
     572           0 : void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions )
     573             : {
     574           0 :     rNumOptions = 0;
     575           0 :     *rOptions = NULL;
     576             : 
     577             :     // emit features ordered to OrderDependency
     578             :     // ignore features that are set to default
     579             : 
     580             :     // sanity check
     581           0 :     if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser )
     582             :     {
     583             :         int i;
     584           0 :         int nKeys = rJob.m_aContext.countValuesModified();
     585           0 :         ::std::vector< const PPDKey* > aKeys( nKeys );
     586           0 :         for(  i = 0; i < nKeys; i++ )
     587           0 :             aKeys[i] = rJob.m_aContext.getModifiedKey( i );
     588           0 :         ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
     589             : 
     590           0 :         for( i = 0; i < nKeys; i++ )
     591             :         {
     592           0 :             const PPDKey* pKey = aKeys[i];
     593           0 :             const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
     594           0 :             OUString sPayLoad;
     595           0 :             if (pValue && pValue->m_eType == eInvocation)
     596             :             {
     597           0 :                 sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption;
     598             :             }
     599             : 
     600           0 :             if (!sPayLoad.isEmpty())
     601             :             {
     602           0 :                 OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US );
     603           0 :                 OString aValue = OUStringToOString( sPayLoad, RTL_TEXTENCODING_ASCII_US );
     604           0 :                 rNumOptions = cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
     605             :             }
     606           0 :         }
     607             :     }
     608             : 
     609           0 :     if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 )
     610             :     {
     611           0 :         OString aVal( OString::number( rJob.m_nCopies ) );
     612           0 :         rNumOptions = cupsAddOption( "copies", aVal.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
     613           0 :         aVal = OString::boolean(rJob.m_bCollate);
     614           0 :         rNumOptions = cupsAddOption( "collate", aVal.getStr(), rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
     615             :     }
     616           0 :     if( ! bBanner )
     617             :     {
     618           0 :         rNumOptions = cupsAddOption( "job-sheets", "none", rNumOptions, reinterpret_cast<cups_option_t**>(rOptions) );
     619             :     }
     620           0 : }
     621             : 
     622           0 : bool CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber )
     623             : {
     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
     628             :                );
     629             : 
     630           0 :     osl::MutexGuard aGuard( m_aCUPSMutex );
     631             : 
     632             :     std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
     633           0 :         m_aCUPSDestMap.find( rPrintername );
     634           0 :     if( dest_it == m_aCUPSDestMap.end() )
     635             :     {
     636             :         OSL_TRACE( "defer to PrinterInfoManager::endSpool" );
     637           0 :         return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner, rFaxNumber );
     638             :     }
     639             : 
     640           0 :     std::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
     641           0 :     if( it == m_aSpoolFiles.end() )
     642           0 :         return false;
     643           0 :     fclose( pFile );
     644           0 :     PendingJob job( rPrintername, rJobTitle, rDocumentJobData, bBanner, rFaxNumber, it->second );
     645           0 :     m_aSpoolFiles.erase( pFile );
     646           0 :     pendingJobs.push_back( job );
     647           0 :     if( !batchMode ) // process immediately, otherwise will be handled by flushBatchPrint()
     648           0 :         return processPendingJobs();
     649           0 :     return true;
     650             : }
     651             : 
     652           0 : bool CUPSManager::startBatchPrint()
     653             : {
     654           0 :     batchMode = true;
     655           0 :     return true;
     656             : }
     657             : 
     658           0 : bool CUPSManager::supportsBatchPrint() const
     659             : {
     660           0 :     return true;
     661             : }
     662             : 
     663           0 : bool CUPSManager::flushBatchPrint()
     664             : {
     665           0 :     osl::MutexGuard aGuard( m_aCUPSMutex );
     666           0 :     batchMode = false;                 // reset the batch print mode
     667           0 :     return processPendingJobs();
     668             : }
     669             : 
     670           0 : bool CUPSManager::processPendingJobs()
     671             : {
     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           0 :     PendingJob currentJobData;
     674           0 :     bool first = true;
     675           0 :     std::vector< OString > files;
     676           0 :     bool ok = true;
     677           0 :     while( !pendingJobs.empty())
     678             :     {
     679           0 :         if( first )
     680             :         {
     681           0 :             currentJobData = pendingJobs.front();
     682           0 :             first = false;
     683             :         }
     684           0 :         else if( currentJobData.printerName != pendingJobs.front().printerName
     685           0 :                 || currentJobData.jobTitle != pendingJobs.front().jobTitle
     686           0 :                 || currentJobData.jobData != pendingJobs.front().jobData
     687           0 :                 || currentJobData.banner != pendingJobs.front().banner )
     688             :         {
     689           0 :             if( !printJobs( currentJobData, files ))
     690           0 :                 ok = false;
     691           0 :             files.clear();
     692           0 :             currentJobData = pendingJobs.front();
     693             :         }
     694           0 :         files.push_back( pendingJobs.front().file );
     695           0 :         pendingJobs.pop_front();
     696             :     }
     697           0 :     if( !first )
     698             :     {
     699           0 :         if( !printJobs( currentJobData, files )) // print the last batch
     700           0 :             ok = false;
     701             :     }
     702           0 :     return ok;
     703             : }
     704             : 
     705           0 : bool CUPSManager::printJobs( const PendingJob& job, const std::vector< OString >& files )
     706             : {
     707             :     std::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
     708           0 :         m_aCUPSDestMap.find( job.printerName );
     709             : 
     710           0 :         rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
     711             : 
     712             :         // setup cups options
     713           0 :         int nNumOptions = 0;
     714           0 :         cups_option_t* pOptions = NULL;
     715           0 :         getOptionsFromDocumentSetup( job.jobData, job.banner, nNumOptions, reinterpret_cast<void**>(&pOptions) );
     716             : 
     717           0 :         OString sJobName(OUStringToOString(job.jobTitle, aEnc));
     718             : 
     719             :         //fax4CUPS, "the job name will be dialled for you"
     720             :         //so override the jobname with the desired number
     721           0 :         if (!job.faxNumber.isEmpty())
     722             :         {
     723           0 :             sJobName = OUStringToOString(job.faxNumber, aEnc);
     724             :         }
     725             : 
     726           0 :         cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + dest_it->second;
     727             : 
     728           0 :         std::vector< const char* > fnames;
     729           0 :         for( std::vector< OString >::const_iterator it = files.begin();
     730           0 :              it != files.end();
     731             :              ++it )
     732           0 :             fnames.push_back( it->getStr());
     733             : 
     734             :         int nJobID = cupsPrintFiles(pDest->name,
     735           0 :             fnames.size(),
     736             :             fnames.data(),
     737             :             sJobName.getStr(),
     738           0 :             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           0 :         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
     751             : 
     752           0 :         for( std::vector< OString >::const_iterator it = files.begin();
     753           0 :              it != files.end();
     754             :              ++it )
     755           0 :             unlink( it->getStr());
     756             : 
     757           0 :         if( pOptions )
     758           0 :             cupsFreeOptions( nNumOptions, pOptions );
     759             : 
     760           0 :     return nJobID != 0;
     761             : }
     762             : 
     763           0 : void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
     764             : {
     765           0 :     PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo );
     766           0 : }
     767             : 
     768          48 : bool CUPSManager::checkPrintersChanged( bool bWait )
     769             : {
     770          48 :     bool bChanged = false;
     771          48 :     if( bWait )
     772             :     {
     773          48 :         if(  m_aDestThread )
     774             :         {
     775             :             // initial asynchronous detection still running
     776             :             SAL_INFO("vcl.unx.print", "syncing cups discovery thread");
     777          48 :             osl_joinWithThread( m_aDestThread );
     778          48 :             osl_destroyThread( m_aDestThread );
     779          48 :             m_aDestThread = NULL;
     780             :             SAL_INFO("vcl.unx.print", "done: syncing cups discovery thread");
     781             :         }
     782             :         else
     783             :         {
     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
     790             : 
     791             :             // there is no way to query CUPS whether the printer list has changed
     792             :             // so get the dest list anew
     793           0 :             if( m_nDests && m_pDests )
     794           0 :                 cupsFreeDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
     795           0 :             m_nDests = 0;
     796           0 :             m_pDests = NULL;
     797           0 :             runDests();
     798             :         }
     799             :     }
     800          48 :     if( m_aCUPSMutex.tryToAcquire() )
     801             :     {
     802          48 :         bChanged = m_bNewDests;
     803          48 :         m_aCUPSMutex.release();
     804             :     }
     805             : 
     806          48 :     if( ! bChanged )
     807             :     {
     808          48 :         bChanged = PrinterInfoManager::checkPrintersChanged( bWait );
     809             :         // #i54375# ensure new merging with CUPS list in :initialize
     810          48 :         if( bChanged )
     811           0 :             m_bNewDests = true;
     812             :     }
     813             : 
     814          48 :     if( bChanged )
     815           0 :         initialize();
     816             : 
     817          48 :     return bChanged;
     818             : }
     819             : 
     820           0 : bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver )
     821             : {
     822             :     // don't touch the CUPS printers
     823           0 :     if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ||
     824           0 :         rDriver.startsWith("CUPS:")
     825             :         )
     826           0 :         return false;
     827           0 :     return PrinterInfoManager::addPrinter( rName, rDriver );
     828             : }
     829             : 
     830           0 : bool CUPSManager::removePrinter( const OUString& rName, bool bCheck )
     831             : {
     832             :     // don't touch the CUPS printers
     833           0 :     if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() )
     834           0 :         return false;
     835           0 :     return PrinterInfoManager::removePrinter( rName, bCheck );
     836             : }
     837             : 
     838           0 : bool CUPSManager::setDefaultPrinter( const OUString& rName )
     839             : {
     840           0 :     bool bSuccess = false;
     841             :     std::unordered_map< OUString, int, OUStringHash >::iterator nit =
     842           0 :         m_aCUPSDestMap.find( rName );
     843           0 :     if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() )
     844             :     {
     845           0 :         cups_dest_t* pDests = static_cast<cups_dest_t*>(m_pDests);
     846           0 :         for( int i = 0; i < m_nDests; i++ )
     847           0 :             pDests[i].is_default = 0;
     848           0 :         pDests[ nit->second ].is_default = 1;
     849           0 :         cupsSetDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
     850           0 :         m_aDefaultPrinter = rName;
     851           0 :         m_aCUPSMutex.release();
     852           0 :         bSuccess = true;
     853             :     }
     854             :     else
     855           0 :         bSuccess = PrinterInfoManager::setDefaultPrinter( rName );
     856             : 
     857           0 :     return bSuccess;
     858             : }
     859             : 
     860           0 : bool CUPSManager::writePrinterConfig()
     861             : {
     862           0 :     bool bDestModified = false;
     863           0 :     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
     864             : 
     865           0 :     for( std::unordered_map< OUString, Printer, OUStringHash >::iterator prt =
     866           0 :              m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt )
     867             :     {
     868             :         std::unordered_map< OUString, int, OUStringHash >::iterator nit =
     869           0 :             m_aCUPSDestMap.find( prt->first );
     870           0 :         if( nit == m_aCUPSDestMap.end() )
     871           0 :             continue;
     872             : 
     873           0 :         if( ! prt->second.m_bModified )
     874           0 :             continue;
     875             : 
     876           0 :         if( m_aCUPSMutex.tryToAcquire() )
     877             :         {
     878           0 :             bDestModified = true;
     879           0 :             cups_dest_t* pDest = static_cast<cups_dest_t*>(m_pDests) + nit->second;
     880           0 :             PrinterInfo& rInfo = prt->second.m_aInfo;
     881             : 
     882             :             // create new option list
     883           0 :             int nNewOptions = 0;
     884           0 :             cups_option_t* pNewOptions = NULL;
     885           0 :             int nValues = rInfo.m_aContext.countValuesModified();
     886           0 :             for( int i = 0; i < nValues; i++ )
     887             :             {
     888           0 :                 const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i );
     889           0 :                 const PPDValue* pValue = rInfo.m_aContext.getValue( pKey );
     890           0 :                 if( pKey && pValue ) // sanity check
     891             :                 {
     892           0 :                     OString aName = OUStringToOString( pKey->getKey(), aEncoding );
     893           0 :                     OString aValue = OUStringToOString( pValue->m_aOption, aEncoding );
     894           0 :                     nNewOptions = cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions );
     895             :                 }
     896             :             }
     897             :             // set PPD options on CUPS dest
     898           0 :             cupsFreeOptions( pDest->num_options, pDest->options );
     899           0 :             pDest->num_options = nNewOptions;
     900           0 :             pDest->options = pNewOptions;
     901           0 :             m_aCUPSMutex.release();
     902             :         }
     903             :     }
     904           0 :     if( bDestModified && m_aCUPSMutex.tryToAcquire() )
     905             :     {
     906           0 :         cupsSetDests( m_nDests, static_cast<cups_dest_t*>(m_pDests) );
     907           0 :         m_aCUPSMutex.release();
     908             :     }
     909             : 
     910           0 :     return PrinterInfoManager::writePrinterConfig();
     911             : }
     912             : 
     913             : namespace
     914             : {
     915             :     class RTSPWDialog : public ModalDialog
     916             :     {
     917             :         VclPtr<FixedText> m_pText;
     918             :         VclPtr<Edit>      m_pUserEdit;
     919             :         VclPtr<Edit>      m_pPassEdit;
     920             : 
     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;
     927             :     };
     928             : 
     929           0 :     RTSPWDialog::RTSPWDialog( const OString& rServer, const OString& rUserName, vcl::Window* pParent )
     930             :         : ModalDialog(pParent, "CUPSPasswordDialog",
     931           0 :             "vcl/ui/cupspassworddialog.ui")
     932             :     {
     933           0 :         get(m_pText, "text");
     934           0 :         get(m_pUserEdit, "user");
     935           0 :         get(m_pPassEdit, "pass");
     936             : 
     937           0 :         OUString aText(m_pText->GetText());
     938           0 :         aText = aText.replaceFirst("%s", OStringToOUString(rServer, osl_getThreadTextEncoding()));
     939           0 :         m_pText->SetText(aText);
     940           0 :         m_pUserEdit->SetText( OStringToOUString(rUserName, osl_getThreadTextEncoding()));
     941           0 :     }
     942             : 
     943           0 :     RTSPWDialog::~RTSPWDialog()
     944             :     {
     945           0 :         disposeOnce();
     946           0 :     }
     947             : 
     948           0 :     void RTSPWDialog::dispose()
     949             :     {
     950           0 :         m_pText.clear();
     951           0 :         m_pUserEdit.clear();
     952           0 :         m_pPassEdit.clear();
     953           0 :         ModalDialog::dispose();
     954           0 :     }
     955             : 
     956           0 :     OString RTSPWDialog::getUserName() const
     957             :     {
     958           0 :         return OUStringToOString( m_pUserEdit->GetText(), osl_getThreadTextEncoding() );
     959             :     }
     960             : 
     961           0 :     OString RTSPWDialog::getPassword() const
     962             :     {
     963           0 :         return OUStringToOString( m_pPassEdit->GetText(), osl_getThreadTextEncoding() );
     964             :     }
     965             : 
     966           0 :     bool AuthenticateQuery(const OString& rServer, OString& rUserName, OString& rPassword)
     967             :     {
     968           0 :         bool bRet = false;
     969             : 
     970           0 :         ScopedVclPtrInstance<RTSPWDialog> aDialog(rServer, rUserName, nullptr);
     971           0 :         if (aDialog->Execute())
     972             :         {
     973           0 :             rUserName = aDialog->getUserName();
     974           0 :             rPassword = aDialog->getPassword();
     975           0 :             bRet = true;
     976             :         }
     977             : 
     978           0 :         return bRet;
     979             :     }
     980             : }
     981             : 
     982           0 : const char* CUPSManager::authenticateUser( const char* /*pIn*/ )
     983             : {
     984           0 :     const char* pRet = NULL;
     985             : 
     986           0 :     osl::MutexGuard aGuard( m_aCUPSMutex );
     987             : 
     988           0 :     OString aUser = cupsUser();
     989           0 :     OString aServer = cupsServer();
     990           0 :     OString aPassword;
     991           0 :     if (AuthenticateQuery(aServer, aUser, aPassword))
     992             :     {
     993           0 :         m_aPassword = aPassword;
     994           0 :         m_aUser = aUser;
     995           0 :         cupsSetUser( m_aUser.getStr() );
     996           0 :         pRet = m_aPassword.getStr();
     997             :     }
     998             : 
     999           0 :     return pRet;
    1000         801 : }
    1001             : 
    1002             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11