| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #ifdef ENABLE_CUPS |
| #include <cups/cups.h> |
| #include <cups/ppd.h> |
| |
| #else // !ENABLE_CUPS |
| typedef void ppd_file_t; |
| typedef void cups_dest_t; |
| typedef void cups_option_t; |
| #endif |
| |
| #include <unistd.h> |
| |
| #include "cupsmgr.hxx" |
| |
| #include "osl/thread.h" |
| #include "osl/diagnose.h" |
| #include "osl/conditn.hxx" |
| |
| #include "rtl/ustrbuf.hxx" |
| |
| #include <algorithm> |
| #include <setjmp.h> |
| #include <signal.h> |
| |
| #define CUPS_LIB_NAME "libcups.so.2" |
| |
| namespace psp |
| { |
| class CUPSWrapper |
| { |
| oslModule m_pLib; |
| osl::Mutex m_aGetPPDMutex; |
| bool m_bPPDThreadRunning; |
| |
| int (*m_pcupsPrintFile)(const char*, const char*, const char*, int, cups_option_t*); |
| int (*m_pcupsGetDests)(cups_dest_t**); |
| void (*m_pcupsSetDests)(int,cups_dest_t*); |
| void (*m_pcupsFreeDests)(int,cups_dest_t*); |
| const char* (*m_pcupsGetPPD)(const char*); |
| int (*m_pcupsMarkOptions)(ppd_file_t*,int,cups_option_t*); |
| int (*m_pcupsAddOption)(const char*,const char*,int,cups_option_t**); |
| void (*m_pcupsFreeOptions)(int,cups_option_t*); |
| ppd_file_t* (*m_pppdOpenFile)(const char* pFile); |
| void (*m_pppdClose)(ppd_file_t*); |
| const char* (*m_pcupsServer)(); |
| void (*m_pcupsSetPasswordCB)(const char*(cb)(const char*)); |
| const char* (*m_pcupsUser)(); |
| void (*m_pcupsSetUser)(const char*); |
| const char* (*m_pcupsGetOption)(const char*,int,cups_option_t*); |
| |
| oslGenericFunction loadSymbol( const char* ); |
| public: |
| CUPSWrapper(); |
| ~CUPSWrapper(); |
| |
| bool isValid(); |
| |
| int cupsGetDests(cups_dest_t** pDests) |
| { return m_pcupsGetDests(pDests); } |
| |
| void cupsSetDests( int nDests, cups_dest_t* pDests ) |
| { m_pcupsSetDests( nDests, pDests ); } |
| |
| void cupsFreeDests(int nDests, cups_dest_t* pDests) |
| { m_pcupsFreeDests(nDests, pDests); } |
| |
| int cupsPrintFile( const char* pPrinter, |
| const char* pFileName, |
| const char* pTitle, |
| int nOptions, |
| cups_option_t* pOptions ) |
| { return m_pcupsPrintFile( pPrinter, pFileName, pTitle, nOptions, pOptions ); } |
| |
| rtl::OString cupsGetPPD( const char* pPrinter ); |
| |
| int cupsMarkOptions(ppd_file_t* pPPD, int nOptions, cups_option_t* pOptions ) |
| { return m_pcupsMarkOptions(pPPD, nOptions, pOptions); } |
| |
| int cupsAddOption( const char* pName, const char* pValue, int nOptions, cups_option_t** pOptions ) |
| { return m_pcupsAddOption( pName, pValue, nOptions, pOptions ); } |
| |
| void cupsFreeOptions( int nOptions, cups_option_t* pOptions ) |
| { m_pcupsFreeOptions( nOptions, pOptions ); } |
| |
| ppd_file_t* ppdOpenFile( const char* pFileName ) |
| { return m_pppdOpenFile( pFileName ); } |
| |
| void ppdClose( ppd_file_t* pPPD ) |
| { m_pppdClose( pPPD ); } |
| |
| const char *cupsServer(void) |
| { return m_pcupsServer(); } |
| |
| const char *cupsUser(void) |
| { return m_pcupsUser(); } |
| |
| void cupsSetPasswordCB(const char *(*cb)(const char *)) |
| { m_pcupsSetPasswordCB( cb ); } |
| |
| void cupsSetUser(const char *user) |
| { m_pcupsSetUser( user ); } |
| |
| const char* cupsGetOption(const char* name, int num_options, cups_option_t* options) |
| { return m_pcupsGetOption( name, num_options, options ); } |
| |
| }; |
| } |
| |
| using namespace psp; |
| using namespace osl; |
| using namespace rtl; |
| |
| /* |
| * CUPSWrapper class |
| */ |
| |
| oslGenericFunction CUPSWrapper::loadSymbol( const char* pSymbol ) |
| { |
| OUString aSym( OUString::createFromAscii( pSymbol ) ); |
| oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); |
| #endif |
| return pSym; |
| } |
| |
| CUPSWrapper::CUPSWrapper() |
| : m_pLib( NULL ), |
| m_bPPDThreadRunning( false ) |
| { |
| #ifdef ENABLE_CUPS |
| m_pLib = osl_loadAsciiModule( CUPS_LIB_NAME, SAL_LOADMODULE_LAZY ); |
| if( ! m_pLib ) |
| m_pLib = osl_loadAsciiModule( "cups", SAL_LOADMODULE_LAZY ); |
| #endif |
| |
| if( ! m_pLib ) |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "no cups library found\n" ); |
| #endif |
| return; |
| } |
| |
| m_pcupsPrintFile = (int(*)(const char*,const char*,const char*,int,cups_option_t*)) |
| loadSymbol( "cupsPrintFile" ); |
| m_pcupsGetDests = (int(*)(cups_dest_t**)) |
| loadSymbol( "cupsGetDests" ); |
| m_pcupsSetDests = (void(*)(int,cups_dest_t*)) |
| loadSymbol( "cupsSetDests" ); |
| m_pcupsFreeDests = (void(*)(int,cups_dest_t*)) |
| loadSymbol( "cupsFreeDests" ); |
| m_pcupsGetPPD = (const char*(*)(const char*)) |
| loadSymbol( "cupsGetPPD" ); |
| m_pcupsMarkOptions = (int(*)(ppd_file_t*,int,cups_option_t*)) |
| loadSymbol( "cupsMarkOptions" ); |
| m_pcupsAddOption = (int(*)(const char*,const char*,int,cups_option_t**)) |
| loadSymbol( "cupsAddOption" ); |
| m_pcupsFreeOptions = (void(*)(int,cups_option_t*)) |
| loadSymbol( "cupsFreeOptions" ); |
| m_pppdOpenFile = (ppd_file_t*(*)(const char*)) |
| loadSymbol( "ppdOpenFile" ); |
| m_pppdClose = (void(*)(ppd_file_t*)) |
| loadSymbol( "ppdClose" ); |
| m_pcupsServer = (const char*(*)()) |
| loadSymbol( "cupsServer" ); |
| m_pcupsUser = (const char*(*)()) |
| loadSymbol( "cupsUser" ); |
| m_pcupsSetPasswordCB = (void(*)(const char*(*)(const char*))) |
| loadSymbol( "cupsSetPasswordCB" ); |
| m_pcupsSetUser = (void(*)(const char*)) |
| loadSymbol( "cupsSetUser" ); |
| m_pcupsGetOption = (const char*(*)(const char*,int,cups_option_t*)) |
| loadSymbol( "cupsGetOption" ); |
| |
| if( ! ( |
| m_pcupsPrintFile && |
| m_pcupsGetDests && |
| m_pcupsSetDests && |
| m_pcupsFreeDests && |
| m_pcupsGetPPD && |
| m_pcupsMarkOptions && |
| m_pcupsAddOption && |
| m_pcupsServer && |
| m_pcupsUser && |
| m_pcupsSetPasswordCB && |
| m_pcupsSetUser && |
| m_pcupsFreeOptions && |
| m_pppdOpenFile && |
| m_pppdClose && |
| m_pcupsGetOption |
| ) ) |
| { |
| osl_unloadModule( m_pLib ); |
| m_pLib = NULL; |
| } |
| } |
| |
| CUPSWrapper::~CUPSWrapper() |
| { |
| if( m_pLib ) |
| osl_unloadModule( m_pLib ); |
| } |
| |
| bool CUPSWrapper::isValid() |
| { |
| return m_pLib != NULL; |
| } |
| |
| typedef const char*(*PPDFunction)(const char*); |
| struct GetPPDAttribs |
| { |
| PPDFunction m_pFunction; |
| osl::Condition m_aCondition; |
| OString m_aParameter; |
| OString m_aResult; |
| int m_nRefs; |
| bool* m_pResetRunning; |
| osl::Mutex* m_pSyncMutex; |
| |
| GetPPDAttribs( PPDFunction pFn, const char * m_pParameter, |
| bool* pResetRunning, osl::Mutex* pSyncMutex ) |
| : m_pFunction( pFn ), |
| m_aParameter( m_pParameter ), |
| m_pResetRunning( pResetRunning ), |
| m_pSyncMutex( pSyncMutex ) |
| { |
| m_nRefs = 2; |
| m_aCondition.reset(); |
| } |
| |
| ~GetPPDAttribs() |
| { |
| if( m_aResult.getLength() ) |
| unlink( m_aResult.getStr() ); |
| } |
| |
| void unref() |
| { |
| if( --m_nRefs == 0 ) |
| { |
| *m_pResetRunning = false; |
| delete this; |
| } |
| } |
| |
| void executeCall() |
| { |
| // This CUPS method is not at all thread-safe we need |
| // to dup the pointer to a static buffer it returns ASAP |
| OString aResult = m_pFunction( m_aParameter.getStr() ); |
| MutexGuard aGuard( *m_pSyncMutex ); |
| m_aResult = aResult; |
| m_aCondition.set(); |
| unref(); |
| } |
| |
| OString waitResult( TimeValue *pDelay ) |
| { |
| m_pSyncMutex->release(); |
| |
| if (m_aCondition.wait( pDelay ) != Condition::result_ok |
| ) |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "cupsGetPPD %s timed out\n", m_aParameter.getStr() ); |
| #endif |
| } |
| m_pSyncMutex->acquire(); |
| |
| OString aRetval = m_aResult; |
| m_aResult = OString(); |
| unref(); |
| |
| return aRetval; |
| } |
| }; |
| |
| extern "C" { |
| static void getPPDWorker(void* pData) |
| { |
| GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData; |
| pAttribs->executeCall(); |
| } |
| } |
| |
| OString CUPSWrapper::cupsGetPPD( const char* pPrinter ) |
| { |
| OString aResult; |
| |
| m_aGetPPDMutex.acquire(); |
| // if one thread hangs in cupsGetPPD already, don't start another |
| if( ! m_bPPDThreadRunning ) |
| { |
| m_bPPDThreadRunning = true; |
| GetPPDAttribs* pAttribs = new GetPPDAttribs( m_pcupsGetPPD, |
| pPrinter, |
| &m_bPPDThreadRunning, |
| &m_aGetPPDMutex ); |
| |
| oslThread aThread = osl_createThread( getPPDWorker, pAttribs ); |
| |
| TimeValue aValue; |
| aValue.Seconds = 5; |
| aValue.Nanosec = 0; |
| |
| // NOTE: waitResult release and acquires the GetPPD mutex |
| aResult = pAttribs->waitResult( &aValue ); |
| osl_destroyThread( aThread ); |
| } |
| m_aGetPPDMutex.release(); |
| |
| return aResult; |
| } |
| |
| #ifdef ENABLE_CUPS |
| static const char* setPasswordCallback( const char* pIn ) |
| { |
| const char* pRet = NULL; |
| |
| PrinterInfoManager& rMgr = PrinterInfoManager::get(); |
| if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check |
| pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn ); |
| return pRet; |
| } |
| #endif |
| |
| /* |
| * CUPSManager class |
| */ |
| |
| CUPSManager* CUPSManager::tryLoadCUPS() |
| { |
| CUPSManager* pManager = NULL; |
| #ifdef ENABLE_CUPS |
| static const char* pEnv = getenv( "SAL_DISABLE_CUPS" ); |
| |
| if( ! pEnv || ! *pEnv ) |
| { |
| // try to load CUPS |
| CUPSWrapper* pWrapper = new CUPSWrapper(); |
| if( pWrapper->isValid() ) |
| pManager = new CUPSManager( pWrapper ); |
| else |
| delete pWrapper; |
| } |
| #endif |
| return pManager; |
| } |
| |
| extern "C" |
| { |
| static void run_dest_thread_stub( void* pThis ) |
| { |
| CUPSManager::runDestThread( pThis ); |
| } |
| } |
| |
| CUPSManager::CUPSManager( CUPSWrapper* pWrapper ) : |
| PrinterInfoManager( CUPS ), |
| m_pCUPSWrapper( pWrapper ), |
| m_nDests( 0 ), |
| m_pDests( NULL ), |
| m_bNewDests( false ) |
| { |
| m_aDestThread = osl_createThread( run_dest_thread_stub, this ); |
| } |
| |
| CUPSManager::~CUPSManager() |
| { |
| if( m_aDestThread ) |
| { |
| // if the thread is still running here, then |
| // cupsGetDests is hung; terminate the thread instead of joining |
| osl_terminateThread( m_aDestThread ); |
| osl_destroyThread( m_aDestThread ); |
| } |
| |
| if( m_nDests && m_pDests ) |
| m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); |
| delete m_pCUPSWrapper; |
| } |
| |
| void CUPSManager::runDestThread( void* pThis ) |
| { |
| ((CUPSManager*)pThis)->runDests(); |
| } |
| |
| static sigjmp_buf aViolationBuffer; |
| |
| extern "C" |
| { |
| static void lcl_signal_action(int nSignal) |
| { |
| fprintf( stderr, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal ); |
| siglongjmp( aViolationBuffer, 1 ); |
| } |
| } |
| |
| void CUPSManager::runDests() |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "starting cupsGetDests\n" ); |
| #endif |
| int nDests = 0; |
| cups_dest_t* pDests = NULL; |
| |
| // #i86306# prepare against really broken CUPS installations / missing servers |
| |
| // install signal handler for SEGV, BUS and ABRT |
| struct sigaction act; |
| struct sigaction oact[3]; |
| |
| act.sa_handler = lcl_signal_action; |
| act.sa_flags = 0; |
| sigemptyset(&(act.sa_mask)); |
| |
| int nSegvSignalInstalled = sigaction(SIGSEGV, &act, &oact[0]); |
| int nBusSignalInstalled = sigaction(SIGBUS, &act, &oact[1]); |
| int nAbortSignalInstalled = sigaction(SIGABRT, &act, &oact[2]); |
| |
| // prepare against a signal during FcInit or FcConfigGetCurrent |
| if( sigsetjmp( aViolationBuffer, ~0 ) == 0 ) |
| { |
| nDests = m_pCUPSWrapper->cupsGetDests( &pDests ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "came out of cupsGetDests\n" ); |
| #endif |
| |
| osl::MutexGuard aGuard( m_aCUPSMutex ); |
| m_nDests = nDests; |
| m_pDests = pDests; |
| m_bNewDests = true; |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "finished cupsGetDests\n" ); |
| #endif |
| } |
| else |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" ); |
| #endif |
| } |
| |
| // restore old signal handlers |
| if( nSegvSignalInstalled == 0 ) |
| sigaction( SIGSEGV, &oact[0], NULL ); |
| if( nBusSignalInstalled == 0 ) |
| sigaction( SIGBUS, &oact[1], NULL ); |
| if( nAbortSignalInstalled == 0 ) |
| sigaction( SIGABRT, &oact[2], NULL ); |
| } |
| |
| void CUPSManager::initialize() |
| { |
| // get normal printers, clear printer list |
| PrinterInfoManager::initialize(); |
| |
| #ifdef ENABLE_CUPS |
| // check whether thread has completed |
| // if not behave like old printing system |
| osl::MutexGuard aGuard( m_aCUPSMutex ); |
| |
| if( ! m_bNewDests ) |
| return; |
| |
| // dest thread has run, clean up |
| if( m_aDestThread ) |
| { |
| osl_joinWithThread( m_aDestThread ); |
| osl_destroyThread( m_aDestThread ); |
| m_aDestThread = NULL; |
| } |
| m_bNewDests = false; |
| |
| // clear old stuff |
| m_aCUPSDestMap.clear(); |
| |
| if( ! (m_nDests && m_pDests ) ) |
| return; |
| |
| if( isCUPSDisabled() ) |
| return; |
| |
| // check for CUPS server(?) > 1.2 |
| // since there is no API to query, check for options that were |
| // introduced in dests with 1.2 |
| // this is needed to check for %%IncludeFeature support |
| // (#i65684#, #i65491#) |
| bool bUsePDF = false; |
| cups_dest_t* pDest = ((cups_dest_t*)m_pDests); |
| const char* pOpt = m_pCUPSWrapper->cupsGetOption( "printer-info", |
| pDest->num_options, |
| pDest->options ); |
| if( pOpt ) |
| { |
| m_bUseIncludeFeature = true; |
| bUsePDF = true; |
| if( m_aGlobalDefaults.m_nPSLevel == 0 && m_aGlobalDefaults.m_nPDFDevice == 0 ) |
| m_aGlobalDefaults.m_nPDFDevice = 1; |
| } |
| // do not send include JobPatch; CUPS will insert that itself |
| // TODO: currently unknwon which versions of CUPS insert JobPatches |
| // so currently it is assumed CUPS = don't insert JobPatch files |
| m_bUseJobPatch = false; |
| |
| rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); |
| int nPrinter = m_nDests; |
| |
| // reset global default PPD options; these are queried on demand from CUPS |
| m_aGlobalDefaults.m_pParser = NULL; |
| m_aGlobalDefaults.m_aContext = PPDContext(); |
| |
| // add CUPS printers, should there be a printer |
| // with the same name as a CUPS printer, overwrite it |
| while( nPrinter-- ) |
| { |
| pDest = ((cups_dest_t*)m_pDests)+nPrinter; |
| OUString aPrinterName = OStringToOUString( pDest->name, aEncoding ); |
| if( pDest->instance && *pDest->instance ) |
| { |
| OUStringBuffer aBuf( 256 ); |
| aBuf.append( aPrinterName ); |
| aBuf.append( sal_Unicode( '/' ) ); |
| aBuf.append( OStringToOUString( pDest->instance, aEncoding ) ); |
| aPrinterName = aBuf.makeStringAndClear(); |
| } |
| |
| // initialize printer with possible configuration from psprint.conf |
| bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end(); |
| Printer aPrinter = m_aPrinters[ aPrinterName ]; |
| if( bSetToGlobalDefaults ) |
| aPrinter.m_aInfo = m_aGlobalDefaults; |
| aPrinter.m_aInfo.m_aPrinterName = aPrinterName; |
| if( pDest->is_default ) |
| m_aDefaultPrinter = aPrinterName; |
| |
| for( int k = 0; k < pDest->num_options; k++ ) |
| { |
| if(!strcmp(pDest->options[k].name, "printer-info")) |
| aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding); |
| if(!strcmp(pDest->options[k].name, "printer-location")) |
| aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding); |
| } |
| |
| |
| OUStringBuffer aBuf( 256 ); |
| aBuf.appendAscii( "CUPS:" ); |
| aBuf.append( aPrinterName ); |
| // note: the parser that goes with the PrinterInfo |
| // is created implicitly by the JobData::operator=() |
| // when it detects the NULL ptr m_pParser. |
| // if we wanted to fill in the parser here this |
| // would mean we'd have to download PPDs for each and |
| // every printer - which would be really bad runtime |
| // behaviour |
| aPrinter.m_aInfo.m_pParser = NULL; |
| aPrinter.m_aInfo.m_aContext.setParser( NULL ); |
| std::hash_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName ); |
| if( c_it != m_aDefaultContexts.end() ) |
| { |
| aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); |
| aPrinter.m_aInfo.m_aContext = c_it->second; |
| } |
| if( bUsePDF && aPrinter.m_aInfo.m_nPSLevel == 0 && aPrinter.m_aInfo.m_nPDFDevice == 0 ) |
| aPrinter.m_aInfo.m_nPDFDevice = 1; |
| aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear(); |
| aPrinter.m_bModified = false; |
| |
| m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter; |
| m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter; |
| } |
| |
| // remove everything that is not a CUPS printer and not |
| // a special purpose printer (PDF, Fax) |
| std::list< OUString > aRemovePrinters; |
| for( std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin(); |
| it != m_aPrinters.end(); ++it ) |
| { |
| if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() ) |
| continue; |
| |
| if( it->second.m_aInfo.m_aFeatures.getLength() > 0 ) |
| continue; |
| aRemovePrinters.push_back( it->first ); |
| } |
| while( aRemovePrinters.begin() != aRemovePrinters.end() ) |
| { |
| m_aPrinters.erase( aRemovePrinters.front() ); |
| aRemovePrinters.pop_front(); |
| } |
| |
| m_pCUPSWrapper->cupsSetPasswordCB( setPasswordCallback ); |
| #endif // ENABLE_CUPS |
| } |
| |
| #ifdef ENABLE_CUPS |
| static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext ) |
| { |
| rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); |
| for( int i = 0; i < pPPDGroup->num_options; i++ ) |
| { |
| ppd_option_t* pOption = pPPDGroup->options + i; |
| for( int n = 0; n < pOption->num_choices; n++ ) |
| { |
| ppd_choice_t* pChoice = pOption->choices + n; |
| if( pChoice->marked ) |
| { |
| const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) ); |
| if( pKey ) |
| { |
| const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) ); |
| if( pValue ) |
| { |
| if( pValue != pKey->getDefaultValue() ) |
| { |
| rContext.setValue( pKey, pValue, true ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice ); |
| #endif |
| |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice ); |
| #endif |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword ); |
| #endif |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword ); |
| #endif |
| } |
| } |
| } |
| |
| // recurse through subgroups |
| for( int g = 0; g < pPPDGroup->num_subgroups; g++ ) |
| { |
| updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext ); |
| } |
| } |
| #endif // ENABLE_CUPS |
| |
| const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter ) |
| { |
| const PPDParser* pNewParser = NULL; |
| OUString aPrinter; |
| |
| if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 ) |
| aPrinter = rPrinter.copy( 5 ); |
| else |
| aPrinter = rPrinter; |
| |
| #ifdef ENABLE_CUPS |
| if( m_aCUPSMutex.tryToAcquire() ) |
| { |
| if( m_nDests && m_pDests && ! isCUPSDisabled() ) |
| { |
| std::hash_map< OUString, int, OUStringHash >::iterator dest_it = |
| m_aCUPSDestMap.find( aPrinter ); |
| if( dest_it != m_aCUPSDestMap.end() ) |
| { |
| cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; |
| OString aPPDFile = m_pCUPSWrapper->cupsGetPPD( pDest->name ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() ); |
| #endif |
| if( aPPDFile.getLength() ) |
| { |
| rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); |
| OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) ); |
| // update the printer info with context information |
| ppd_file_t* pPPD = m_pCUPSWrapper->ppdOpenFile( aPPDFile.getStr() ); |
| if( pPPD ) |
| { |
| // create the new parser |
| PPDParser* pCUPSParser = new PPDParser( aFileName ); |
| pCUPSParser->m_aFile = rPrinter; |
| pNewParser = pCUPSParser; |
| |
| /*int nConflicts =*/ m_pCUPSWrapper->cupsMarkOptions( pPPD, pDest->num_options, pDest->options ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "processing the following options for printer %s (instance %s):\n", |
| pDest->name, pDest->instance ); |
| for( int k = 0; k < pDest->num_options; k++ ) |
| fprintf( stderr, " \"%s\" = \"%s\"\n", |
| pDest->options[k].name, |
| pDest->options[k].value ); |
| #endif |
| PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; |
| |
| // remember the default context for later use |
| PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; |
| rContext.setParser( pNewParser ); |
| // set system default paper; printer CUPS PPD options |
| // may overwrite it |
| setDefaultPaper( rContext ); |
| for( int i = 0; i < pPPD->num_groups; i++ ) |
| updatePrinterContextInfo( pPPD->groups + i, rContext ); |
| |
| rInfo.m_pParser = pNewParser; |
| rInfo.m_aContext = rContext; |
| |
| // clean up the mess |
| m_pCUPSWrapper->ppdClose( pPPD ); |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" ); |
| #endif |
| |
| // remove temporary PPD file |
| unlink( aPPDFile.getStr() ); |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" ); |
| #endif |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else |
| fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() ); |
| #endif |
| } |
| m_aCUPSMutex.release(); |
| } |
| #if OSL_DEBUG_LEVEL >1 |
| else |
| fprintf( stderr, "could not acquire CUPS mutex !!!\n" ); |
| #endif |
| #endif // ENABLE_CUPS |
| |
| if( ! pNewParser ) |
| { |
| // get the default PPD |
| pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); |
| |
| PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; |
| |
| rInfo.m_pParser = pNewParser; |
| rInfo.m_aContext.setParser( pNewParser ); |
| } |
| |
| return pNewParser; |
| } |
| |
| void CUPSManager::setupJobContextData( |
| JobData& |
| #ifdef ENABLE_CUPS |
| rData |
| #endif |
| ) |
| { |
| #ifdef ENABLE_CUPS |
| std::hash_map< OUString, int, OUStringHash >::iterator dest_it = |
| m_aCUPSDestMap.find( rData.m_aPrinterName ); |
| |
| if( dest_it == m_aCUPSDestMap.end() ) |
| return PrinterInfoManager::setupJobContextData( rData ); |
| |
| std::hash_map< OUString, Printer, OUStringHash >::iterator p_it = |
| m_aPrinters.find( rData.m_aPrinterName ); |
| if( p_it == m_aPrinters.end() ) // huh ? |
| { |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() ); |
| #endif |
| return; |
| } |
| |
| if( p_it->second.m_aInfo.m_pParser == NULL ) |
| { |
| // in turn calls createCUPSParser |
| // which updates the printer info |
| p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); |
| } |
| if( p_it->second.m_aInfo.m_aContext.getParser() == NULL ) |
| { |
| OUString aPrinter; |
| if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 ) |
| aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 ); |
| else |
| aPrinter = p_it->second.m_aInfo.m_aDriverName; |
| |
| p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; |
| } |
| |
| rData.m_pParser = p_it->second.m_aInfo.m_pParser; |
| rData.m_aContext = p_it->second.m_aInfo.m_aContext; |
| #endif |
| } |
| |
| FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) |
| { |
| OSL_TRACE( "endSpool: %s, %s", |
| rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), |
| bQuickCommand ? "true" : "false" ); |
| |
| if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() ) |
| { |
| OSL_TRACE( "defer to PrinterInfoManager::startSpool" ); |
| return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); |
| } |
| |
| #ifdef ENABLE_CUPS |
| OUString aTmpURL, aTmpFile; |
| osl_createTempFile( NULL, NULL, &aTmpURL.pData ); |
| osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); |
| OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); |
| FILE* fp = fopen( aSysFile.getStr(), "w" ); |
| if( fp ) |
| m_aSpoolFiles[fp] = aSysFile; |
| |
| return fp; |
| #else |
| return NULL; |
| #endif |
| } |
| |
| struct less_ppd_key : public ::std::binary_function<double, double, bool> |
| { |
| bool operator()(const PPDKey* left, const PPDKey* right) |
| { return left->getOrderDependency() < right->getOrderDependency(); } |
| }; |
| |
| void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const |
| { |
| rNumOptions = 0; |
| *rOptions = NULL; |
| int i; |
| |
| // emit features ordered to OrderDependency |
| // ignore features that are set to default |
| |
| // sanity check |
| if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) |
| { |
| int nKeys = rJob.m_aContext.countValuesModified(); |
| ::std::vector< const PPDKey* > aKeys( nKeys ); |
| for( i = 0; i < nKeys; i++ ) |
| aKeys[i] = rJob.m_aContext.getModifiedKey( i ); |
| ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); |
| |
| for( i = 0; i < nKeys; i++ ) |
| { |
| const PPDKey* pKey = aKeys[i]; |
| const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); |
| if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() ) |
| { |
| OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); |
| OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ); |
| rNumOptions = m_pCUPSWrapper->cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions ); |
| } |
| } |
| } |
| |
| if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 ) |
| { |
| rtl::OString aVal( rtl::OString::valueOf( sal_Int32( rJob.m_nCopies ) ) ); |
| rNumOptions = m_pCUPSWrapper->cupsAddOption( "copies", aVal.getStr(), rNumOptions, (cups_option_t**)rOptions ); |
| } |
| if( ! bBanner ) |
| { |
| rNumOptions = m_pCUPSWrapper->cupsAddOption( "job-sheets", "none", rNumOptions, (cups_option_t**)rOptions ); |
| } |
| } |
| |
| int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner ) |
| { |
| OSL_TRACE( "endSpool: %s, %s, copy count = %d", |
| rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), |
| rtl::OUStringToOString( rJobTitle, RTL_TEXTENCODING_UTF8 ).getStr(), |
| rDocumentJobData.m_nCopies |
| ); |
| |
| int nJobID = 0; |
| |
| osl::MutexGuard aGuard( m_aCUPSMutex ); |
| |
| std::hash_map< OUString, int, OUStringHash >::iterator dest_it = |
| m_aCUPSDestMap.find( rPrintername ); |
| if( dest_it == m_aCUPSDestMap.end() ) |
| { |
| OSL_TRACE( "defer to PrinterInfoManager::endSpool" ); |
| return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner ); |
| } |
| |
| #ifdef ENABLE_CUPS |
| std::hash_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); |
| if( it != m_aSpoolFiles.end() ) |
| { |
| fclose( pFile ); |
| rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); |
| |
| // setup cups options |
| int nNumOptions = 0; |
| cups_option_t* pOptions = NULL; |
| getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions ); |
| |
| cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; |
| nJobID = m_pCUPSWrapper->cupsPrintFile( pDest->name, |
| it->second.getStr(), |
| OUStringToOString( rJobTitle, aEnc ).getStr(), |
| nNumOptions, pOptions ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n", |
| pDest->name, |
| it->second.getStr(), |
| OUStringToOString( rJobTitle, aEnc ).getStr(), |
| nNumOptions, |
| pOptions, |
| nJobID |
| ); |
| for( int n = 0; n < nNumOptions; n++ ) |
| fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value ); |
| OString aCmd( "cp " ); |
| aCmd = aCmd + it->second; |
| aCmd = aCmd + OString( " $HOME/cupsprint.ps" ); |
| system( aCmd.getStr() ); |
| #endif |
| |
| unlink( it->second.getStr() ); |
| m_aSpoolFiles.erase( pFile ); |
| if( pOptions ) |
| m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions ); |
| } |
| #endif // ENABLE_CUPS |
| |
| return nJobID; |
| } |
| |
| |
| void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) |
| { |
| PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo ); |
| } |
| |
| bool CUPSManager::checkPrintersChanged( bool bWait ) |
| { |
| bool bChanged = false; |
| if( bWait ) |
| { |
| if( m_aDestThread ) |
| { |
| // initial asynchronous detection still running |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "syncing cups discovery thread\n" ); |
| #endif |
| osl_joinWithThread( m_aDestThread ); |
| osl_destroyThread( m_aDestThread ); |
| m_aDestThread = NULL; |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "done: syncing cups discovery thread\n" ); |
| #endif |
| } |
| else |
| { |
| // #i82321# check for cups printer updates |
| // with this change the whole asynchronous detection in a thread is |
| // almost useless. The only relevance left is for some stalled systems |
| // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION |
| // (see vcl/unx/source/gdi/salprnpsp.cxx) |
| // so that checkPrintersChanged( true ) will never be called |
| |
| // there is no way to query CUPS whether the printer list has changed |
| // so get the dest list anew |
| if( m_nDests && m_pDests ) |
| m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); |
| m_nDests = 0; |
| m_pDests = NULL; |
| runDests(); |
| } |
| } |
| if( m_aCUPSMutex.tryToAcquire() ) |
| { |
| bChanged = m_bNewDests; |
| m_aCUPSMutex.release(); |
| } |
| |
| if( ! bChanged ) |
| { |
| bChanged = PrinterInfoManager::checkPrintersChanged( bWait ); |
| // #i54375# ensure new merging with CUPS list in :initialize |
| if( bChanged ) |
| m_bNewDests = true; |
| } |
| |
| if( bChanged ) |
| initialize(); |
| |
| return bChanged; |
| } |
| |
| bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver ) |
| { |
| // don't touch the CUPS printers |
| if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() || |
| rDriver.compareToAscii( "CUPS:", 5 ) == 0 |
| ) |
| return false; |
| return PrinterInfoManager::addPrinter( rName, rDriver ); |
| } |
| |
| bool CUPSManager::removePrinter( const OUString& rName, bool bCheck ) |
| { |
| // don't touch the CUPS printers |
| if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ) |
| return false; |
| return PrinterInfoManager::removePrinter( rName, bCheck ); |
| } |
| |
| bool CUPSManager::setDefaultPrinter( const OUString& rName ) |
| { |
| bool bSuccess = false; |
| #ifdef ENABLE_CUPS |
| std::hash_map< OUString, int, OUStringHash >::iterator nit = |
| m_aCUPSDestMap.find( rName ); |
| if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() ) |
| { |
| cups_dest_t* pDests = (cups_dest_t*)m_pDests; |
| for( int i = 0; i < m_nDests; i++ ) |
| pDests[i].is_default = 0; |
| pDests[ nit->second ].is_default = 1; |
| m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); |
| m_aDefaultPrinter = rName; |
| m_aCUPSMutex.release(); |
| bSuccess = true; |
| } |
| else |
| #endif |
| bSuccess = PrinterInfoManager::setDefaultPrinter( rName ); |
| |
| return bSuccess; |
| } |
| |
| bool CUPSManager::writePrinterConfig() |
| { |
| #ifdef ENABLE_CUPS |
| bool bDestModified = false; |
| rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); |
| |
| for( std::hash_map< OUString, Printer, OUStringHash >::iterator prt = |
| m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt ) |
| { |
| std::hash_map< OUString, int, OUStringHash >::iterator nit = |
| m_aCUPSDestMap.find( prt->first ); |
| if( nit == m_aCUPSDestMap.end() ) |
| continue; |
| |
| if( ! prt->second.m_bModified ) |
| continue; |
| |
| if( m_aCUPSMutex.tryToAcquire() ) |
| { |
| bDestModified = true; |
| cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second; |
| PrinterInfo& rInfo = prt->second.m_aInfo; |
| |
| // create new option list |
| int nNewOptions = 0; |
| cups_option_t* pNewOptions = NULL; |
| int nValues = rInfo.m_aContext.countValuesModified(); |
| for( int i = 0; i < nValues; i++ ) |
| { |
| const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i ); |
| const PPDValue* pValue = rInfo.m_aContext.getValue( pKey ); |
| if( pKey && pValue ) // sanity check |
| { |
| OString aName = OUStringToOString( pKey->getKey(), aEncoding ); |
| OString aValue = OUStringToOString( pValue->m_aOption, aEncoding ); |
| nNewOptions = m_pCUPSWrapper->cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions ); |
| } |
| } |
| // set PPD options on CUPS dest |
| m_pCUPSWrapper->cupsFreeOptions( pDest->num_options, pDest->options ); |
| pDest->num_options = nNewOptions; |
| pDest->options = pNewOptions; |
| m_aCUPSMutex.release(); |
| } |
| } |
| if( bDestModified && m_aCUPSMutex.tryToAcquire() ) |
| { |
| m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); |
| m_aCUPSMutex.release(); |
| } |
| #endif // ENABLE_CUPS |
| |
| return PrinterInfoManager::writePrinterConfig(); |
| } |
| |
| bool CUPSManager::addOrRemovePossible() const |
| { |
| return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible(); |
| } |
| |
| const char* CUPSManager::authenticateUser( const char* /*pIn*/ ) |
| { |
| const char* pRet = NULL; |
| |
| #ifdef ENABLE_CUPS |
| oslModule pLib = osl_loadAsciiModule( _XSALSET_LIBNAME, SAL_LOADMODULE_LAZY ); |
| if( pLib ) |
| { |
| bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) = |
| (bool(*)(const OString&,OString&,OString&))osl_getAsciiFunctionSymbol( pLib, "Sal_authenticateQuery" ); |
| if( getpw ) |
| { |
| osl::MutexGuard aGuard( m_aCUPSMutex ); |
| |
| OString aUser = m_pCUPSWrapper->cupsUser(); |
| OString aServer = m_pCUPSWrapper->cupsServer(); |
| OString aPassword; |
| if( getpw( aServer, aUser, aPassword ) ) |
| { |
| m_aPassword = aPassword; |
| m_aUser = aUser; |
| m_pCUPSWrapper->cupsSetUser( m_aUser.getStr() ); |
| pRet = m_aPassword.getStr(); |
| } |
| } |
| osl_unloadModule( pLib ); |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| else fprintf( stderr, "loading of module %s failed\n", _XSALSET_LIBNAME ); |
| #endif |
| #endif // ENABLE_CUPS |
| |
| return pRet; |
| } |