1/*
2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.print;
27
28import java.io.BufferedReader;
29import java.io.FileInputStream;
30import java.io.InputStream;
31import java.io.InputStreamReader;
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.Vector;
35import java.security.AccessController;
36import java.security.PrivilegedActionException;
37import java.security.PrivilegedExceptionAction;
38import javax.print.DocFlavor;
39import javax.print.MultiDocPrintService;
40import javax.print.PrintService;
41import javax.print.PrintServiceLookup;
42import javax.print.attribute.Attribute;
43import javax.print.attribute.AttributeSet;
44import javax.print.attribute.HashPrintRequestAttributeSet;
45import javax.print.attribute.HashPrintServiceAttributeSet;
46import javax.print.attribute.PrintRequestAttribute;
47import javax.print.attribute.PrintRequestAttributeSet;
48import javax.print.attribute.PrintServiceAttribute;
49import javax.print.attribute.PrintServiceAttributeSet;
50import javax.print.attribute.standard.PrinterName;
51import javax.print.attribute.standard.PrinterURI;
52import java.io.File;
53import java.io.FileReader;
54import java.net.URL;
55import java.nio.file.Files;
56
57/*
58 * Remind: This class uses solaris commands. We also need a linux
59 * version
60 */
61public class PrintServiceLookupProvider extends PrintServiceLookup
62    implements BackgroundServiceLookup, Runnable {
63
64    /* Remind: the current implementation is static, as its assumed
65     * its preferable to minimize creation of PrintService instances.
66     * Later we should add logic to add/remove services on the fly which
67     * will take a hit of needing to regather the list of services.
68     */
69    private String defaultPrinter;
70    private PrintService defaultPrintService;
71    private PrintService[] printServices; /* includes the default printer */
72    private Vector<BackgroundLookupListener> lookupListeners = null;
73    private static String debugPrefix = "PrintServiceLookupProvider>> ";
74    private static boolean pollServices = true;
75    private static final int DEFAULT_MINREFRESH = 120;  // 2 minutes
76    private static int minRefreshTime = DEFAULT_MINREFRESH;
77
78
79    static String osname;
80
81    // List of commands used to deal with the printer queues on AIX
82    String[] lpNameComAix = {
83      "/usr/bin/lsallq",
84      "/usr/bin/lpstat -W -p|/usr/bin/expand|/usr/bin/cut -f1 -d' '",
85      "/usr/bin/lpstat -W -d|/usr/bin/expand|/usr/bin/cut -f1 -d' '",
86      "/usr/bin/lpstat -W -v"
87    };
88    private static final int aix_lsallq = 0;
89    private static final int aix_lpstat_p = 1;
90    private static final int aix_lpstat_d = 2;
91    private static final int aix_lpstat_v = 3;
92    private static int aix_defaultPrinterEnumeration = aix_lsallq;
93
94    static {
95        /* The system property "sun.java2d.print.polling"
96         * can be used to force the printing code to poll or not poll
97         * for PrintServices.
98         */
99        String pollStr = java.security.AccessController.doPrivileged(
100            new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
101
102        if (pollStr != null) {
103            if (pollStr.equalsIgnoreCase("true")) {
104                pollServices = true;
105            } else if (pollStr.equalsIgnoreCase("false")) {
106                pollServices = false;
107            }
108        }
109
110        /* The system property "sun.java2d.print.minRefreshTime"
111         * can be used to specify minimum refresh time (in seconds)
112         * for polling PrintServices.  The default is 120.
113         */
114        String refreshTimeStr = java.security.AccessController.doPrivileged(
115            new sun.security.action.GetPropertyAction(
116                "sun.java2d.print.minRefreshTime"));
117
118        if (refreshTimeStr != null) {
119            try {
120                minRefreshTime = (Integer.valueOf(refreshTimeStr)).intValue();
121            } catch (NumberFormatException e) {
122            }
123            if (minRefreshTime < DEFAULT_MINREFRESH) {
124                minRefreshTime = DEFAULT_MINREFRESH;
125            }
126        }
127
128        osname = java.security.AccessController.doPrivileged(
129            new sun.security.action.GetPropertyAction("os.name"));
130
131        /* The system property "sun.java2d.print.aix.lpstat"
132         * can be used to force the usage of 'lpstat -p' to enumerate all
133         * printer queues. By default we use 'lsallq', because 'lpstat -p' can
134         * take lots of time if thousands of printers are attached to a server.
135         */
136        if (isAIX()) {
137            String aixPrinterEnumerator = java.security.AccessController.doPrivileged(
138                new sun.security.action.GetPropertyAction("sun.java2d.print.aix.lpstat"));
139
140            if (aixPrinterEnumerator != null) {
141                if (aixPrinterEnumerator.equalsIgnoreCase("lpstat")) {
142                    aix_defaultPrinterEnumeration = aix_lpstat_p;
143                } else if (aixPrinterEnumerator.equalsIgnoreCase("lsallq")) {
144                    aix_defaultPrinterEnumeration = aix_lsallq;
145                }
146            }
147        }
148    }
149
150    static boolean isMac() {
151        return osname.startsWith("Mac");
152    }
153
154    static boolean isSysV() {
155        return osname.equals("SunOS");
156    }
157
158    static boolean isLinux() {
159        return (osname.equals("Linux"));
160    }
161
162    static boolean isBSD() {
163        return (osname.equals("Linux") ||
164                osname.contains("OS X"));
165    }
166
167    static boolean isAIX() {
168        return osname.equals("AIX");
169    }
170
171    static final int UNINITIALIZED = -1;
172    static final int BSD_LPD = 0;
173    static final int BSD_LPD_NG = 1;
174
175    static int cmdIndex = UNINITIALIZED;
176
177    String[] lpcFirstCom = {
178        "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'",
179        "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
180    };
181
182    String[] lpcAllCom = {
183        "/usr/sbin/lpc status all | grep : | sed -e 's/://'",
184        "/usr/sbin/lpc status all | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort"
185    };
186
187    String[] lpcNameCom = {
188        "| grep : | sed -ne 's/://p'",
189        "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
190    };
191
192
193    static int getBSDCommandIndex() {
194        String command  = "/usr/sbin/lpc status all";
195        String[] names = execCmd(command);
196
197        if ((names == null) || (names.length == 0)) {
198            return BSD_LPD_NG;
199        }
200
201        for (int i=0; i<names.length; i++) {
202            if (names[i].indexOf('@') != -1) {
203                return BSD_LPD_NG;
204            }
205        }
206
207        return BSD_LPD;
208    }
209
210
211    public PrintServiceLookupProvider() {
212        // start the printer listener thread
213        if (pollServices) {
214            Thread thr = new Thread(null, new PrinterChangeListener(),
215                                    "PrinterListener", 0, false);
216            thr.setDaemon(true);
217            thr.start();
218            IPPPrintService.debug_println(debugPrefix+"polling turned on");
219        }
220    }
221
222    /* Want the PrintService which is default print service to have
223     * equality of reference with the equivalent in list of print services
224     * This isn't required by the API and there's a risk doing this will
225     * lead people to assume its guaranteed.
226     */
227    public synchronized PrintService[] getPrintServices() {
228        SecurityManager security = System.getSecurityManager();
229        if (security != null) {
230            security.checkPrintJobAccess();
231        }
232
233        if (printServices == null || !pollServices) {
234            refreshServices();
235        }
236        if (printServices == null) {
237            return new PrintService[0];
238        } else {
239            return printServices.clone();
240        }
241    }
242
243    private int addPrintServiceToList(ArrayList<PrintService> printerList, PrintService ps) {
244        int index = printerList.indexOf(ps);
245        // Check if PrintService with same name is already in the list.
246        if (CUPSPrinter.isCupsRunning() && index != -1) {
247            // Bug in Linux: Duplicate entry of a remote printer
248            // and treats it as local printer but it is returning wrong
249            // information when queried using IPP. Workaround is to remove it.
250            // Even CUPS ignores these entries as shown in lpstat or using
251            // their web configuration.
252            PrinterURI uri = ps.getAttribute(PrinterURI.class);
253            if (uri.getURI().getHost().equals("localhost")) {
254                IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, ignoring the new local printer: "+ps);
255                return index;  // Do not add this.
256            }
257            PrintService oldPS = printerList.get(index);
258            uri = oldPS.getAttribute(PrinterURI.class);
259            if (uri.getURI().getHost().equals("localhost")) {
260                IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, removing existing local printer: "+oldPS);
261                printerList.remove(oldPS);
262            } else {
263                return index;
264            }
265        }
266        printerList.add(ps);
267        return (printerList.size() - 1);
268    }
269
270
271    // refreshes "printServices"
272    public synchronized void refreshServices() {
273        /* excludes the default printer */
274        String[] printers = null; // array of printer names
275        String[] printerURIs = null; //array of printer URIs
276
277        try {
278            getDefaultPrintService();
279        } catch (Throwable t) {
280            IPPPrintService.debug_println(debugPrefix+
281              "Exception getting default printer : " + t);
282        }
283        if (CUPSPrinter.isCupsRunning()) {
284            try {
285                printerURIs = CUPSPrinter.getAllPrinters();
286                IPPPrintService.debug_println("CUPS URIs = " + printerURIs);
287                if (printerURIs != null) {
288                    for (int p = 0; p < printerURIs.length; p++) {
289                       IPPPrintService.debug_println("URI="+printerURIs[p]);
290                    }
291                }
292            } catch (Throwable t) {
293            IPPPrintService.debug_println(debugPrefix+
294              "Exception getting all CUPS printers : " + t);
295            }
296            if ((printerURIs != null) && (printerURIs.length > 0)) {
297                printers = new String[printerURIs.length];
298                for (int i=0; i<printerURIs.length; i++) {
299                    int lastIndex = printerURIs[i].lastIndexOf("/");
300                    printers[i] = printerURIs[i].substring(lastIndex+1);
301                }
302            }
303        } else {
304            if (isMac() || isSysV()) {
305                printers = getAllPrinterNamesSysV();
306            } else if (isAIX()) {
307                printers = getAllPrinterNamesAIX();
308            } else { //BSD
309                printers = getAllPrinterNamesBSD();
310            }
311        }
312
313        if (printers == null) {
314            if (defaultPrintService != null) {
315                printServices = new PrintService[1];
316                printServices[0] = defaultPrintService;
317            } else {
318                printServices = null;
319            }
320            return;
321        }
322
323        ArrayList<PrintService> printerList = new ArrayList<>();
324        int defaultIndex = -1;
325        for (int p=0; p<printers.length; p++) {
326            if (printers[p] == null) {
327                continue;
328            }
329            if ((defaultPrintService != null)
330                && printers[p].equals(getPrinterDestName(defaultPrintService))) {
331                defaultIndex = addPrintServiceToList(printerList, defaultPrintService);
332            } else {
333                if (printServices == null) {
334                    IPPPrintService.debug_println(debugPrefix+
335                                                  "total# of printers = "+printers.length);
336
337                    if (CUPSPrinter.isCupsRunning()) {
338                        try {
339                            addPrintServiceToList(printerList,
340                                                  new IPPPrintService(printers[p],
341                                                                   printerURIs[p],
342                                                                   true));
343                        } catch (Exception e) {
344                            IPPPrintService.debug_println(debugPrefix+
345                                                          " getAllPrinters Exception "+
346                                                          e);
347
348                        }
349                    } else {
350                        printerList.add(new UnixPrintService(printers[p]));
351                    }
352                } else {
353                    int j;
354                    for (j=0; j<printServices.length; j++) {
355                        if (printServices[j] != null) {
356                            if (printers[p].equals(getPrinterDestName(printServices[j]))) {
357                                printerList.add(printServices[j]);
358                                printServices[j] = null;
359                                break;
360                            }
361                        }
362                    }
363
364                    if (j == printServices.length) {      // not found?
365                        if (CUPSPrinter.isCupsRunning()) {
366                            try {
367                                addPrintServiceToList(printerList,
368                                             new IPPPrintService(printers[p],
369                                                                 printerURIs[p],
370                                                                 true));
371                            } catch (Exception e) {
372                                IPPPrintService.debug_println(debugPrefix+
373                                                              " getAllPrinters Exception "+
374                                                              e);
375
376                            }
377                        } else {
378                            printerList.add(new UnixPrintService(printers[p]));
379                        }
380                    }
381                }
382            }
383        }
384
385        // Look for deleted services and invalidate these
386        if (printServices != null) {
387            for (int j=0; j < printServices.length; j++) {
388                if ((printServices[j] instanceof UnixPrintService) &&
389                    (!printServices[j].equals(defaultPrintService))) {
390                    ((UnixPrintService)printServices[j]).invalidateService();
391                }
392            }
393        }
394
395        //if defaultService is not found in printerList
396        if (defaultIndex == -1 && defaultPrintService != null) {
397            defaultIndex = addPrintServiceToList(printerList, defaultPrintService);
398        }
399
400        printServices = printerList.toArray(new PrintService[] {});
401
402        // swap default with the first in the list
403        if (defaultIndex > 0) {
404            PrintService saveService = printServices[0];
405            printServices[0] = printServices[defaultIndex];
406            printServices[defaultIndex] = saveService;
407        }
408    }
409
410    private boolean matchesAttributes(PrintService service,
411                                      PrintServiceAttributeSet attributes) {
412
413        Attribute [] attrs =  attributes.toArray();
414        for (int i=0; i<attrs.length; i++) {
415            @SuppressWarnings("unchecked")
416            Attribute serviceAttr
417                = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
418            if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
419                return false;
420            }
421        }
422        return true;
423    }
424
425      /* This checks for validity of the printer name before passing as
426       * parameter to a shell command.
427       */
428      private boolean checkPrinterName(String s) {
429        char c;
430
431        for (int i=0; i < s.length(); i++) {
432          c = s.charAt(i);
433          if (Character.isLetterOrDigit(c) ||
434              c == '-' || c == '_' || c == '.' || c == '/') {
435            continue;
436          } else {
437            return false;
438          }
439        }
440        return true;
441      }
442
443    /*
444     * Gets the printer name compatible with the list of printers returned by
445     * the system when we query default or all the available printers.
446     */
447    private String getPrinterDestName(PrintService ps) {
448        if (isMac()) {
449            return ((IPPPrintService)ps).getDest();
450        }
451        return ps.getName();
452    }
453
454    /* On a network with many (hundreds) of network printers, it
455     * can save several seconds if you know all you want is a particular
456     * printer, to ask for that printer rather than retrieving all printers.
457     */
458    private PrintService getServiceByName(PrinterName nameAttr) {
459        String name = nameAttr.getValue();
460        if (name == null || name.equals("") || !checkPrinterName(name)) {
461            return null;
462        }
463        /* check if all printers are already available */
464        if (printServices != null) {
465            for (PrintService printService : printServices) {
466                PrinterName printerName = printService.getAttribute(PrinterName.class);
467                if (printerName.getValue().equals(name)) {
468                    return printService;
469                }
470            }
471        }
472        /* take CUPS into account first */
473        if (CUPSPrinter.isCupsRunning()) {
474            try {
475                return new IPPPrintService(name,
476                                           new URL("http://"+
477                                                   CUPSPrinter.getServer()+":"+
478                                                   CUPSPrinter.getPort()+"/"+
479                                                   name));
480            } catch (Exception e) {
481                IPPPrintService.debug_println(debugPrefix+
482                                              " getServiceByName Exception "+
483                                              e);
484            }
485        }
486        /* fallback if nothing not having a printer at this point */
487        PrintService printer = null;
488        if (isMac() || isSysV()) {
489            printer = getNamedPrinterNameSysV(name);
490        } else if (isAIX()) {
491            printer = getNamedPrinterNameAIX(name);
492        } else {
493            printer = getNamedPrinterNameBSD(name);
494        }
495        return printer;
496    }
497
498    private PrintService[]
499        getPrintServices(PrintServiceAttributeSet serviceSet) {
500
501        if (serviceSet == null || serviceSet.isEmpty()) {
502            return getPrintServices();
503        }
504
505        /* Typically expect that if a service attribute is specified that
506         * its a printer name and there ought to be only one match.
507         * Directly retrieve that service and confirm
508         * that it meets the other requirements.
509         * If printer name isn't mentioned then go a slow path checking
510         * all printers if they meet the reqiremements.
511         */
512        PrintService[] services;
513        PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
514        PrintService defService;
515        if (name != null && (defService = getDefaultPrintService()) != null) {
516            /* To avoid execing a unix command  see if the client is asking
517             * for the default printer by name, since we already have that
518             * initialised.
519             */
520
521            PrinterName defName = defService.getAttribute(PrinterName.class);
522
523            if (defName != null && name.equals(defName)) {
524                if (matchesAttributes(defService, serviceSet)) {
525                    services = new PrintService[1];
526                    services[0] = defService;
527                    return services;
528                } else {
529                    return new PrintService[0];
530                }
531            } else {
532                /* Its not the default service */
533                PrintService service = getServiceByName(name);
534                if (service != null &&
535                    matchesAttributes(service, serviceSet)) {
536                    services = new PrintService[1];
537                    services[0] = service;
538                    return services;
539                } else {
540                    return new PrintService[0];
541                }
542            }
543        } else {
544            /* specified service attributes don't include a name.*/
545            Vector<PrintService> matchedServices = new Vector<>();
546            services = getPrintServices();
547            for (int i = 0; i< services.length; i++) {
548                if (matchesAttributes(services[i], serviceSet)) {
549                    matchedServices.add(services[i]);
550                }
551            }
552            services = new PrintService[matchedServices.size()];
553            for (int i = 0; i< services.length; i++) {
554                services[i] = matchedServices.elementAt(i);
555            }
556            return services;
557        }
558    }
559
560    /*
561     * If service attributes are specified then there must be additional
562     * filtering.
563     */
564    public PrintService[] getPrintServices(DocFlavor flavor,
565                                           AttributeSet attributes) {
566        SecurityManager security = System.getSecurityManager();
567        if (security != null) {
568          security.checkPrintJobAccess();
569        }
570        PrintRequestAttributeSet requestSet = null;
571        PrintServiceAttributeSet serviceSet = null;
572
573        if (attributes != null && !attributes.isEmpty()) {
574
575            requestSet = new HashPrintRequestAttributeSet();
576            serviceSet = new HashPrintServiceAttributeSet();
577
578            Attribute[] attrs = attributes.toArray();
579            for (int i=0; i<attrs.length; i++) {
580                if (attrs[i] instanceof PrintRequestAttribute) {
581                    requestSet.add(attrs[i]);
582                } else if (attrs[i] instanceof PrintServiceAttribute) {
583                    serviceSet.add(attrs[i]);
584                }
585            }
586        }
587
588        PrintService[] services = getPrintServices(serviceSet);
589        if (services.length == 0) {
590            return services;
591        }
592
593        if (CUPSPrinter.isCupsRunning()) {
594            ArrayList<PrintService> matchingServices = new ArrayList<>();
595            for (int i=0; i<services.length; i++) {
596                try {
597                    if (services[i].
598                        getUnsupportedAttributes(flavor, requestSet) == null) {
599                        matchingServices.add(services[i]);
600                    }
601                } catch (IllegalArgumentException e) {
602                }
603            }
604            services = new PrintService[matchingServices.size()];
605            return matchingServices.toArray(services);
606
607        } else {
608            // We only need to compare 1 PrintService because all
609            // UnixPrintServices are the same anyway.  We will not use
610            // default PrintService because it might be null.
611            PrintService service = services[0];
612            if ((flavor == null ||
613                 service.isDocFlavorSupported(flavor)) &&
614                 service.getUnsupportedAttributes(flavor, requestSet) == null)
615            {
616                return services;
617            } else {
618                return new PrintService[0];
619            }
620        }
621    }
622
623    /*
624     * return empty array as don't support multi docs
625     */
626    public MultiDocPrintService[]
627        getMultiDocPrintServices(DocFlavor[] flavors,
628                                 AttributeSet attributes) {
629        SecurityManager security = System.getSecurityManager();
630        if (security != null) {
631          security.checkPrintJobAccess();
632        }
633        return new MultiDocPrintService[0];
634    }
635
636
637    public synchronized PrintService getDefaultPrintService() {
638        SecurityManager security = System.getSecurityManager();
639        if (security != null) {
640          security.checkPrintJobAccess();
641        }
642
643        // clear defaultPrintService
644        defaultPrintService = null;
645        String psuri = null;
646
647        IPPPrintService.debug_println("isRunning ? "+
648                                      (CUPSPrinter.isCupsRunning()));
649        if (CUPSPrinter.isCupsRunning()) {
650            String[] printerInfo = CUPSPrinter.getDefaultPrinter();
651            if (printerInfo != null && printerInfo.length >= 2) {
652                defaultPrinter = printerInfo[0];
653                psuri = printerInfo[1];
654            }
655        } else {
656            if (isMac() || isSysV()) {
657                defaultPrinter = getDefaultPrinterNameSysV();
658            } else if (isAIX()) {
659                defaultPrinter = getDefaultPrinterNameAIX();
660            } else {
661                defaultPrinter = getDefaultPrinterNameBSD();
662            }
663        }
664        if (defaultPrinter == null) {
665            return null;
666        }
667        defaultPrintService = null;
668        if (printServices != null) {
669            for (int j=0; j<printServices.length; j++) {
670                if (defaultPrinter.equals(getPrinterDestName(printServices[j]))) {
671                    defaultPrintService = printServices[j];
672                    break;
673                }
674            }
675        }
676        if (defaultPrintService == null) {
677            if (CUPSPrinter.isCupsRunning()) {
678                try {
679                    PrintService defaultPS;
680                    if ((psuri != null) && !psuri.startsWith("file")) {
681                        defaultPS = new IPPPrintService(defaultPrinter,
682                                                        psuri, true);
683                    } else {
684                        defaultPS = new IPPPrintService(defaultPrinter,
685                                            new URL("http://"+
686                                                    CUPSPrinter.getServer()+":"+
687                                                    CUPSPrinter.getPort()+"/"+
688                                                    defaultPrinter));
689                    }
690                    defaultPrintService = defaultPS;
691                } catch (Exception e) {
692                }
693            } else {
694                defaultPrintService = new UnixPrintService(defaultPrinter);
695            }
696        }
697
698        return defaultPrintService;
699    }
700
701    public synchronized void
702        getServicesInbackground(BackgroundLookupListener listener) {
703        if (printServices != null) {
704            listener.notifyServices(printServices);
705        } else {
706            if (lookupListeners == null) {
707                lookupListeners = new Vector<>();
708                lookupListeners.add(listener);
709                Thread lookupThread = new Thread(this);
710                lookupThread.start();
711            } else {
712                lookupListeners.add(listener);
713            }
714        }
715    }
716
717    /* This method isn't used in most cases because we rely on code in
718     * javax.print.PrintServiceLookup. This is needed just for the cases
719     * where those interfaces are by-passed.
720     */
721    private PrintService[] copyOf(PrintService[] inArr) {
722        if (inArr == null || inArr.length == 0) {
723            return inArr;
724        } else {
725            PrintService []outArr = new PrintService[inArr.length];
726            System.arraycopy(inArr, 0, outArr, 0, inArr.length);
727            return outArr;
728        }
729    }
730
731    public void run() {
732        PrintService[] services = getPrintServices();
733        synchronized (this) {
734            BackgroundLookupListener listener;
735            for (int i=0; i<lookupListeners.size(); i++) {
736                listener = lookupListeners.elementAt(i);
737                listener.notifyServices(copyOf(services));
738            }
739            lookupListeners = null;
740        }
741    }
742
743    private String getDefaultPrinterNameBSD() {
744        if (cmdIndex == UNINITIALIZED) {
745            cmdIndex = getBSDCommandIndex();
746        }
747        String[] names = execCmd(lpcFirstCom[cmdIndex]);
748        if (names == null || names.length == 0) {
749            return null;
750        }
751
752        if ((cmdIndex==BSD_LPD_NG) &&
753            (names[0].startsWith("missingprinter"))) {
754            return null;
755        }
756        return names[0];
757    }
758
759    private PrintService getNamedPrinterNameBSD(String name) {
760      if (cmdIndex == UNINITIALIZED) {
761        cmdIndex = getBSDCommandIndex();
762      }
763      String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex];
764      String[] result = execCmd(command);
765
766      if (result == null || !(result[0].equals(name))) {
767          return null;
768      }
769      return new UnixPrintService(name);
770    }
771
772    private String[] getAllPrinterNamesBSD() {
773        if (cmdIndex == UNINITIALIZED) {
774            cmdIndex = getBSDCommandIndex();
775        }
776        String[] names = execCmd(lpcAllCom[cmdIndex]);
777        if (names == null || names.length == 0) {
778          return null;
779        }
780        return names;
781    }
782
783    static String getDefaultPrinterNameSysV() {
784        String defaultPrinter = "lp";
785        String command = "/usr/bin/lpstat -d";
786
787        String [] names = execCmd(command);
788        if (names == null || names.length == 0) {
789            return defaultPrinter;
790        } else {
791            int index = names[0].indexOf(":");
792            if (index == -1  || (names[0].length() <= index+1)) {
793                return null;
794            } else {
795                String name = names[0].substring(index+1).trim();
796                if (name.length() == 0) {
797                    return null;
798                } else {
799                    return name;
800                }
801            }
802        }
803    }
804
805    private PrintService getNamedPrinterNameSysV(String name) {
806
807        String command = "/usr/bin/lpstat -v " + name;
808        String []result = execCmd(command);
809
810        if (result == null || result[0].indexOf("unknown printer") > 0) {
811            return null;
812        } else {
813            return new UnixPrintService(name);
814        }
815    }
816
817    private String[] getAllPrinterNamesSysV() {
818        String defaultPrinter = "lp";
819        String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':' | /usr/bin/sort";
820
821        String [] names = execCmd(command);
822        ArrayList<String> printerNames = new ArrayList<>();
823        for (int i=0; i < names.length; i++) {
824            if (!names[i].equals("_default") &&
825                !names[i].equals(defaultPrinter) &&
826                !names[i].equals("")) {
827                printerNames.add(names[i]);
828            }
829        }
830        return printerNames.toArray(new String[printerNames.size()]);
831    }
832
833    private String getDefaultPrinterNameAIX() {
834        String[] names = execCmd(lpNameComAix[aix_lpstat_d]);
835        // Remove headers and bogus entries added by remote printers.
836        names = UnixPrintService.filterPrinterNamesAIX(names);
837        if (names == null || names.length != 1) {
838            // No default printer found
839            return null;
840        } else {
841            return names[0];
842        }
843    }
844
845    private PrintService getNamedPrinterNameAIX(String name) {
846        // On AIX there should be no blank after '-v'.
847        String[] result = execCmd(lpNameComAix[aix_lpstat_v] + name);
848        // Remove headers and bogus entries added by remote printers.
849        result = UnixPrintService.filterPrinterNamesAIX(result);
850        if (result == null || result.length != 1) {
851            return null;
852        } else {
853            return new UnixPrintService(name);
854        }
855    }
856
857    private String[] getAllPrinterNamesAIX() {
858        // Determine all printers of the system.
859        String [] names = execCmd(lpNameComAix[aix_defaultPrinterEnumeration]);
860
861        // Remove headers and bogus entries added by remote printers.
862        names = UnixPrintService.filterPrinterNamesAIX(names);
863
864        ArrayList<String> printerNames = new ArrayList<String>();
865        for ( int i=0; i < names.length; i++) {
866            printerNames.add(names[i]);
867        }
868        return printerNames.toArray(new String[printerNames.size()]);
869    }
870
871    static String[] execCmd(final String command) {
872        ArrayList<String> results = null;
873        try {
874            final String[] cmd = new String[3];
875            if (isSysV() || isAIX()) {
876                cmd[0] = "/usr/bin/sh";
877                cmd[1] = "-c";
878                cmd[2] = "env LC_ALL=C " + command;
879            } else {
880                cmd[0] = "/bin/sh";
881                cmd[1] = "-c";
882                cmd[2] = "LC_ALL=C " + command;
883            }
884
885            results = AccessController.doPrivileged(
886                new PrivilegedExceptionAction<ArrayList<String>>() {
887                    public ArrayList<String> run() throws IOException {
888
889                        Process proc;
890                        BufferedReader bufferedReader = null;
891                        File f = Files.createTempFile("prn","xc").toFile();
892                        cmd[2] = cmd[2]+">"+f.getAbsolutePath();
893
894                        proc = Runtime.getRuntime().exec(cmd);
895                        try {
896                            boolean done = false; // in case of interrupt.
897                            while (!done) {
898                                try {
899                                    proc.waitFor();
900                                    done = true;
901                                } catch (InterruptedException e) {
902                                }
903                            }
904
905                            if (proc.exitValue() == 0) {
906                                FileReader reader = new FileReader(f);
907                                bufferedReader = new BufferedReader(reader);
908                                String line;
909                                ArrayList<String> results = new ArrayList<>();
910                                while ((line = bufferedReader.readLine())
911                                       != null) {
912                                    results.add(line);
913                                }
914                                return results;
915                            }
916                        } finally {
917                            f.delete();
918                            // promptly close all streams.
919                            if (bufferedReader != null) {
920                                bufferedReader.close();
921                            }
922                            proc.getInputStream().close();
923                            proc.getErrorStream().close();
924                            proc.getOutputStream().close();
925                        }
926                        return null;
927                    }
928                });
929        } catch (PrivilegedActionException e) {
930        }
931        if (results == null) {
932            return new String[0];
933        } else {
934            return results.toArray(new String[results.size()]);
935        }
936    }
937
938    private class PrinterChangeListener implements Runnable {
939
940        @Override
941        public void run() {
942            int refreshSecs;
943            while (true) {
944                try {
945                    refreshServices();
946                } catch (Exception se) {
947                    IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread.");
948                    break;
949                }
950
951                if ((printServices != null) &&
952                    (printServices.length > minRefreshTime)) {
953                    // compute new refresh time 1 printer = 1 sec
954                    refreshSecs = printServices.length;
955                } else {
956                    refreshSecs = minRefreshTime;
957                }
958                try {
959                    Thread.sleep(refreshSecs * 1000);
960                } catch (InterruptedException e) {
961                    break;
962                }
963            }
964        }
965    }
966}
967