1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <unistd.h>
28 #include <sys/wait.h>
29 #include <signal.h>
30 
31 #include "cupsmgr.hxx"
32 #include "vcl/fontmanager.hxx"
33 #include "vcl/strhelper.hxx"
34 
35 #include "unx/saldata.hxx"
36 
37 #include "tools/urlobj.hxx"
38 #include "tools/stream.hxx"
39 #include "tools/debug.hxx"
40 #include "tools/config.hxx"
41 
42 #include "i18npool/paper.hxx"
43 
44 #include "rtl/strbuf.hxx"
45 
46 #include "osl/thread.hxx"
47 #include "osl/mutex.hxx"
48 #include "osl/process.h"
49 
50 // filename of configuration files
51 #define PRINT_FILENAME  "psprint.conf"
52 // the group of the global defaults
53 #define GLOBAL_DEFAULTS_GROUP "__Global_Printer_Defaults__"
54 
55 #include <hash_set>
56 
57 using namespace psp;
58 using namespace rtl;
59 using namespace osl;
60 
61 namespace psp
62 {
63     class SystemQueueInfo : public Thread
64     {
65         mutable Mutex               m_aMutex;
66         bool                        m_bChanged;
67         std::list< PrinterInfoManager::SystemPrintQueue >
68                                     m_aQueues;
69         OUString                    m_aCommand;
70 
71         virtual void run();
72 
73         public:
74         SystemQueueInfo();
75         ~SystemQueueInfo();
76 
77         bool hasChanged() const;
78         OUString getCommand() const;
79 
80         // sets changed status to false; therefore not const
81         void getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues );
82     };
83 } // namespace
84 
85 /*
86 *  class PrinterInfoManager
87 */
88 
89 // -----------------------------------------------------------------
90 
get()91 PrinterInfoManager& PrinterInfoManager::get()
92 {
93     SalData* pSalData = GetSalData();
94 
95     if( ! pSalData->m_pPIManager )
96     {
97         pSalData->m_pPIManager = CUPSManager::tryLoadCUPS();
98         if( ! pSalData->m_pPIManager )
99             pSalData->m_pPIManager = new PrinterInfoManager();
100 
101         pSalData->m_pPIManager->initialize();
102         #if OSL_DEBUG_LEVEL > 1
103         fprintf( stderr, "PrinterInfoManager::get create Manager of type %d\n", pSalData->m_pPIManager->getType() );
104         #endif
105     }
106 
107     return *pSalData->m_pPIManager;
108 }
109 
release()110 void PrinterInfoManager::release()
111 {
112     SalData* pSalData = GetSalData();
113     delete pSalData->m_pPIManager;
114     pSalData->m_pPIManager = NULL;
115 }
116 
117 // -----------------------------------------------------------------
118 
PrinterInfoManager(Type eType)119 PrinterInfoManager::PrinterInfoManager( Type eType ) :
120     m_pQueueInfo( NULL ),
121     m_eType( eType ),
122     m_bUseIncludeFeature( false ),
123     m_bUseJobPatch( true ),
124     m_aSystemDefaultPaper( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ),
125     m_bDisableCUPS( false )
126 {
127     if( eType == Default )
128         m_pQueueInfo = new SystemQueueInfo();
129     initSystemDefaultPaper();
130 }
131 
132 // -----------------------------------------------------------------
133 
~PrinterInfoManager()134 PrinterInfoManager::~PrinterInfoManager()
135 {
136     delete m_pQueueInfo;
137     #if OSL_DEBUG_LEVEL > 1
138     fprintf( stderr, "PrinterInfoManager: destroyed Manager of type %d\n", getType() );
139     #endif
140 }
141 
142 // -----------------------------------------------------------------
143 
isCUPSDisabled() const144 bool PrinterInfoManager::isCUPSDisabled() const
145 {
146     return m_bDisableCUPS;
147 }
148 
149 // -----------------------------------------------------------------
150 
setCUPSDisabled(bool bDisable)151 void PrinterInfoManager::setCUPSDisabled( bool bDisable )
152 {
153     m_bDisableCUPS = bDisable;
154     writePrinterConfig();
155     // actually we know the printers changed
156     // however this triggers reinitialization the right way
157     checkPrintersChanged( true );
158 }
159 
160 // -----------------------------------------------------------------
161 
initSystemDefaultPaper()162 void PrinterInfoManager::initSystemDefaultPaper()
163 {
164     m_aSystemDefaultPaper = rtl::OStringToOUString(
165         PaperInfo::toPSName(PaperInfo::getSystemDefaultPaper().getPaper()),
166         RTL_TEXTENCODING_UTF8);
167 }
168 
169 // -----------------------------------------------------------------
170 
checkPrintersChanged(bool bWait)171 bool PrinterInfoManager::checkPrintersChanged( bool bWait )
172 {
173     // check if files were created, deleted or modified since initialize()
174     ::std::list< WatchFile >::const_iterator it;
175     bool bChanged = false;
176     for( it = m_aWatchFiles.begin(); it != m_aWatchFiles.end() && ! bChanged; ++it )
177     {
178         DirectoryItem aItem;
179         if( DirectoryItem::get( it->m_aFilePath, aItem ) )
180         {
181             if( it->m_aModified.Seconds != 0 )
182                 bChanged = true; // file probably has vanished
183         }
184         else
185         {
186             FileStatus aStatus( FileStatusMask_ModifyTime );
187             if( aItem.getFileStatus( aStatus ) )
188                 bChanged = true; // unlikely but not impossible
189             else
190             {
191                 TimeValue aModified = aStatus.getModifyTime();
192                 if( aModified.Seconds != it->m_aModified.Seconds )
193                     bChanged = true;
194             }
195         }
196     }
197 
198     if( bWait && m_pQueueInfo )
199     {
200         #if OSL_DEBUG_LEVEL > 1
201         fprintf( stderr, "syncing printer discovery thread\n" );
202         #endif
203         m_pQueueInfo->join();
204         #if OSL_DEBUG_LEVEL > 1
205         fprintf( stderr, "done: syncing printer discovery thread\n" );
206         #endif
207     }
208 
209     if( ! bChanged && m_pQueueInfo )
210         bChanged = m_pQueueInfo->hasChanged();
211     if( bChanged )
212     {
213         initialize();
214     }
215 
216     return bChanged;
217 }
218 
219 // -----------------------------------------------------------------
220 
initialize()221 void PrinterInfoManager::initialize()
222 {
223     m_bUseIncludeFeature = false;
224     rtl_TextEncoding aEncoding = gsl_getSystemTextEncoding();
225     m_aPrinters.clear();
226     m_aWatchFiles.clear();
227     OUString aDefaultPrinter;
228 
229     // first initialize the global defaults
230     // have to iterate over all possible files
231     // there should be only one global setup section in all
232     // available config files
233     m_aGlobalDefaults = PrinterInfo();
234 
235     // need a parser for the PPDContext. generic printer should do.
236     m_aGlobalDefaults.m_pParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) );
237     m_aGlobalDefaults.m_aContext.setParser( m_aGlobalDefaults.m_pParser );
238     m_aGlobalDefaults.m_bPerformFontSubstitution = true;
239     m_bDisableCUPS = false;
240 
241     if( ! m_aGlobalDefaults.m_pParser )
242     {
243         #if OSL_DEBUG_LEVEL > 1
244         fprintf( stderr, "Error: no default PPD file SGENPRT available, shutting down psprint...\n" );
245         #endif
246         return;
247     }
248 
249     std::list< OUString > aDirList;
250     psp::getPrinterPathList( aDirList, NULL );
251     std::list< OUString >::const_iterator print_dir_it;
252     for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it )
253     {
254         INetURLObject aFile( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
255         aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) );
256         Config aConfig( aFile.PathToFileName() );
257         if( aConfig.HasGroup( GLOBAL_DEFAULTS_GROUP ) )
258         {
259             #if OSL_DEBUG_LEVEL > 1
260             fprintf( stderr, "found global defaults in %s\n", OUStringToOString( aFile.PathToFileName(), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
261             #endif
262             aConfig.SetGroup( GLOBAL_DEFAULTS_GROUP );
263 
264             ByteString aValue( aConfig.ReadKey( "Copies" ) );
265             if( aValue.Len() )
266                 m_aGlobalDefaults.m_nCopies = aValue.ToInt32();
267 
268             aValue = aConfig.ReadKey( "Orientation" );
269             if( aValue.Len() )
270                 m_aGlobalDefaults.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait;
271 
272             aValue = aConfig.ReadKey( "MarginAdjust" );
273             if( aValue.Len() )
274             {
275                 m_aGlobalDefaults.m_nLeftMarginAdjust   = aValue.GetToken( 0, ',' ).ToInt32();
276                 m_aGlobalDefaults.m_nRightMarginAdjust  = aValue.GetToken( 1, ',' ).ToInt32();
277                 m_aGlobalDefaults.m_nTopMarginAdjust    = aValue.GetToken( 2, ',' ).ToInt32();
278                 m_aGlobalDefaults.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32();
279             }
280 
281             aValue = aConfig.ReadKey( "ColorDepth", "24" );
282             if( aValue.Len() )
283                 m_aGlobalDefaults.m_nColorDepth = aValue.ToInt32();
284 
285             aValue = aConfig.ReadKey( "ColorDevice" );
286             if( aValue.Len() )
287                 m_aGlobalDefaults.m_nColorDevice = aValue.ToInt32();
288 
289             aValue = aConfig.ReadKey( "PSLevel" );
290             if( aValue.Len() )
291                 m_aGlobalDefaults.m_nPSLevel = aValue.ToInt32();
292 
293             aValue = aConfig.ReadKey( "PDFDevice" );
294             if( aValue.Len() )
295                 m_aGlobalDefaults.m_nPDFDevice = aValue.ToInt32();
296 
297             aValue = aConfig.ReadKey( "PerformFontSubstitution" );
298             if( aValue.Len() )
299             {
300                 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
301                     m_aGlobalDefaults.m_bPerformFontSubstitution = true;
302                 else
303                     m_aGlobalDefaults.m_bPerformFontSubstitution = false;
304             }
305 
306             aValue = aConfig.ReadKey( "DisableCUPS" );
307             if( aValue.Len() )
308             {
309                 if( aValue.Equals( "1" ) || aValue.EqualsIgnoreCaseAscii( "true" ) )
310                     m_bDisableCUPS = true;
311                 else
312                     m_bDisableCUPS = false;
313             }
314 
315             // get the PPDContext of global JobData
316             for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ )
317             {
318                 ByteString aKey( aConfig.GetKeyName( nKey ) );
319                 if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL )
320                 {
321                     aValue = aConfig.ReadKey( aKey );
322                     const PPDKey* pKey = m_aGlobalDefaults.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) );
323                     if( pKey )
324                     {
325                         m_aGlobalDefaults.m_aContext.
326                         setValue( pKey,
327                         aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ),
328                         sal_True );
329                     }
330                 }
331                 else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL )
332                 {
333                     aValue = aConfig.ReadKey( aKey );
334                     m_aGlobalDefaults.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 );
335                 }
336             }
337             #if OSL_DEBUG_LEVEL > 1
338             fprintf( stderr, "global settings: fontsubst = %s, %d substitutes\n", m_aGlobalDefaults.m_bPerformFontSubstitution ? "true" : "false", (int)m_aGlobalDefaults.m_aFontSubstitutes.size() );
339             #endif
340         }
341     }
342     setDefaultPaper( m_aGlobalDefaults.m_aContext );
343     fillFontSubstitutions( m_aGlobalDefaults );
344 
345     // now collect all available printers
346     for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it )
347     {
348         INetURLObject aDir( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
349         INetURLObject aFile( aDir );
350         aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) );
351 
352         // check directory validity
353         OUString aUniPath;
354         FileBase::getFileURLFromSystemPath( aDir.PathToFileName(), aUniPath );
355         Directory aDirectory( aUniPath );
356         if( aDirectory.open() )
357             continue;
358         aDirectory.close();
359 
360 
361         FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aUniPath );
362         FileStatus aStatus( FileStatusMask_ModifyTime );
363         DirectoryItem aItem;
364 
365         // setup WatchFile list
366         WatchFile aWatchFile;
367         aWatchFile.m_aFilePath = aUniPath;
368         if( ! DirectoryItem::get( aUniPath, aItem ) &&
369             ! aItem.getFileStatus( aStatus ) )
370         {
371             aWatchFile.m_aModified = aStatus.getModifyTime();
372         }
373         else
374         {
375             aWatchFile.m_aModified.Seconds = 0;
376             aWatchFile.m_aModified.Nanosec = 0;
377         }
378         m_aWatchFiles.push_back( aWatchFile );
379 
380         Config aConfig( aFile.PathToFileName() );
381         for( int nGroup = 0; nGroup < aConfig.GetGroupCount(); nGroup++ )
382         {
383             aConfig.SetGroup( aConfig.GetGroupName( nGroup ) );
384             ByteString aValue = aConfig.ReadKey( "Printer" );
385             if( aValue.Len() )
386             {
387                 OUString aPrinterName;
388 
389                 int nNamePos = aValue.Search( '/' );
390                 // check for valid value of "Printer"
391                 if( nNamePos == STRING_NOTFOUND )
392                     continue;
393 
394                 Printer aPrinter;
395                 // initialize to global defaults
396                 aPrinter.m_aInfo = m_aGlobalDefaults;
397                 // global settings do not default the printer substitution
398                 // list ! the substitution list in there is only used for
399                 // newly created printers
400                 aPrinter.m_aInfo.m_aFontSubstitutes.clear();
401                 aPrinter.m_aInfo.m_aFontSubstitutions.clear();
402 
403                 aPrinterName = String( aValue.Copy( nNamePos+1 ), RTL_TEXTENCODING_UTF8 );
404                 aPrinter.m_aInfo.m_aPrinterName     = aPrinterName;
405                 aPrinter.m_aInfo.m_aDriverName      = String( aValue.Copy( 0, nNamePos ), RTL_TEXTENCODING_UTF8 );
406 
407                 // set parser, merge settings
408                 // don't do this for CUPS printers as this is done
409                 // by the CUPS system itself
410                 if( aPrinter.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 )
411                 {
412                     aPrinter.m_aInfo.m_pParser          = PPDParser::getParser( aPrinter.m_aInfo.m_aDriverName );
413                     aPrinter.m_aInfo.m_aContext.setParser( aPrinter.m_aInfo.m_pParser );
414                     // note: setParser also purges the context
415 
416                     // ignore this printer if its driver is not found
417                     if( ! aPrinter.m_aInfo.m_pParser )
418                         continue;
419 
420                     // merge the ppd context keys if the printer has the same keys and values
421                     // this is a bit tricky, since it involves mixing two PPDs
422                     // without constraints which might end up badly
423                     // this feature should be use with caution
424                     // it is mainly to select default paper sizes for new printers
425                     for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ )
426                     {
427                         const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified );
428                         const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey );
429                         const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL;
430                         if( pDefKey && pPrinterKey )
431                             // at least the options exist in both PPDs
432                         {
433                             if( pDefValue )
434                             {
435                                 const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption );
436                                 if( pPrinterValue )
437                                     // the printer has a corresponding option for the key
438                                 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue );
439                             }
440                             else
441                                 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL );
442                         }
443                     }
444 
445                     aValue = aConfig.ReadKey( "Command" );
446                     // no printer without a command
447                     if( ! aValue.Len() )
448                     {
449                         /*  TODO:
450                         *  porters: please append your platform to the Solaris
451                         *  case if your platform has SystemV printing per default.
452                         */
453                         #if defined SOLARIS
454                         aValue = "lp";
455                         #else
456                         aValue = "lpr";
457                         #endif
458                     }
459                     aPrinter.m_aInfo.m_aCommand = String( aValue, RTL_TEXTENCODING_UTF8 );
460                 }
461 
462                 aValue = aConfig.ReadKey( "QuickCommand" );
463                 aPrinter.m_aInfo.m_aQuickCommand = String( aValue, RTL_TEXTENCODING_UTF8 );
464 
465                 aValue = aConfig.ReadKey( "Features" );
466                 aPrinter.m_aInfo.m_aFeatures = String( aValue, RTL_TEXTENCODING_UTF8 );
467 
468                 // override the settings in m_aGlobalDefaults if keys exist
469                 aValue = aConfig.ReadKey( "DefaultPrinter" );
470                 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
471                     aDefaultPrinter = aPrinterName;
472 
473                 aValue = aConfig.ReadKey( "Location" );
474                 aPrinter.m_aInfo.m_aLocation = String( aValue, RTL_TEXTENCODING_UTF8 );
475 
476                 aValue = aConfig.ReadKey( "Comment" );
477                 aPrinter.m_aInfo.m_aComment = String( aValue, RTL_TEXTENCODING_UTF8 );
478 
479                 aValue = aConfig.ReadKey( "Copies" );
480                 if( aValue.Len() )
481                     aPrinter.m_aInfo.m_nCopies = aValue.ToInt32();
482 
483                 aValue = aConfig.ReadKey( "Orientation" );
484                 if( aValue.Len() )
485                     aPrinter.m_aInfo.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait;
486 
487                 aValue = aConfig.ReadKey( "MarginAdjust" );
488                 if( aValue.Len() )
489                 {
490                     aPrinter.m_aInfo.m_nLeftMarginAdjust    = aValue.GetToken( 0, ',' ).ToInt32();
491                     aPrinter.m_aInfo.m_nRightMarginAdjust   = aValue.GetToken( 1, ',' ).ToInt32();
492                     aPrinter.m_aInfo.m_nTopMarginAdjust     = aValue.GetToken( 2, ',' ).ToInt32();
493                     aPrinter.m_aInfo.m_nBottomMarginAdjust  = aValue.GetToken( 3, ',' ).ToInt32();
494                 }
495 
496                 aValue = aConfig.ReadKey( "ColorDepth" );
497                 if( aValue.Len() )
498                     aPrinter.m_aInfo.m_nColorDepth = aValue.ToInt32();
499 
500                 aValue = aConfig.ReadKey( "ColorDevice" );
501                 if( aValue.Len() )
502                     aPrinter.m_aInfo.m_nColorDevice = aValue.ToInt32();
503 
504                 aValue = aConfig.ReadKey( "PSLevel" );
505                 if( aValue.Len() )
506                     aPrinter.m_aInfo.m_nPSLevel = aValue.ToInt32();
507 
508                 aValue = aConfig.ReadKey( "PDFDevice" );
509                 if( aValue.Len() )
510                     aPrinter.m_aInfo.m_nPDFDevice = aValue.ToInt32();
511 
512                 aValue = aConfig.ReadKey( "PerformFontSubstitution" );
513                 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
514                     aPrinter.m_aInfo.m_bPerformFontSubstitution = true;
515                 else
516                     aPrinter.m_aInfo.m_bPerformFontSubstitution = false;
517 
518                 // now iterate over all keys to extract multi key information:
519                 // 1. PPDContext information
520                 // 2. Font substitution table
521                 for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ )
522                 {
523                     ByteString aKey( aConfig.GetKeyName( nKey ) );
524                     if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL && aPrinter.m_aInfo.m_pParser )
525                     {
526                         aValue = aConfig.ReadKey( aKey );
527                         const PPDKey* pKey = aPrinter.m_aInfo.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) );
528                         if( pKey )
529                         {
530                             aPrinter.m_aInfo.m_aContext.
531                             setValue( pKey,
532                             aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ),
533                             sal_True );
534                         }
535                     }
536                     else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL )
537                     {
538                         aValue = aConfig.ReadKey( aKey );
539                         aPrinter.m_aInfo.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 );
540                     }
541                 }
542 
543                 setDefaultPaper( aPrinter.m_aInfo.m_aContext );
544                 fillFontSubstitutions( aPrinter.m_aInfo );
545 
546                 // finally insert printer
547                 FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aPrinter.m_aFile );
548                 aPrinter.m_bModified    = false;
549                 aPrinter.m_aGroup       = aConfig.GetGroupName( nGroup );
550                 std::hash_map< OUString, Printer, OUStringHash >::const_iterator find_it =
551                 m_aPrinters.find( aPrinterName );
552                 if( find_it != m_aPrinters.end() )
553                 {
554                     aPrinter.m_aAlternateFiles = find_it->second.m_aAlternateFiles;
555                     aPrinter.m_aAlternateFiles.push_front( find_it->second.m_aFile );
556                 }
557                 m_aPrinters[ aPrinterName ] = aPrinter;
558             }
559         }
560     }
561 
562     // set default printer
563     if( m_aPrinters.size() )
564     {
565         if( m_aPrinters.find( aDefaultPrinter ) == m_aPrinters.end() )
566             aDefaultPrinter = m_aPrinters.begin()->first;
567     }
568     else
569         aDefaultPrinter = OUString();
570     m_aDefaultPrinter = aDefaultPrinter;
571 
572     if( m_eType != Default )
573         return;
574 
575     // add a default printer for every available print queue
576     // merge paper and font substitution from default printer,
577     // all else from global defaults
578     PrinterInfo aMergeInfo( m_aGlobalDefaults );
579     aMergeInfo.m_aDriverName    = String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) );
580     aMergeInfo.m_aFeatures      = String( RTL_CONSTASCII_USTRINGPARAM( "autoqueue" ) );
581 
582     if( m_aDefaultPrinter.getLength() )
583     {
584         PrinterInfo aDefaultInfo( getPrinterInfo( m_aDefaultPrinter ) );
585         aMergeInfo.m_bPerformFontSubstitution = aDefaultInfo.m_bPerformFontSubstitution;
586         fillFontSubstitutions( aMergeInfo );
587 
588         const PPDKey* pDefKey           = aDefaultInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
589         const PPDKey* pMergeKey         = aMergeInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
590         const PPDValue* pDefValue       = aDefaultInfo.m_aContext.getValue( pDefKey );
591         const PPDValue* pMergeValue     = pMergeKey ? pMergeKey->getValue( pDefValue->m_aOption ) : NULL;
592         if( pMergeKey && pMergeValue )
593             aMergeInfo.m_aContext.setValue( pMergeKey, pMergeValue );
594     }
595 
596     getSystemPrintQueues();
597     for( ::std::list< SystemPrintQueue >::iterator it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it )
598     {
599         String aPrinterName( RTL_CONSTASCII_USTRINGPARAM( "<" ) );
600         aPrinterName += String( it->m_aQueue );
601         aPrinterName.Append( '>' );
602 
603         if( m_aPrinters.find( aPrinterName ) != m_aPrinters.end() )
604             // probably user made this one permanent in padmin
605         continue;
606 
607         String aCmd( m_aSystemPrintCommand );
608         aCmd.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ), it->m_aQueue );
609 
610         Printer aPrinter;
611 
612         // initialize to merged defaults
613         aPrinter.m_aInfo = aMergeInfo;
614         aPrinter.m_aInfo.m_aPrinterName     = aPrinterName;
615         aPrinter.m_aInfo.m_aCommand         = aCmd;
616         aPrinter.m_aInfo.m_aComment         = it->m_aComment;
617         aPrinter.m_aInfo.m_aLocation        = it->m_aLocation;
618         aPrinter.m_bModified                = false;
619         aPrinter.m_aGroup                   = ByteString( aPrinterName, aEncoding ); //provide group name in case user makes this one permanent in padmin
620 
621         m_aPrinters[ aPrinterName ] = aPrinter;
622     }
623 }
624 
625 // -----------------------------------------------------------------
626 
listPrinters(::std::list<OUString> & rList) const627 void PrinterInfoManager::listPrinters( ::std::list< OUString >& rList ) const
628 {
629     ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it;
630     rList.clear();
631     for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it )
632         rList.push_back( it->first );
633 }
634 
635 // -----------------------------------------------------------------
636 
getPrinterInfo(const OUString & rPrinter) const637 const PrinterInfo& PrinterInfoManager::getPrinterInfo( const OUString& rPrinter ) const
638 {
639     static PrinterInfo aEmptyInfo;
640     ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it = m_aPrinters.find( rPrinter );
641 
642     DBG_ASSERT( it != m_aPrinters.end(), "Do not ask for info about nonexistent printers" );
643 
644     return it != m_aPrinters.end() ? it->second.m_aInfo : aEmptyInfo;
645 }
646 
647 // -----------------------------------------------------------------
648 
changePrinterInfo(const OUString & rPrinter,const PrinterInfo & rNewInfo)649 void PrinterInfoManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
650 {
651     ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinter );
652 
653     DBG_ASSERT( it != m_aPrinters.end(), "Do not change nonexistant printers" );
654 
655     if( it != m_aPrinters.end() )
656     {
657         it->second.m_aInfo      = rNewInfo;
658         // recalculate font substitutions
659         fillFontSubstitutions( it->second.m_aInfo );
660         it->second.m_bModified  = true;
661         writePrinterConfig();
662     }
663 }
664 
665 // -----------------------------------------------------------------
666 
667 // need to check writeability / creatability of config files
checkWriteability(const OUString & rUniPath)668 static bool checkWriteability( const OUString& rUniPath )
669 {
670     bool bRet = false;
671     OUString aSysPath;
672     FileBase::getSystemPathFromFileURL( rUniPath, aSysPath );
673     SvFileStream aStream( aSysPath, STREAM_READ | STREAM_WRITE );
674     if( aStream.IsOpen() && aStream.IsWritable() )
675         bRet = true;
676     return bRet;
677 }
678 
writePrinterConfig()679 bool PrinterInfoManager::writePrinterConfig()
680 {
681     // find at least one writeable config
682     ::std::hash_map< OUString, Config*, OUStringHash > files;
683     ::std::hash_map< OUString, int, OUStringHash > rofiles;
684     ::std::hash_map< OUString, Config*, OUStringHash >::iterator file_it;
685 
686     for( ::std::list< WatchFile >::const_iterator wit = m_aWatchFiles.begin(); wit != m_aWatchFiles.end(); ++wit )
687     {
688         if( checkWriteability( wit->m_aFilePath ) )
689         {
690             files[ wit->m_aFilePath ] = new Config( wit->m_aFilePath );
691             break;
692         }
693     }
694 
695     if( files.empty() )
696         return false;
697 
698     Config* pGlobal = files.begin()->second;
699     pGlobal->SetGroup( GLOBAL_DEFAULTS_GROUP );
700     pGlobal->WriteKey( "DisableCUPS", m_bDisableCUPS ? "true" : "false" );
701 
702     ::std::hash_map< OUString, Printer, OUStringHash >::iterator it;
703     for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it )
704     {
705         if( ! it->second.m_bModified )
706             // printer was not changed, do nothing
707         continue;
708 
709         // don't save autoqueue printers
710         sal_Int32 nIndex = 0;
711         bool bAutoQueue = false;
712         while( nIndex != -1 && ! bAutoQueue )
713         {
714             OUString aToken( it->second.m_aInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
715             if( aToken.getLength() && aToken.compareToAscii( "autoqueue" ) == 0 )
716                 bAutoQueue = true;
717         }
718         if( bAutoQueue )
719             continue;
720 
721         if( it->second.m_aFile.getLength() )
722         {
723             // check if file is writable
724             if( files.find( it->second.m_aFile ) == files.end() )
725             {
726                 bool bInsertToNewFile = false;
727                 // maybe it is simply not inserted yet
728                 if( rofiles.find( it->second.m_aFile ) == rofiles.end() )
729                 {
730                     if( checkWriteability( it->second.m_aFile ) )
731                         files[ it->second.m_aFile ] = new Config( it->second.m_aFile );
732                     else
733                         bInsertToNewFile = true;
734                 }
735                 else
736                     bInsertToNewFile = true;
737                 // original file is read only, insert printer in a new writeable file
738                 if( bInsertToNewFile )
739                 {
740                     rofiles[ it->second.m_aFile ] = 1;
741                     // update alternate file list
742                     // the remove operation ensures uniqueness of each alternate
743                     it->second.m_aAlternateFiles.remove( it->second.m_aFile );
744                     it->second.m_aAlternateFiles.remove( files.begin()->first );
745                     it->second.m_aAlternateFiles.push_front( it->second.m_aFile );
746                     // update file
747                     it->second.m_aFile = files.begin()->first;
748                 }
749             }
750         }
751         else // a new printer, write it to the first file available
752             it->second.m_aFile = files.begin()->first;
753 
754         if( ! it->second.m_aGroup.getLength() ) // probably a new printer
755             it->second.m_aGroup = OString( it->first.getStr(), it->first.getLength(), RTL_TEXTENCODING_UTF8 );
756 
757         if( files.find( it->second.m_aFile ) != files.end() )
758         {
759             Config* pConfig = files[ it->second.m_aFile ];
760             pConfig->DeleteGroup( it->second.m_aGroup ); // else some old keys may remain
761             pConfig->SetGroup( it->second.m_aGroup );
762 
763             ByteString aValue( String( it->second.m_aInfo.m_aDriverName ), RTL_TEXTENCODING_UTF8 );
764             aValue += '/';
765             aValue += ByteString( String( it->first ), RTL_TEXTENCODING_UTF8 );
766             pConfig->WriteKey( "Printer", aValue );
767             pConfig->WriteKey( "DefaultPrinter", it->first == m_aDefaultPrinter ? "1" : "0" );
768             pConfig->WriteKey( "Location", ByteString( String( it->second.m_aInfo.m_aLocation ), RTL_TEXTENCODING_UTF8 ) );
769             pConfig->WriteKey( "Comment", ByteString( String( it->second.m_aInfo.m_aComment ), RTL_TEXTENCODING_UTF8 ) );
770             pConfig->WriteKey( "Command", ByteString( String( it->second.m_aInfo.m_aCommand ), RTL_TEXTENCODING_UTF8 ) );
771             pConfig->WriteKey( "QuickCommand", ByteString( String( it->second.m_aInfo.m_aQuickCommand ), RTL_TEXTENCODING_UTF8 ) );
772             pConfig->WriteKey( "Features", ByteString( String( it->second.m_aInfo.m_aFeatures ), RTL_TEXTENCODING_UTF8 ) );
773             pConfig->WriteKey( "Copies", ByteString::CreateFromInt32( it->second.m_aInfo.m_nCopies ) );
774             pConfig->WriteKey( "Orientation", it->second.m_aInfo.m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait" );
775             pConfig->WriteKey( "PSLevel", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPSLevel ) );
776             pConfig->WriteKey( "PDFDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPDFDevice ) );
777             pConfig->WriteKey( "ColorDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDevice ) );
778             pConfig->WriteKey( "ColorDepth", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDepth ) );
779             aValue = ByteString::CreateFromInt32( it->second.m_aInfo.m_nLeftMarginAdjust );
780             aValue += ',';
781             aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nRightMarginAdjust );
782             aValue += ',';
783             aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nTopMarginAdjust );
784             aValue += ',';
785             aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nBottomMarginAdjust );
786             pConfig->WriteKey( "MarginAdjust", aValue );
787 
788             if( it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 )
789             {
790                 // write PPDContext (not for CUPS)
791                 for( int i = 0; i < it->second.m_aInfo.m_aContext.countValuesModified(); i++ )
792                 {
793                     const PPDKey* pKey = it->second.m_aInfo.m_aContext.getModifiedKey( i );
794                     ByteString aKey( "PPD_" );
795                     aKey += ByteString( pKey->getKey(), RTL_TEXTENCODING_ISO_8859_1 );
796 
797                     const PPDValue* pValue = it->second.m_aInfo.m_aContext.getValue( pKey );
798                     aValue = pValue ? ByteString( pValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ) : ByteString( "*nil" );
799                     pConfig->WriteKey( aKey, aValue );
800                 }
801             }
802 
803             // write font substitution table
804             pConfig->WriteKey( "PerformFontSubstitution", it->second.m_aInfo.m_bPerformFontSubstitution ? "true" : "false" );
805             for( ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst = it->second.m_aInfo.m_aFontSubstitutes.begin();
806             subst != it->second.m_aInfo.m_aFontSubstitutes.end(); ++subst )
807             {
808                 ByteString aKey( "SubstFont_" );
809                 aKey.Append( OUStringToOString( subst->first, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
810                 pConfig->WriteKey( aKey, OUStringToOString( subst->second, RTL_TEXTENCODING_ISO_8859_1 ) );
811             }
812         }
813     }
814 
815     // get rid of Config objects. this also writes any changes
816     for( file_it = files.begin(); file_it != files.end(); ++file_it )
817         delete file_it->second;
818 
819     return true;
820 }
821 
822 // -----------------------------------------------------------------
823 
addPrinter(const OUString & rPrinterName,const OUString & rDriverName)824 bool PrinterInfoManager::addPrinter( const OUString& rPrinterName, const OUString& rDriverName )
825 {
826     bool bSuccess = false;
827 
828     const PPDParser* pParser = NULL;
829     if( m_aPrinters.find( rPrinterName ) == m_aPrinters.end() && ( pParser = PPDParser::getParser( rDriverName ) ) )
830     {
831         Printer aPrinter;
832         aPrinter.m_bModified                        = true;
833         aPrinter.m_aInfo                            = m_aGlobalDefaults;
834         aPrinter.m_aInfo.m_aDriverName              = rDriverName;
835         aPrinter.m_aInfo.m_pParser                  = pParser;
836         aPrinter.m_aInfo.m_aContext.setParser( pParser );
837         aPrinter.m_aInfo.m_aPrinterName             = rPrinterName;
838 
839         fillFontSubstitutions( aPrinter.m_aInfo );
840         // merge PPD values with global defaults
841         for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ )
842         {
843             const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified );
844             const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey );
845             const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL;
846             if( pDefKey && pPrinterKey )
847                 // at least the options exist in both PPDs
848             {
849                 if( pDefValue )
850                 {
851                     const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption );
852                     if( pPrinterValue )
853                         // the printer has a corresponding option for the key
854                     aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue );
855                 }
856                 else
857                     aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL );
858             }
859         }
860 
861         m_aPrinters[ rPrinterName ] = aPrinter;
862         bSuccess = true;
863         #if OSL_DEBUG_LEVEL > 1
864         fprintf( stderr, "new printer %s, level = %d, pdfdevice = %d, colordevice = %d, depth = %d\n",
865         OUStringToOString( rPrinterName, osl_getThreadTextEncoding() ).getStr(),
866         m_aPrinters[rPrinterName].m_aInfo.m_nPSLevel,
867         m_aPrinters[rPrinterName].m_aInfo.m_nPDFDevice,
868         m_aPrinters[rPrinterName].m_aInfo.m_nColorDevice,
869         m_aPrinters[rPrinterName].m_aInfo.m_nColorDepth );
870         #endif
871         // comment: logically one should writePrinterConfig() here
872         // but immediately after addPrinter() a changePrinterInfo()
873         // will follow (see padmin code), which writes it again,
874         // so we can currently save some performance here
875     }
876     return bSuccess;
877 }
878 
879 // -----------------------------------------------------------------
880 
removePrinter(const OUString & rPrinterName,bool bCheckOnly)881 bool PrinterInfoManager::removePrinter( const OUString& rPrinterName, bool bCheckOnly )
882 {
883     bool bSuccess = true;
884 
885     ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName );
886     if( it != m_aPrinters.end() )
887     {
888         if( it->second.m_aFile.getLength() )
889         {
890             // this printer already exists in a config file
891 
892 
893             // check writeability of config file(s)
894             if( ! checkWriteability( it->second.m_aFile ) )
895                 bSuccess = false;
896             else
897             {
898                 for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin();
899                 file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it )
900                 {
901                     if( ! checkWriteability( *file_it ) )
902                         bSuccess = false;
903                 }
904             }
905             if( bSuccess && ! bCheckOnly )
906             {
907 
908                 Config aConfig( it->second.m_aFile );
909                 aConfig.DeleteGroup( it->second.m_aGroup );
910                 aConfig.Flush();
911                 for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin();
912                 file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it )
913                 {
914                     Config aAltConfig( *file_it );
915                     aAltConfig.DeleteGroup( it->second.m_aGroup );
916                     aAltConfig.Flush();
917                 }
918             }
919         }
920         if( bSuccess && ! bCheckOnly )
921         {
922             m_aPrinters.erase( it );
923             // need this here because someone may call
924             // checkPrintersChanged after the removal
925             // but then other added printers were not flushed
926             // to disk, so they are discarded
927             writePrinterConfig();
928         }
929     }
930     return bSuccess;
931 }
932 
933 // -----------------------------------------------------------------
934 
setDefaultPrinter(const OUString & rPrinterName)935 bool PrinterInfoManager::setDefaultPrinter( const OUString& rPrinterName )
936 {
937     bool bSuccess = false;
938 
939     ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName );
940     if( it != m_aPrinters.end() )
941     {
942         bSuccess = true;
943         it->second.m_bModified = true;
944         if( ( it = m_aPrinters.find( m_aDefaultPrinter ) ) != m_aPrinters.end() )
945             it->second.m_bModified = true;
946         m_aDefaultPrinter = rPrinterName;
947         writePrinterConfig();
948     }
949     return bSuccess;
950 }
951 
952 // -----------------------------------------------------------------
addOrRemovePossible() const953 bool PrinterInfoManager::addOrRemovePossible() const
954 {
955     return true;
956 }
957 
958 // -----------------------------------------------------------------
959 
fillFontSubstitutions(PrinterInfo & rInfo) const960 void PrinterInfoManager::fillFontSubstitutions( PrinterInfo& rInfo ) const
961 {
962     PrintFontManager& rFontManager( PrintFontManager::get() );
963     rInfo.m_aFontSubstitutions.clear();
964 
965     if( ! rInfo.m_bPerformFontSubstitution ||
966         ! rInfo.m_aFontSubstitutes.size() )
967     return;
968 
969     ::std::list< FastPrintFontInfo > aFonts;
970     ::std::hash_map< OUString, ::std::list< FastPrintFontInfo >, OUStringHash > aPrinterFonts;
971     rFontManager.getFontListWithFastInfo( aFonts, rInfo.m_pParser );
972 
973     // get builtin fonts
974     ::std::list< FastPrintFontInfo >::const_iterator it;
975     for( it = aFonts.begin(); it != aFonts.end(); ++it )
976         if( it->m_eType == fonttype::Builtin )
977             aPrinterFonts[ it->m_aFamilyName.toAsciiLowerCase() ].push_back( *it );
978 
979     // map lower case, so build a local copy of the font substitutions
980     ::std::hash_map< OUString, OUString, OUStringHash > aSubstitutions;
981     ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst;
982     for( subst = rInfo.m_aFontSubstitutes.begin(); subst != rInfo.m_aFontSubstitutes.end(); ++subst )
983     {
984         OUString aFamily( subst->first.toAsciiLowerCase() );
985         // first look if there is a builtin of this family
986         // in this case override the substitution table
987         if( aPrinterFonts.find( aFamily ) != aPrinterFonts.end() )
988             aSubstitutions[ aFamily ] = aFamily;
989         else
990             aSubstitutions[ aFamily ] = subst->second.toAsciiLowerCase();
991     }
992 
993 
994     // now find substitutions
995     for( it = aFonts.begin(); it != aFonts.end(); ++it )
996     {
997         if( it->m_eType != fonttype::Builtin )
998         {
999             OUString aFamily( it->m_aFamilyName.toAsciiLowerCase() );
1000             subst = aSubstitutions.find( aFamily );
1001             if( subst != aSubstitutions.end() )
1002             {
1003                 // search a substitution
1004                 const ::std::list< FastPrintFontInfo >& rBuiltins( aPrinterFonts[ aSubstitutions[ aFamily ] ] );
1005                 ::std::list< FastPrintFontInfo >::const_iterator builtin;
1006                 int nLastMatch = -10000;
1007                 fontID nSubstitute = -1;
1008                 for( builtin = rBuiltins.begin(); builtin != rBuiltins.end(); ++builtin )
1009                 {
1010                     int nMatch = 0;
1011                     int nDiff;
1012                     if( builtin->m_eItalic == it->m_eItalic )
1013                         nMatch += 8000;
1014 
1015                     nDiff = builtin->m_eWeight - it->m_eWeight;
1016                     nDiff = nDiff < 0 ? -nDiff : nDiff;
1017                     nMatch += 4000 - 1000*nDiff;
1018 
1019                     nDiff = builtin->m_eWidth - it->m_eWidth;
1020                     nDiff = nDiff < 0 ? -nDiff : nDiff;
1021                     nMatch += 2000 - 500*nDiff;
1022 
1023                     if( nMatch > nLastMatch )
1024                     {
1025                         nLastMatch = nMatch;
1026                         nSubstitute = builtin->m_nID;
1027                     }
1028                 }
1029                 if( nSubstitute != -1 )
1030                 {
1031                     rInfo.m_aFontSubstitutions[ it->m_nID ] = nSubstitute;
1032                     #if OSL_DEBUG_LEVEL > 2
1033                     FastPrintFontInfo aInfo;
1034                     rFontManager.getFontFastInfo( nSubstitute, aInfo );
1035                     fprintf( stderr,
1036                     "substitute %s %s %d %d\n"
1037                     " ->        %s %s %d %d\n",
1038                     OUStringToOString( it->m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1039                     it->m_eItalic == italic::Upright ? "r" : it->m_eItalic == italic::Oblique ? "o" : it->m_eItalic == italic::Italic ? "i" : "u",
1040                     it->m_eWeight,
1041                     it->m_eWidth,
1042 
1043                     OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1044                     aInfo.m_eItalic == italic::Upright ? "r" : aInfo.m_eItalic == italic::Oblique ? "o" : aInfo.m_eItalic == italic::Italic ? "i" : "u",
1045                     aInfo.m_eWeight,
1046                     aInfo.m_eWidth
1047                     );
1048                     #endif
1049                 }
1050             }
1051         }
1052     }
1053 }
1054 
1055 // -----------------------------------------------------------------
1056 
getSystemPrintCommands(std::list<OUString> & rCommands)1057 void PrinterInfoManager::getSystemPrintCommands( std::list< OUString >& rCommands )
1058 {
1059     if( m_pQueueInfo && m_pQueueInfo->hasChanged() )
1060     {
1061         m_aSystemPrintCommand = m_pQueueInfo->getCommand();
1062         m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues );
1063         delete m_pQueueInfo, m_pQueueInfo = NULL;
1064     }
1065 
1066     std::list< SystemPrintQueue >::const_iterator it;
1067     rCommands.clear();
1068     String aPrinterConst( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) );
1069     for( it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it )
1070     {
1071         String aCmd( m_aSystemPrintCommand );
1072         aCmd.SearchAndReplace( aPrinterConst, it->m_aQueue );
1073         rCommands.push_back( aCmd );
1074     }
1075 }
1076 
getSystemPrintQueues()1077 const std::list< PrinterInfoManager::SystemPrintQueue >& PrinterInfoManager::getSystemPrintQueues()
1078 {
1079     if( m_pQueueInfo && m_pQueueInfo->hasChanged() )
1080     {
1081         m_aSystemPrintCommand = m_pQueueInfo->getCommand();
1082         m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues );
1083         delete m_pQueueInfo, m_pQueueInfo = NULL;
1084     }
1085 
1086     return m_aSystemPrintQueues;
1087 }
1088 
checkFeatureToken(const rtl::OUString & rPrinterName,const char * pToken) const1089 bool PrinterInfoManager::checkFeatureToken( const rtl::OUString& rPrinterName, const char* pToken ) const
1090 {
1091     const PrinterInfo& rPrinterInfo( getPrinterInfo( rPrinterName ) );
1092     sal_Int32 nIndex = 0;
1093     while( nIndex != -1 )
1094     {
1095         OUString aOuterToken = rPrinterInfo.m_aFeatures.getToken( 0, ',', nIndex );
1096         sal_Int32 nInnerIndex = 0;
1097         OUString aInnerToken = aOuterToken.getToken( 0, '=', nInnerIndex );
1098         if( aInnerToken.equalsIgnoreAsciiCaseAscii( pToken ) )
1099             return true;
1100     }
1101     return false;
1102 }
1103 
startSpool(const OUString & rPrintername,bool bQuickCommand)1104 FILE* PrinterInfoManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
1105 {
1106     const PrinterInfo&   rPrinterInfo   = getPrinterInfo (rPrintername);
1107     const rtl::OUString& rCommand       = (bQuickCommand && rPrinterInfo.m_aQuickCommand.getLength() ) ?
1108                                           rPrinterInfo.m_aQuickCommand : rPrinterInfo.m_aCommand;
1109     rtl::OString aShellCommand  = OUStringToOString (rCommand, RTL_TEXTENCODING_ISO_8859_1);
1110     aShellCommand += rtl::OString( " 2>/dev/null" );
1111 
1112     return popen (aShellCommand.getStr(), "w");
1113 }
1114 
endSpool(const OUString &,const OUString &,FILE * pFile,const JobData &,bool)1115 int PrinterInfoManager::endSpool( const OUString& /*rPrintername*/, const OUString& /*rJobTitle*/, FILE* pFile, const JobData& /*rDocumentJobData*/, bool /*bBanner*/ )
1116 {
1117     return (0 == pclose( pFile ));
1118 }
1119 
setupJobContextData(JobData & rData)1120 void PrinterInfoManager::setupJobContextData( JobData& rData )
1121 {
1122     std::hash_map< OUString, Printer, OUStringHash >::iterator it =
1123     m_aPrinters.find( rData.m_aPrinterName );
1124     if( it != m_aPrinters.end() )
1125     {
1126         rData.m_pParser     = it->second.m_aInfo.m_pParser;
1127         rData.m_aContext    = it->second.m_aInfo.m_aContext;
1128     }
1129 }
1130 
setDefaultPaper(PPDContext & rContext) const1131 void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const
1132 {
1133     if(  ! rContext.getParser() )
1134         return;
1135 
1136     const PPDKey* pPageSizeKey = rContext.getParser()->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
1137     if( ! pPageSizeKey )
1138         return;
1139 
1140     int nModified = rContext.countValuesModified();
1141     while( nModified-- &&
1142         rContext.getModifiedKey( nModified ) != pPageSizeKey )
1143     ;
1144 
1145     if( nModified >= 0 ) // paper was set already, do not modify
1146     {
1147         #if OSL_DEBUG_LEVEL > 1
1148         fprintf( stderr, "not setting default paper, already set %s\n",
1149         OUStringToOString( rContext.getValue( pPageSizeKey )->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1150         #endif
1151         return;
1152     }
1153 
1154     // paper not set, fill in default value
1155     const PPDValue* pPaperVal = NULL;
1156     int nValues = pPageSizeKey->countValues();
1157     for( int i = 0; i < nValues && ! pPaperVal; i++ )
1158     {
1159         const PPDValue* pVal = pPageSizeKey->getValue( i );
1160         if( pVal->m_aOption.EqualsIgnoreCaseAscii( m_aSystemDefaultPaper.getStr() ) )
1161             pPaperVal = pVal;
1162     }
1163     if( pPaperVal )
1164     {
1165         #if OSL_DEBUG_LEVEL > 1
1166         fprintf( stderr, "setting default paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1167         #endif
1168         rContext.setValue( pPageSizeKey, pPaperVal );
1169         #if OSL_DEBUG_LEVEL > 1
1170         pPaperVal = rContext.getValue( pPageSizeKey );
1171         fprintf( stderr, "-> got paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1172         #endif
1173     }
1174 }
1175 
1176 // -----------------------------------------------------------------
1177 
SystemQueueInfo()1178 SystemQueueInfo::SystemQueueInfo() :
1179     m_bChanged( false )
1180 {
1181     create();
1182 }
1183 
~SystemQueueInfo()1184 SystemQueueInfo::~SystemQueueInfo()
1185 {
1186     static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
1187     if( ! pNoSyncDetection || !*pNoSyncDetection )
1188         join();
1189     else
1190         terminate();
1191 }
1192 
hasChanged() const1193 bool SystemQueueInfo::hasChanged() const
1194 {
1195     MutexGuard aGuard( m_aMutex );
1196     bool bChanged = m_bChanged;
1197     return bChanged;
1198 }
1199 
getSystemQueues(std::list<PrinterInfoManager::SystemPrintQueue> & rQueues)1200 void SystemQueueInfo::getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues )
1201 {
1202     MutexGuard aGuard( m_aMutex );
1203     rQueues = m_aQueues;
1204     m_bChanged = false;
1205 }
1206 
getCommand() const1207 OUString SystemQueueInfo::getCommand() const
1208 {
1209     MutexGuard aGuard( m_aMutex );
1210     OUString aRet = m_aCommand;
1211     return aRet;
1212 }
1213 
1214 struct SystemCommandParameters;
1215 typedef void(* tokenHandler)(const std::list< rtl::OString >&,
1216                 std::list< PrinterInfoManager::SystemPrintQueue >&,
1217                 const SystemCommandParameters*);
1218 
1219 struct SystemCommandParameters
1220 {
1221     const char*     pQueueCommand;
1222     const char*     pPrintCommand;
1223     const char*     pForeToken;
1224     const char*     pAftToken;
1225     unsigned int    nForeTokenCount;
1226     tokenHandler    pHandler;
1227 };
1228 
1229 #if ! (defined(LINUX) || defined(NETBSD) || defined(FREEBSD))
lpgetSysQueueTokenHandler(const std::list<rtl::OString> & i_rLines,std::list<PrinterInfoManager::SystemPrintQueue> & o_rQueues,const SystemCommandParameters *)1230 static void lpgetSysQueueTokenHandler(
1231     const std::list< rtl::OString >& i_rLines,
1232     std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
1233     const SystemCommandParameters* )
1234 {
1235     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1236     std::hash_set< OUString, OUStringHash > aUniqueSet;
1237     std::hash_set< OUString, OUStringHash > aOnlySet;
1238     aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_all" ) ) );
1239     aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
1240 
1241     // the eventual "all" attribute of the "_all" queue tells us, which
1242     // printers are to be used for this user at all
1243 
1244     // find _all: line
1245     rtl::OString aAllLine( "_all:" );
1246     rtl::OString aAllAttr( "all=" );
1247     for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1248          it != i_rLines.end(); ++it )
1249     {
1250         if( it->indexOf( aAllLine, 0 ) == 0 )
1251         {
1252             // now find the "all" attribute
1253             ++it;
1254             while( it != i_rLines.end() )
1255             {
1256                 rtl::OString aClean( WhitespaceToSpace( *it ) );
1257                 if( aClean.indexOf( aAllAttr, 0 ) == 0 )
1258                 {
1259                     // insert the comma separated entries into the set of printers to use
1260                     sal_Int32 nPos = aAllAttr.getLength();
1261                     while( nPos != -1 )
1262                     {
1263                         OString aTok( aClean.getToken( 0, ',', nPos ) );
1264                         if( aTok.getLength() > 0 )
1265                             aOnlySet.insert( rtl::OStringToOUString( aTok, aEncoding ) );
1266                     }
1267                     break;
1268                 }
1269             }
1270             break;
1271         }
1272     }
1273 
1274     bool bInsertAttribute = false;
1275     rtl::OString aDescrStr( "description=" );
1276     rtl::OString aLocStr( "location=" );
1277     for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1278          it != i_rLines.end(); ++it )
1279     {
1280         sal_Int32 nPos = 0;
1281         // find the begin of a new printer section
1282         nPos = it->indexOf( ':', 0 );
1283         if( nPos != -1 )
1284         {
1285             OUString aSysQueue( rtl::OStringToOUString( it->copy( 0, nPos ), aEncoding ) );
1286             // do not insert duplicates (e.g. lpstat tends to produce such lines)
1287             // in case there was a "_all" section, insert only those printer explicitly
1288             // set in the "all" attribute
1289             if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() &&
1290                 ( aOnlySet.empty() || aOnlySet.find( aSysQueue ) != aOnlySet.end() )
1291                 )
1292             {
1293                 o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() );
1294                 o_rQueues.back().m_aQueue = aSysQueue;
1295                 o_rQueues.back().m_aLocation = aSysQueue;
1296                 aUniqueSet.insert( aSysQueue );
1297                 bInsertAttribute = true;
1298             }
1299             else
1300                 bInsertAttribute = false;
1301             continue;
1302         }
1303         if( bInsertAttribute && ! o_rQueues.empty() )
1304         {
1305             // look for "description" attribute, insert as comment
1306             nPos = it->indexOf( aDescrStr, 0 );
1307             if( nPos != -1 )
1308             {
1309                 ByteString aComment( WhitespaceToSpace( it->copy(nPos+12) ) );
1310                 if( aComment.Len() > 0 )
1311                     o_rQueues.back().m_aComment = String( aComment, aEncoding );
1312                 continue;
1313             }
1314             // look for "location" attribute, inser as location
1315             nPos = it->indexOf( aLocStr, 0 );
1316             if( nPos != -1 )
1317             {
1318                 ByteString aLoc( WhitespaceToSpace( it->copy(nPos+9) ) );
1319                 if( aLoc.Len() > 0 )
1320                     o_rQueues.back().m_aLocation = String( aLoc, aEncoding );
1321                 continue;
1322             }
1323         }
1324     }
1325 }
1326 #endif
standardSysQueueTokenHandler(const std::list<rtl::OString> & i_rLines,std::list<PrinterInfoManager::SystemPrintQueue> & o_rQueues,const SystemCommandParameters * i_pParms)1327 static void standardSysQueueTokenHandler(
1328     const std::list< rtl::OString >& i_rLines,
1329     std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
1330     const SystemCommandParameters* i_pParms)
1331 {
1332     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1333     std::hash_set< OUString, OUStringHash > aUniqueSet;
1334     rtl::OString aForeToken( i_pParms->pForeToken );
1335     rtl::OString aAftToken( i_pParms->pAftToken );
1336     /* Normal Unix print queue discovery, also used for Darwin 5 LPR printing
1337     */
1338     for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1339          it != i_rLines.end(); ++it )
1340     {
1341         sal_Int32 nPos = 0;
1342 
1343         // search for a line describing a printer:
1344         // find if there are enough tokens before the name
1345         for( unsigned int i = 0; i < i_pParms->nForeTokenCount && nPos != -1; i++ )
1346         {
1347             nPos = it->indexOf( aForeToken, nPos );
1348             if( nPos != -1 && it->getLength() >= nPos+aForeToken.getLength() )
1349                 nPos += aForeToken.getLength();
1350         }
1351         if( nPos != -1 )
1352         {
1353             // find if there is the token after the queue
1354             sal_Int32 nAftPos = it->indexOf( aAftToken, nPos );
1355             if( nAftPos != -1 )
1356             {
1357                 // get the queue name between fore and aft tokens
1358                 OUString aSysQueue( rtl::OStringToOUString( it->copy( nPos, nAftPos - nPos ), aEncoding ) );
1359                 // do not insert duplicates (e.g. lpstat tends to produce such lines)
1360                 if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() )
1361                 {
1362                     o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() );
1363                     o_rQueues.back().m_aQueue = aSysQueue;
1364                     o_rQueues.back().m_aLocation = aSysQueue;
1365                     aUniqueSet.insert( aSysQueue );
1366                 }
1367             }
1368         }
1369     }
1370 }
1371 
1372 static const struct SystemCommandParameters aParms[] =
1373 {
1374     #if defined(LINUX) || defined(NETBSD) || defined(FREEBSD)
1375     { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1376     { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1377     { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler }
1378     #else
1379     { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpget list", "lp -d \"(PRINTER)\"", "", ":", 0, lpgetSysQueueTokenHandler },
1380     { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler },
1381     { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1382     { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }
1383     #endif
1384 };
1385 
run()1386 void SystemQueueInfo::run()
1387 {
1388     char pBuffer[1024];
1389     FILE *pPipe;
1390     std::list< rtl::OString > aLines;
1391 
1392     /* Discover which command we can use to get a list of all printer queues */
1393     for( unsigned int i = 0; i < sizeof(aParms)/sizeof(aParms[0]); i++ )
1394     {
1395         aLines.clear();
1396         rtl::OStringBuffer aCmdLine( 128 );
1397         aCmdLine.append( aParms[i].pQueueCommand );
1398         #if OSL_DEBUG_LEVEL > 1
1399         fprintf( stderr, "trying print queue command \"%s\" ... ", aParms[i].pQueueCommand );
1400         #endif
1401         aCmdLine.append( " 2>/dev/null" );
1402         if( (pPipe = popen( aCmdLine.getStr(), "r" )) )
1403         {
1404             while( fgets( pBuffer, 1024, pPipe ) )
1405                 aLines.push_back( rtl::OString( pBuffer ) );
1406             if( ! pclose( pPipe ) )
1407             {
1408                 std::list< PrinterInfoManager::SystemPrintQueue > aSysPrintQueues;
1409                 aParms[i].pHandler( aLines, aSysPrintQueues, &(aParms[i]) );
1410                 MutexGuard aGuard( m_aMutex );
1411                 m_bChanged  = true;
1412                 m_aQueues   = aSysPrintQueues;
1413                 m_aCommand  = rtl::OUString::createFromAscii( aParms[i].pPrintCommand );
1414                 #if OSL_DEBUG_LEVEL > 1
1415                 fprintf( stderr, "success\n" );
1416                 #endif
1417                 break;
1418             }
1419         }
1420         #if OSL_DEBUG_LEVEL > 1
1421         fprintf( stderr, "failed\n" );
1422         #endif
1423     }
1424 }
1425 
1426