/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <cups/http.h>
#include <cups/ipp.h>
#include <cups/ppd.h>
#include <unistd.h>
#include <unx/cupsmgr.hxx>
#include <o3tl/string_view.hxx>
#include <osl/thread.h>
#include <osl/file.h>
#include <osl/conditn.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <officecfg/Office/Common.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/window.hxx>
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <string_view>
using namespace psp;
using namespace osl;
namespace {
struct GetPPDAttribs
{
osl::Condition m_aCondition;
OString m_aParameter;
OString m_aResult;
int m_nRefs;
bool* m_pResetRunning;
osl::Mutex* m_pSyncMutex;
GetPPDAttribs( const char * m_pParameter,
bool* pResetRunning, osl::Mutex* pSyncMutex )
: m_aParameter( m_pParameter ),
m_pResetRunning( pResetRunning ),
m_pSyncMutex( pSyncMutex )
{
m_nRefs = 2;
m_aCondition.reset();
}
~GetPPDAttribs()
{
if( !m_aResult.isEmpty() )
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
SAL_WNODEPRECATED_DECLARATIONS_PUSH
const char* pResult = cupsGetPPD(m_aParameter.getStr());
OString aResult = pResult ? OString(pResult) : OString();
SAL_WNODEPRECATED_DECLARATIONS_POP
MutexGuard aGuard( *m_pSyncMutex );
m_aResult = std::move(aResult);
m_aCondition.set();
unref();
}
OString waitResult( TimeValue const *pDelay )
{
m_pSyncMutex->release();
if (m_aCondition.wait( pDelay ) != Condition::result_ok
)
{
SAL_WARN("vcl.unx.print",
"cupsGetPPD " << m_aParameter << " timed out");
}
m_pSyncMutex->acquire();
OString aRetval = m_aResult;
m_aResult.clear();
unref();
return aRetval;
}
};
}
extern "C" {
static void getPPDWorker(void* pData)
{
osl_setThreadName("CUPSManager getPPDWorker");
GetPPDAttribs* pAttribs = static_cast<GetPPDAttribs*>(pData);
pAttribs->executeCall();
}
}
OString CUPSManager::threadedCupsGetPPD( 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( 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;
}
static const char* setPasswordCallback( const char* /*pIn*/ )
{
const char* pRet = nullptr;
PrinterInfoManager& rMgr = PrinterInfoManager::get();
if( rMgr.getType() == PrinterInfoManager::Type::CUPS ) // sanity check
pRet = static_cast<CUPSManager&>(rMgr).authenticateUser();
return pRet;
}
/*
* CUPSManager class
*/
CUPSManager* CUPSManager::tryLoadCUPS()
{
CUPSManager* pManager = nullptr;
static const char* pEnv = getenv("SAL_DISABLE_CUPS");
if (!pEnv || !*pEnv)
pManager = new CUPSManager();
return pManager;
}
extern "C"
{
static void run_dest_thread_stub( void* pThis )
{
osl_setThreadName("CUPSManager cupsGetDests");
CUPSManager::runDestThread( pThis );
}
}
CUPSManager::CUPSManager() :
PrinterInfoManager( PrinterInfoManager::Type::CUPS ),
m_nDests( 0 ),
m_pDests( nullptr ),
m_bNewDests( false ),
m_bPPDThreadRunning( false )
{
m_aDestThread = osl_createThread( run_dest_thread_stub, this );
}
CUPSManager::~CUPSManager()
{
if( m_aDestThread )
{
osl_joinWithThread( m_aDestThread );
osl_destroyThread( m_aDestThread );
}
if (m_nDests && m_pDests)
cupsFreeDests( m_nDests, m_pDests );
}
void CUPSManager::runDestThread( void* pThis )
{
static_cast<CUPSManager*>(pThis)->runDests();
}
void CUPSManager::runDests()
{
SAL_INFO("vcl.unx.print", "starting cupsGetDests");
cups_dest_t* pDests = nullptr;
// n#722902 - do a fast-failing check for cups working *at all* first
http_t* p_http;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
if( (p_http=httpConnectEncrypt(
cupsServer(),
ippPort(),
cupsEncryption())) == nullptr )
return;
int nDests = cupsGetDests2(p_http, &pDests);
SAL_INFO("vcl.unx.print", "came out of cupsGetDests");
osl::MutexGuard aGuard( m_aCUPSMutex );
m_nDests = nDests;
m_pDests = pDests;
m_bNewDests = true;
SAL_INFO("vcl.unx.print", "finished cupsGetDests");
httpClose(p_http);
SAL_WNODEPRECATED_DECLARATIONS_POP
}
static void SetIfCustomOption(PPDContext& rContext, const cups_option_t& rOption, rtl_TextEncoding aEncoding)
{
if (strncmp(rOption.value, RTL_CONSTASCII_STRINGPARAM("Custom.")) == 0)
{
const PPDParser* pParser = rContext.getParser();
if (!pParser)
{
// normal for first sight of this printer
return;
}
const PPDKey* pKey = pParser->getKey(OStringToOUString(rOption.name, aEncoding));
if (!pKey)
{
SAL_WARN("vcl.unx.print", "Custom key " << rOption.name << " not found");
return;
}
const PPDValue* pCustomValue = rContext.getValue(pKey);
if (!pCustomValue)
{
SAL_WARN("vcl.unx.print", "Value for " << rOption.name << " not found");
return;
}
if (!pCustomValue->m_bCustomOption)
{
SAL_WARN("vcl.unx.print", "Value for " << rOption.name << " not set to custom option");
return;
}
// seems sensible to keep a value the user explicitly set even if lpoptions was used to set
// another default
if (pCustomValue->m_bCustomOptionSetViaApp)
return;
pCustomValue->m_aCustomOption = OStringToOUString(rOption.value, aEncoding);
}
}
void CUPSManager::initialize()
{
// get normal printers, clear printer list
PrinterInfoManager::initialize();
// 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 = nullptr;
}
m_bNewDests = false;
// clear old stuff
m_aCUPSDestMap.clear();
if( ! (m_nDests && m_pDests ) )
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#)
cups_dest_t* pDest = m_pDests;
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 = nullptr;
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 = m_pDests+nPrinter;
OUString aPrinterName = OStringToOUString( pDest->name, aEncoding );
if( pDest->instance && *pDest->instance )
{
aPrinterName += "/" +
OStringToOUString( pDest->instance, aEncoding );
}
// 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;
// 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 = nullptr;
aPrinter.m_aInfo.m_aContext.setParser( nullptr );
std::unordered_map< OUString, PPDContext >::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;
}
aPrinter.m_aInfo.m_aDriverName = "CUPS:" + 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);
if(!strcmp(pDest->options[k].name, "auth-info-required"))
aPrinter.m_aInfo.m_aAuthInfoRequired=OStringToOUString(pDest->options[k].value, aEncoding);
// tdf#149439 Update Custom values that may have changed if this is not a newly discovered printer
SetIfCustomOption(aPrinter.m_aInfo.m_aContext, pDest->options[k], aEncoding);
}
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::unordered_map< OUString, Printer >::iterator it = m_aPrinters.begin();
while(it != m_aPrinters.end())
{
if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() )
{
++it;
continue;
}
if( !it->second.m_aInfo.m_aFeatures.isEmpty() )
{
++it;
continue;
}
it = m_aPrinters.erase(it);
}
cupsSetPasswordCB( setPasswordCallback );
}
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 );
SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is set to " << pChoice->choice);
}
else
SAL_INFO("vcl.unx.print", "key " << pOption->keyword << " is defaulted to " << pChoice->choice);
}
else
SAL_INFO("vcl.unx.print", "caution: value " << pChoice->choice << " not found in key " << pOption->keyword);
}
else
SAL_INFO("vcl.unx.print", "caution: key " << pOption->keyword << " not found in parser");
}
}
}
// recurse through subgroups
for( int g = 0; g < pPPDGroup->num_subgroups; g++ )
{
updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext );
}
}
const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter )
{
const PPDParser* pNewParser = nullptr;
OUString aPrinter;
if( rPrinter.startsWith("CUPS:") )
aPrinter = rPrinter.copy( 5 );
else
aPrinter = rPrinter;
if( m_aCUPSMutex.tryToAcquire() )
{
if (m_nDests && m_pDests)
{
std::unordered_map< OUString, int >::iterator dest_it =
m_aCUPSDestMap.find( aPrinter );
if( dest_it != m_aCUPSDestMap.end() )
{
cups_dest_t* pDest = m_pDests + dest_it->second;
OString aPPDFile = threadedCupsGetPPD( pDest->name );
SAL_INFO("vcl.unx.print",
"PPD for " << aPrinter << " is " << aPPDFile);
if( !aPPDFile.isEmpty() )
{
rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) );
// update the printer info with context information
SAL_WNODEPRECATED_DECLARATIONS_PUSH
ppd_file_t* pPPD = ppdOpenFile( aPPDFile.getStr() );
SAL_WNODEPRECATED_DECLARATIONS_POP
if( pPPD )
{
// create the new parser
PPDParser* pCUPSParser = new PPDParser( aFileName );
pCUPSParser->m_aFile = rPrinter;
pNewParser = pCUPSParser;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
/*int nConflicts =*/ cupsMarkOptions( pPPD, pDest->num_options, pDest->options );
SAL_WNODEPRECATED_DECLARATIONS_POP
SAL_INFO("vcl.unx.print", "processing the following options for printer " << pDest->name << " (instance " << (pDest->instance == nullptr ? "null" : pDest->instance) << "):");
for( int k = 0; k < pDest->num_options; k++ )
SAL_INFO("vcl.unx.print",
" \"" << pDest->options[k].name <<
"\" = \"" << pDest->options[k].value << "\"");
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 );
// tdf#149439 Set Custom values.
for (int k = 0; k < pDest->num_options; ++k)
SetIfCustomOption(rContext, pDest->options[k], aEncoding);
rInfo.m_pParser = pNewParser;
rInfo.m_aContext = rContext;
// clean up the mess
SAL_WNODEPRECATED_DECLARATIONS_PUSH
ppdClose( pPPD );
SAL_WNODEPRECATED_DECLARATIONS_POP
}
else
SAL_INFO("vcl.unx.print", "ppdOpenFile failed, falling back to generic driver");
// remove temporary PPD file
if (!getenv("SAL_CUPS_PPD_RETAIN_TMP"))
unlink(aPPDFile.getStr());
else
std::cout << "Saved PPD file as: " << aPPDFile << std::endl;
}
else
SAL_INFO("vcl.unx.print", "cupsGetPPD failed, falling back to generic driver");
}
else
SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter);
}
m_aCUPSMutex.release();
}
else
SAL_WARN("vcl.unx.print", "could not acquire CUPS mutex !!!" );
if( ! pNewParser )
{
// get the default PPD
pNewParser = PPDParser::getParser( u"SGENPRT"_ustr );
SAL_INFO("vcl.unx.print", "Parsing default SGENPRT PPD" );
PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo;
rInfo.m_pParser = pNewParser;
rInfo.m_aContext.setParser( pNewParser );
}
return pNewParser;
}
void CUPSManager::setupJobContextData( JobData& rData )
{
std::unordered_map< OUString, int >::iterator dest_it =
m_aCUPSDestMap.find( rData.m_aPrinterName );
if( dest_it == m_aCUPSDestMap.end() )
return PrinterInfoManager::setupJobContextData( rData );
std::unordered_map< OUString, Printer >::iterator p_it =
m_aPrinters.find( rData.m_aPrinterName );
if( p_it == m_aPrinters.end() ) // huh ?
{
SAL_WARN("vcl.unx.print", "CUPS printer list in disorder, "
"no dest for printer " << rData.m_aPrinterName);
return;
}
if( p_it->second.m_aInfo.m_pParser == nullptr )
{
// 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() == nullptr )
{
OUString aPrinter;
if( p_it->second.m_aInfo.m_aDriverName.startsWith("CUPS:") )
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;
}
FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
{
SAL_INFO( "vcl.unx.print", "startSpool: " << rPrintername << " " << (bQuickCommand ? "true" : "false") );
if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() )
{
SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::startSpool" );
return PrinterInfoManager::startSpool( rPrintername, bQuickCommand );
}
OUString aTmpURL, aTmpFile;
osl_createTempFile( nullptr, nullptr, &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;
}
namespace {
struct less_ppd_key
{
bool operator()(const PPDKey* left, const PPDKey* right)
{ return left->getOrderDependency() < right->getOrderDependency(); }
};
}
void CUPSManager::getOptionsFromDocumentSetup(const JobData& rJob, bool bBanner, int& rNumOptions,
cups_option_t** ppOptions)
{
rNumOptions = 0;
*ppOptions = nullptr;
// 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 )
{
std::size_t i;
std::size_t 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 );
OUString sPayLoad;
if (pValue && pValue->m_eType == eInvocation)
{
sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption;
}
if (!sPayLoad.isEmpty())
{
OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US );
OString aValue = OUStringToOString( sPayLoad, RTL_TEXTENCODING_ASCII_US );
rNumOptions = cupsAddOption(aKey.getStr(), aValue.getStr(), rNumOptions, ppOptions);
// for duplex, also set the corresponding CUPS "sides" option, see section
// "Printing On Both Sides of the Paper" at https://www.cups.org/doc/options.html
if (aKey == "Duplex")
{
if (aValue == "None")
rNumOptions = cupsAddOption("sides", "one-sided", rNumOptions, ppOptions);
else if (aValue == "DuplexTumble")
rNumOptions = cupsAddOption("sides", "two-sided-short-edge", rNumOptions, ppOptions);
else if (aValue == "DuplexNoTumble")
rNumOptions = cupsAddOption("sides", "two-sided-long-edge", rNumOptions, ppOptions);
}
}
}
}
if( rJob.m_nCopies > 1 )
{
OString aVal( OString::number( rJob.m_nCopies ) );
rNumOptions = cupsAddOption("copies", aVal.getStr(), rNumOptions, ppOptions);
aVal = OString::boolean(rJob.m_bCollate);
rNumOptions = cupsAddOption("collate", aVal.getStr(), rNumOptions, ppOptions);
}
if( ! bBanner )
{
rNumOptions = cupsAddOption("job-sheets", "none", rNumOptions, ppOptions);
}
}
namespace
{
class RTSPWDialog : public weld::GenericDialogController
{
std::unique_ptr<weld::Label> m_xText;
std::unique_ptr<weld::Label> m_xDomainLabel;
std::unique_ptr<weld::Entry> m_xDomainEdit;
std::unique_ptr<weld::Label> m_xUserLabel;
std::unique_ptr<weld::Entry> m_xUserEdit;
std::unique_ptr<weld::Label> m_xPassLabel;
std::unique_ptr<weld::Entry> m_xPassEdit;
public:
RTSPWDialog(weld::Window* pParent, std::string_view rServer, std::string_view rUserName);
OString getDomain() const
{
return OUStringToOString( m_xDomainEdit->get_text(), osl_getThreadTextEncoding() );
}
OString getUserName() const
{
return OUStringToOString( m_xUserEdit->get_text(), osl_getThreadTextEncoding() );
}
OString getPassword() const
{
return OUStringToOString( m_xPassEdit->get_text(), osl_getThreadTextEncoding() );
}
void SetDomainVisible(bool bShow)
{
m_xDomainLabel->set_visible(bShow);
m_xDomainEdit->set_visible(bShow);
}
void SetUserVisible(bool bShow)
{
m_xUserLabel->set_visible(bShow);
m_xUserEdit->set_visible(bShow);
}
void SetPassVisible(bool bShow)
{
m_xPassLabel->set_visible(bShow);
m_xPassEdit->set_visible(bShow);
}
};
RTSPWDialog::RTSPWDialog(weld::Window* pParent, std::string_view rServer, std::string_view rUserName)
: GenericDialogController(pParent, u"vcl/ui/cupspassworddialog.ui"_ustr, u"CUPSPasswordDialog"_ustr)
, m_xText(m_xBuilder->weld_label(u"text"_ustr))
, m_xDomainLabel(m_xBuilder->weld_label(u"label3"_ustr))
, m_xDomainEdit(m_xBuilder->weld_entry(u"domain"_ustr))
, m_xUserLabel(m_xBuilder->weld_label(u"label1"_ustr))
, m_xUserEdit(m_xBuilder->weld_entry(u"user"_ustr))
, m_xPassLabel(m_xBuilder->weld_label(u"label2"_ustr))
, m_xPassEdit(m_xBuilder->weld_entry(u"pass"_ustr))
{
OUString aText(m_xText->get_label());
aText = aText.replaceFirst("%s", OStringToOUString(rServer, osl_getThreadTextEncoding()));
m_xText->set_label(aText);
m_xDomainEdit->set_text(u"WORKGROUP"_ustr);
if (rUserName.empty())
m_xUserEdit->grab_focus();
else
{
m_xUserEdit->set_text(OStringToOUString(rUserName, osl_getThreadTextEncoding()));
m_xPassEdit->grab_focus();
}
}
bool AuthenticateQuery(std::string_view rServer, OString& rUserName, OString& rPassword)
{
bool bRet = false;
RTSPWDialog aDialog(Application::GetDefDialogParent(), rServer, rUserName);
if (aDialog.run() == RET_OK)
{
rUserName = aDialog.getUserName();
rPassword = aDialog.getPassword();
bRet = true;
}
return bRet;
}
}
namespace
{
OString EscapeCupsOption(const OString& rIn)
{
OStringBuffer sRet;
sal_Int32 nLen = rIn.getLength();
for (sal_Int32 i = 0; i < nLen; ++i)
{
switch(rIn[i])
{
case '\\':
case '\'':
case '\"':
case ',':
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
sRet.append('\\');
break;
}
sRet.append(rIn[i]);
}
return sRet.makeStringAndClear();
}
}
bool CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber )
{
SAL_INFO( "vcl.unx.print", "endSpool: " << rPrintername << "," << rJobTitle << " copy count = " << rDocumentJobData.m_nCopies );
int nJobID = 0;
osl::MutexGuard aGuard( m_aCUPSMutex );
std::unordered_map< OUString, int >::iterator dest_it =
m_aCUPSDestMap.find( rPrintername );
if( dest_it == m_aCUPSDestMap.end() )
{
SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::endSpool" );
return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner, rFaxNumber );
}
std::unordered_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 = nullptr;
getOptionsFromDocumentSetup(rDocumentJobData, bBanner, nNumOptions, &pOptions);
PrinterInfo aInfo(getPrinterInfo(rPrintername));
if (!aInfo.m_aAuthInfoRequired.isEmpty())
{
bool bDomain(false), bUser(false), bPass(false);
sal_Int32 nIndex = 0;
do
{
std::u16string_view aToken = o3tl::getToken(aInfo.m_aAuthInfoRequired, 0, ',', nIndex);
if (aToken == u"domain")
bDomain = true;
else if (aToken == u"username")
bUser = true;
else if (aToken == u"password")
bPass = true;
}
while (nIndex >= 0);
if (bDomain || bUser || bPass)
{
OString sPrinterName(OUStringToOString(rPrintername, RTL_TEXTENCODING_UTF8));
OString sUser = cupsUser();
RTSPWDialog aDialog(Application::GetDefDialogParent(), sPrinterName, sUser);
aDialog.SetDomainVisible(bDomain);
aDialog.SetUserVisible(bUser);
aDialog.SetPassVisible(bPass);
if (aDialog.run() == RET_OK)
{
OString sAuth;
if (bDomain)
sAuth = EscapeCupsOption(aDialog.getDomain());
if (bUser)
{
if (bDomain)
sAuth += ",";
sAuth += EscapeCupsOption(aDialog.getUserName());
}
if (bPass)
{
if (bUser || bDomain)
sAuth += ",";
sAuth += EscapeCupsOption(aDialog.getPassword());
}
nNumOptions = cupsAddOption("auth-info", sAuth.getStr(), nNumOptions, &pOptions);
}
}
}
OString sJobName(OUStringToOString(rJobTitle, aEnc));
//fax4CUPS, "the job name will be dialled for you"
//so override the jobname with the desired number
if (!rFaxNumber.isEmpty())
{
sJobName = OUStringToOString(rFaxNumber, aEnc);
}
cups_dest_t* pDest = m_pDests + dest_it->second;
nJobID = cupsPrintFile(pDest->name,
it->second.getStr(),
sJobName.getStr(),
nNumOptions, pOptions);
SAL_INFO("vcl.unx.print", "cupsPrintFile( " << pDest->name << ", "
<< it->second << ", " << rJobTitle << ", " << nNumOptions
<< ", " << pOptions << " ) returns " << nJobID);
for( int n = 0; n < nNumOptions; n++ )
SAL_INFO("vcl.unx.print",
" option " << pOptions[n].name << "=" << pOptions[n].value);
#if OSL_DEBUG_LEVEL > 1
OString aCmd( "cp " );
aCmd += it->second.getStr();
aCmd += OString( " $HOME/cupsprint.ps" );
system( aCmd.getStr() );
#endif
unlink( it->second.getStr() );
m_aSpoolFiles.erase(it);
if( pOptions )
cupsFreeOptions( nNumOptions, pOptions );
}
return nJobID != 0;
}
bool CUPSManager::checkPrintersChanged( bool bWait )
{
bool bChanged = false;
if( bWait )
{
if( m_aDestThread )
{
// initial asynchronous detection still running
SAL_INFO("vcl.unx.print", "syncing cups discovery thread");
osl_joinWithThread( m_aDestThread );
osl_destroyThread( m_aDestThread );
m_aDestThread = nullptr;
SAL_INFO("vcl.unx.print", "done: syncing cups discovery thread");
}
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 )
cupsFreeDests( m_nDests, m_pDests );
m_nDests = 0;
m_pDests = nullptr;
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;
}
const char* CUPSManager::authenticateUser()
{
const char* pRet = nullptr;
osl::MutexGuard aGuard( m_aCUPSMutex );
OString aUser = cupsUser();
OString aServer = cupsServer();
OString aPassword;
if (AuthenticateQuery(aServer, aUser, aPassword))
{
m_aPassword = aPassword;
m_aUser = aUser;
cupsSetUser( m_aUser.getStr() );
pRet = m_aPassword.getStr();
}
return pRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V595 The 'pOptions' pointer was utilized before it was verified against nullptr. Check lines: 891, 902.