1#!/usr/bin/python
2
3import re
4import comfychair, stf
5from samba import spoolss
6
7class PrintServerTest(comfychair.TestCase):
8    """An abstract class requiring a print server."""
9    def setUp(self):
10        # TODO: create a test printer
11        self.server = stf.get_server(platform = "nt")
12        self.require(self.server != None, "print server required")
13        # TODO: remove hardcoded printer name
14        self.printername = "p"
15        self.uncname = "\\\\%s\\%s" % \
16                           (self.server["hostname"], self.printername)
17
18class W2kPrintServerTest(comfychair.TestCase):
19    """An abstract class requiring a print server."""
20    def setUp(self):
21        # TODO: create a test printer
22        self.server = stf.get_server(platform = "nt5")
23        self.require(self.server != None, "print server required")
24        # TODO: remove hardcoded printer name
25        self.printername = "p"
26        self.uncname = "\\\\%s\\%s" % \
27                           (self.server["hostname"], self.printername)
28
29class CredentialTest(PrintServerTest):
30    """An class that calls a function with various sets of credentials."""
31    def runTest(self):
32
33        bad_user_creds = {"username": "spotty",
34                          "domain": "dog",
35                          "password": "bone"}
36
37        cases = ((self.server["administrator"], "Admin credentials", 1),
38                 (bad_user_creds,               "Bad credentials",   0))
39
40        # TODO: add unpriv user case
41
42        for creds, testname, result in cases:
43            try:
44                self.runTestArg(creds)
45            except:
46                if result:
47                    import traceback
48                    traceback.print_exc()
49                    self.fail("rpc with creds %s failed when it "
50                              "should have suceeded" % creds)
51                return
52
53            if not result:
54                self.fail("rpc with creds %s suceeded when it should "
55                          "have failed" % creds)
56
57class ArgTestServer(PrintServerTest):
58    """Test a RPC that takes a UNC print server name."""
59    def runTest(self):
60
61        # List of test cases, %s substituted for server name
62
63        cases = (("",             "No server name",          0),
64                 ("\\\\%s",       "Valid server name",       1),
65                 ("\\%s",         "Invalid unc server name", 0),
66                 ("\\\\%s__",     "Invalid unc server name", 0))
67
68        for unc, testname, result in cases:
69            unc = re.sub("%s", self.server["hostname"], unc)
70            try:
71                self.runTestArg(unc)
72            except:
73                if result:
74                    self.fail("rpc(\"%s\") failed when it should have "
75                              "suceeded" % unc)
76                return
77
78            if not result:
79                # Suceeded when we should have failed
80                self.fail("rpc(\"%s\") suceeded when it should have "
81                          "failed" % unc)
82
83class ArgTestServerAndPrinter(ArgTestServer):
84    """Test a RPC that takes a UNC print server or UNC printer name."""
85    def runTest(self):
86
87        ArgTestServer.runTest(self)
88
89        # List of test cases, %s substituted for server name, %p substituted
90        # for printer name.
91
92        cases = (("\\\\%s\\%p",   "Valid server and printer name",      1),
93                 ("\\\\%s\\%p__", "Valid server, invalid printer name", 0),
94                 ("\\\\%s__\\%p", "Invalid server, valid printer name", 0))
95
96        for unc, testname, result in cases:
97            unc = re.sub("%s", self.server["hostname"], unc)
98            unc = re.sub("%p", self.printername, unc)
99            try:
100                self.runTestArg(unc)
101            except:
102                if result:
103                    self.fail("openprinter(\"%s\") failed when it should have "
104                              "suceeded" % unc)
105                return
106
107            if not result:
108                # Suceeded when we should have failed
109                self.fail("openprinter(\"%s\") suceeded when it should have "
110                          "failed" % unc)
111
112class OpenPrinterArg(ArgTestServerAndPrinter):
113    """Test the OpenPrinter RPC with combinations of valid and invalid
114    server and printer names."""
115    def runTestArg(self, unc):
116        spoolss.openprinter(unc)
117
118class OpenPrinterCred(CredentialTest):
119    """Test opening printer with good and bad credentials."""
120    def runTestArg(self, creds):
121        spoolss.openprinter(self.uncname, creds = creds)
122
123class ClosePrinter(PrintServerTest):
124    """Test the ClosePrinter RPC on a printer handle."""
125    def runTest(self):
126        hnd = spoolss.openprinter(self.uncname)
127        spoolss.closeprinter(hnd)
128
129class ClosePrinterServer(PrintServerTest):
130    """Test the ClosePrinter RPC on a print server handle."""
131    def runTest(self):
132        hnd = spoolss.openprinter("\\\\%s" % self.server["hostname"])
133        spoolss.closeprinter(hnd)
134
135class GetPrinterInfo(PrintServerTest):
136    """Retrieve printer info at various levels."""
137
138    # Sample printer data
139
140    sample_info = {
141        0: {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '\\\\win2kdc1', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': '\\\\win2kdc1\\p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 2, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0},
142        1: {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': '\\\\win2kdc1\\p', 'description': '\\\\win2kdc1\\p,HP LaserJet 4,Canberra office'},
143        2: {'comment': "I'm a teapot!", 'status': 1, 'print_processor': 'WinPrint', 'until_time': 0, 'share_name': 'p', 'start_time': 0, 'device_mode': {'icm_method': 1, 'bits_per_pel': 0, 'log_pixels': 0, 'orientation': 1, 'panning_width': 0, 'color': 2, 'pels_width': 0, 'print_quality': 600, 'driver_version': 24, 'display_flags': 0, 'y_resolution': 600, 'media_type': 0, 'display_frequency': 0, 'icm_intent': 0, 'pels_height': 0, 'reserved1': 0, 'size': 220, 'scale': 100, 'dither_type': 0, 'panning_height': 0, 'default_source': 7, 'duplex': 1, 'fields': 16131, 'spec_version': 1025, 'copies': 1, 'device_name': '\\\\win2kdc1\\p', 'paper_size': 1, 'paper_length': 0, 'private': 'private', 'collate': 0, 'paper_width': 0, 'form_name': 'Letter', 'reserved2': 0, 'tt_option': 0}, 'port_name': 'LPT1:', 'sepfile': '', 'parameters': '', 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'name': '\\\\win2kdc1\\p', 'server_name': '\\\\win2kdc1', 'level': 2, 'datatype': 'RAW', 'cjobs': 0, 'average_ppm': 0, 'priority': 1, 'driver_name': 'HP LaserJet 4', 'location': 'Canberra office', 'attributes': 8776, 'default_priority': 0},
144        3: {'flags': 4, 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'level': 3}
145        }
146
147    def runTest(self):
148        self.hnd = spoolss.openprinter(self.uncname)
149
150        # Everyone should have getprinter levels 0-3
151
152        for i in (0, 1, 2, 3):
153            info = self.hnd.getprinter(level = i)
154            try:
155                stf.dict_check(self.sample_info[i], info)
156            except ValueError, msg:
157                raise "info%d: %s" % (i, msg)
158
159class EnumPrinters(PrintServerTest):
160    """Enumerate print info at various levels."""
161
162    sample_info = {
163
164        0: {'q': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1833435136, 'name': 'q', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'p': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1831337984, 'name': 'p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'magpie': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': 'magpie', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}},
165
166        1: {'q': {'comment': 'cheepy birds', 'level': 1, 'flags': 8388608, 'name': 'q', 'description': 'q,HP LaserJet 4,'}, 'p': {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': 'p', 'description': 'p,HP LaserJet 4,Canberra office'}, 'magpie': {'comment': '', 'level': 1, 'flags': 8388608, 'name': 'magpie', 'description': 'magpie,Generic / Text Only,'}}
167        }
168
169    def runTest(self):
170        for i in (0, 1):
171            info = spoolss.enumprinters(
172                "\\\\%s" % self.server["hostname"], level = i)
173            try:
174                stf.dict_check(self.sample_info[i], info)
175            except ValueError, msg:
176                raise "info%d: %s" % (i, msg)
177
178class EnumPrintersArg(ArgTestServer):
179    def runTestArg(self, unc):
180        spoolss.enumprinters(unc)
181
182class EnumPrintersCred(CredentialTest):
183    """Test opening printer with good and bad credentials."""
184    def runTestArg(self, creds):
185        spoolss.enumprinters(
186            "\\\\%s" % self.server["hostname"], creds = creds)
187
188class EnumPrinterdrivers(PrintServerTest):
189
190    sample_info = {
191        1: {'Okipage 10ex (PCL5E) : STANDARD': {'name': 'Okipage 10ex (PCL5E) : STANDARD', 'level': 1}, 'Generic / Text Only': {'name': 'Generic / Text Only', 'level': 1}, 'Brother HL-1030 series': {'name': 'Brother HL-1030 series', 'level': 1}, 'Brother HL-1240 series': {'name': 'Brother HL-1240 series', 'level': 1}, 'HP DeskJet 1220C Printer': {'name': 'HP DeskJet 1220C Printer', 'level': 1}, 'HP LaserJet 4100 PCL 6': {'name': 'HP LaserJet 4100 PCL 6', 'level': 1}, 'HP LaserJet 4': {'name': 'HP LaserJet 4', 'level': 1}},
192        2: {'Okipage 10ex (PCL5E) : STANDARD': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDDUI.DLL', 'name': 'Okipage 10ex (PCL5E) : STANDARD', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\OKIPAGE.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'Generic / Text Only': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRVUI.DLL', 'name': 'Generic / Text Only', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRV.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\TTY.GPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1030 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1030 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL103.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1240 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1240 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL124.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP DeskJet 1220C Printer': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'name': 'HP DeskJet 1220C Printer', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4100 PCL 6': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042E.DLL', 'name': 'HP LaserJet 4100 PCL 6', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042G.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042I.PMD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff0.dll', 'name': 'HP LaserJet 4', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff2.dll', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff39.pmd', 'level': 2, 'architecture': 'Windows NT x86'}}
193        }
194
195    def runTest(self):
196        for i in (1, 2):
197            info = spoolss.enumprinterdrivers(
198                "\\\\%s" % self.server["hostname"], level = i)
199            try:
200                if not self.sample_info.has_key(i):
201                    self.log("%s" % info)
202                    self.fail()
203                stf.dict_check(self.sample_info[i], info)
204            except ValueError, msg:
205                raise "info%d: %s" % (i, msg)
206
207class EnumPrinterdriversArg(ArgTestServer):
208    def runTestArg(self, unc):
209        spoolss.enumprinterdrivers(unc)
210
211class EnumPrinterdriversCred(CredentialTest):
212    """Test opening printer with good and bad credentials."""
213    def runTestArg(self, creds):
214        spoolss.enumprinterdrivers(
215            "\\\\%s" % self.server["hostname"], creds = creds)
216
217def usage():
218    print "Usage: spoolss.py [options] [test1[,test2...]]"
219    print "\t -v/--verbose     Display debugging information"
220    print "\t -l/--list-tests  List available tests"
221    print
222    print "A list of comma separated test names or regular expressions"
223    print "can be used to filter the tests performed."
224
225def test_match(subtest_list, test_name):
226    """Return true if a test matches a comma separated list of regular
227    expression of test names."""
228    # re.match does an implicit ^ at the start of the pattern.
229    # Explicitly anchor to end to avoid matching substrings.
230    for s in string.split(subtest_list, ","):
231        if re.match(s + "$", test_name):
232            return 1
233    return 0
234
235if __name__ == "__main__":
236    import os, sys, string
237    import getopt
238
239    try:
240        opts, args = getopt.getopt(sys.argv[1:], "vl", \
241                                   ["verbose", "list-tests"])
242    except getopt.GetoptError:
243        usage()
244        sys.exit(0)
245
246    verbose = 0
247    list_tests = 0
248
249    for opt, arg in opts:
250        if opt in ("-v", "--verbose"):
251            verbose = 1
252        if opt in ("-l", "--list-tests"):
253            list_tests = 1
254
255    if len(args) > 1:
256        usage()
257        sys.exit(0)
258
259    test_list = [
260        OpenPrinterArg,
261        OpenPrinterCred,
262        ClosePrinter,
263        ClosePrinterServer,
264        GetPrinterInfo,
265        EnumPrinters,
266        EnumPrintersCred,
267        EnumPrintersArg,
268        EnumPrinterdrivers,
269        EnumPrinterdriversCred,
270        EnumPrinterdriversArg,
271        ]
272
273    if len(args):
274        t = []
275        for test in test_list:
276            if test_match(args[0], test.__name__):
277                t.append(test)
278        test_list = t
279
280    if os.environ.has_key("SAMBA_DEBUG"):
281        spoolss.setup_logging(interactive = 1)
282        spoolss.set_debuglevel(10)
283
284    if list_tests:
285        for test in test_list:
286            print test.__name__
287    else:
288        comfychair.runtests(test_list, verbose = verbose)
289