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