1# gui_cmdwin.py
2#
3# openipmi GUI command window handling
4#
5# Author: MontaVista Software, Inc.
6#         Corey Minyard <minyard@mvista.com>
7#         source@mvista.com
8#
9# Copyright 2006 MontaVista Software Inc.
10#
11#  This program is free software; you can redistribute it and/or
12#  modify it under the terms of the GNU Lesser General Public License
13#  as published by the Free Software Foundation; either version 2 of
14#  the License, or (at your option) any later version.
15#
16#
17#  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
18#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20#  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21#  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22#  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23#  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24#  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25#  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26#  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27#
28#  You should have received a copy of the GNU Lesser General Public
29#  License along with this program; if not, write to the Free
30#  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31#
32
33try:
34    import Tix
35except:
36    import tkinter
37    from tkinter import tix as Tix
38
39import xml.dom
40import xml.dom.minidom
41import OpenIPMI
42from openipmigui import _saveprefs
43from openipmigui import _misc
44import os
45import stat
46import sys
47
48class CommandWindow(Tix.ScrolledText):
49    def __init__(self, parent, ui):
50        Tix.ScrolledText.__init__(self, parent)
51        self.ui = ui
52        self.currow = 0
53        self.max_lines = 1000
54        self.max_history = 100
55        self.text.bind("<Key>", self.HandleChar)
56        self.text.bind("<Control-Key>", self.HandleCtrlChar)
57        self.text.insert("end", "> ")
58        self.history = [ ]
59        self.lasthist = 0
60        for cmd in self.ui.mainhandler.init_history:
61            self.history.append(cmd[1])
62            self.lasthist += 1
63            pass
64        self.history.append("")
65        self.ui.mainhandler.init_history = None
66        self.currhist = self.lasthist
67
68        self.cmdlang = OpenIPMI.alloc_cmdlang(self)
69        self.indent = 0;
70        self.cmd_in_progress = False
71
72        self.bind("<Destroy>", self.OnDestroy)
73
74        OpenIPMI.set_cmdlang_global_err_handler(self)
75        return
76
77    def global_cmdlang_err(self, objstr, location, errstr, errval):
78        log = "Global cmdlang err: " + errstr;
79        if (len(location) > 0) or (len(objstr) > 0):
80            log += " at " + objstr + "(" + location + ")"
81            pass
82        log += ": " + errstr + " (" + str(errval) + ")"
83        self.ui.new_log(log)
84        return
85
86    def OnDestroy(self, event):
87        self.cmdlang = None
88        return
89
90    def cmdlang_down(self, cmdlang):
91        self.indent += 2
92        return
93
94    def cmdlang_up(self, cmdlang):
95        if (self.indent >= 2):
96            self.indent -= 2
97            pass
98        return
99
100    def HandleNewLines(self):
101        lastline = int(self.text.index("end").split(".")[0]) - 1
102        while (lastline > self.max_lines):
103            self.text.delete("1.0", "2.0")
104            lastline = int(self.text.index("end").split(".")[0]) - 1
105            pass
106        return
107
108    def InsertString(self, string):
109        (lastrow, lastcol) = self.text.index("end").split(".")
110        lastrow = str(int(lastrow)-1)
111        self.text.insert(lastrow + ".0", string)
112        self.HandleNewLines()
113        self.text.see("insert")
114        return
115
116    def cmdlang_done(self, cmdlang):
117        err = cmdlang.get_err()
118        if (err != 0):
119            errtext = cmdlang.get_errstr()
120            objstr = cmdlang.get_objstr()
121            location = cmdlang.get_location()
122            if (location == None):
123                location = ""
124                pass
125            if (objstr == ""):
126                str = ("error: %s: %s (0x%x, %s)\n"
127                       % (location, errtext, err,
128                          OpenIPMI.get_error_string(err)))
129                pass
130            else:
131                str = ("error: %s %s: %s (0x%x, %s)\n"
132                       % (location, objstr, errtext, err,
133                          OpenIPMI.get_error_string(err)))
134                pass
135            self.InsertString(str)
136            pass
137        self.cmd_in_progress = False
138        self.text.insert("end", "> ")
139        return
140
141    def cmdlang_out(self, cmdlang, name, value):
142        if (cmdlang.is_help()):
143            self.InsertString("%*s%s %s\n" % (self.indent, "", name, value))
144            pass
145        else:
146            self.InsertString("%*s%s: %s\n" % (self.indent, "", name, value))
147            pass
148        return
149
150    def cmdlang_out_binary(self, cmdlang, name, value):
151        self.InsertString("%*s%s: %s\n" % (self.indent, "", name,
152                                           _misc.HexArrayToStr(value)))
153        return
154
155    def cmdlang_out_unicode(self, cmdlang, name, value):
156        self.InsertString("%*s%s:U: %s\n" % (self.indent, "", name,
157                                             _misc.HexArrayToStr(value)))
158        return
159
160    def HandleNewHistory(self):
161        self.history.append("")
162        if (self.lasthist >= self.max_history):
163            del self.history[0]
164            pass
165        else:
166            self.lasthist += 1
167            pass
168        return
169
170    def HandleCtrlChar(self, event):
171        # This is here to catch the control characters and pass them
172        # on so HandleChar() doesn't trap and throw them away.
173        return
174
175    def HandleChar(self, event):
176        key = event.keysym
177        if (key == "BackSpace"):
178            # A key that will result in a backspace.  Make sure it
179            # only occurs on the last line and not in the prompt area.
180            if (self.cmd_in_progress):
181                return "break"
182            (lastrow, lastcol) = self.text.index("end").split(".")
183            lastrow = str(int(lastrow)-1)
184            (currrow, currcol) = self.text.index("insert").split(".")
185            if ((lastrow != currrow) or (int(currcol) <= 2)):
186                # Ignore the keypress
187                return "break"
188            pass
189        elif (key == "Delete"):
190            # A key that will result in a deletion.  Make sure it
191            # only occurs on the last line and not in the prompt area.
192            if (self.cmd_in_progress):
193                return "break"
194            (lastrow, lastcol) = self.text.index("end").split(".")
195            lastrow = str(int(lastrow)-1)
196            (currrow, currcol) = self.text.index("insert").split(".")
197            if ((lastrow != currrow) or (int(currcol) <= 1)):
198                # Ignore the keypress
199                return "break"
200            pass
201        elif (key == "Return"):
202            # Enter the command...
203            if (self.cmd_in_progress):
204                return "break"
205            (lastrow, lastcol) = self.text.index("end").split(".")
206            lastrow = str(int(lastrow)-1)
207            (currrow, currcol) = self.text.index("insert").split(".")
208            if ((lastrow != currrow) or (int(currcol) <= 2)):
209                # Ignore the keypress
210                return "break"
211
212            command = self.text.get(lastrow + ".2", lastrow + ".end")
213            self.HandleNewLines();
214            if (command != ""):
215                self.text.insert("end", "\n")
216                self.history[self.lasthist] = command
217                self.HandleNewHistory()
218                self.cmdlang.handle(str(command))
219                pass
220            else:
221                self.text.insert("end", "\n> ")
222                pass
223            self.text.mark_set("insert", "end")
224            self.currhist = self.lasthist
225            self.text.see("insert")
226            return "break"
227        elif (key == "Up"):
228            # Previous history
229            if (self.cmd_in_progress):
230                return "break"
231            if (self.currhist == 0):
232                return "break"
233            (lastrow, lastcol) = self.text.index("end").split(".")
234            lastrow = str(int(lastrow)-1)
235            if (self.currhist == self.lasthist):
236                command = self.text.get(lastrow + ".2", lastrow + ".end")
237                self.history[self.lasthist] = command
238                pass
239            self.text.delete(lastrow + ".2", lastrow + ".end")
240            self.currhist -= 1
241            self.text.insert(lastrow + ".2", self.history[self.currhist])
242            return "break"
243        elif (key == "Down"):
244            if (self.cmd_in_progress):
245                return "break"
246            # Next history
247            if (self.currhist == self.lasthist):
248                return "break"
249            (lastrow, lastcol) = self.text.index("end").split(".")
250            lastrow = str(int(lastrow)-1)
251            self.text.delete(lastrow + ".2", lastrow + ".end")
252            self.currhist += 1
253            self.text.insert(lastrow + ".2", self.history[self.currhist])
254            return "break"
255        elif (len(event.char) == 1) and (event.char < chr(255)):
256            # A key that will result in text addition.  Make sure it
257            # only occurs on the last line and not in the prompt area.
258            if (self.cmd_in_progress):
259                return "break"
260            (lastrow, lastcol) = self.text.index("end").split(".")
261            lastrow = str(int(lastrow)-1)
262            (currrow, currcol) = self.text.index("insert").split(".")
263            if ((lastrow != currrow) or (int(currcol) < 2)):
264                # Ignore the keypress
265                return "break"
266            pass
267        elif ((key == "Left") or (key == "Right") or
268              (key == "Insert") or
269              (key == "End") or (key == "Home") or
270              (key == "Prior") or (key == "Next")):
271            # Pass these through
272            return
273        else:
274            return "break"
275        return
276
277    pass
278
279def cmphist(a, b):
280    return cmp(a[0], b[0])
281
282def _HistorySave(mainhandler, file):
283    if (not mainhandler.init_history):
284        return
285    domimpl = xml.dom.getDOMImplementation()
286    doc = domimpl.createDocument(None, "IPMIHistory", None)
287    main = doc.documentElement
288    i = 0
289    for cmd in mainhandler.init_history:
290        if (cmd != ""):
291            helem = doc.createElement("hval")
292            helem.setAttribute("idx", str(i))
293            helem.setAttribute("val", cmd)
294            main.appendChild(helem)
295            i += 1
296            pass
297        pass
298    try:
299        info = os.stat(file)
300        pass
301    except:
302        # File doesn't exist, create it.
303        try:
304            fd = os.open(file, os.O_WRONLY | os.O_CREAT,
305                         stat.S_IRUSR | stat.S_IWUSR)
306            os.close(fd)
307            pass
308        except:
309            _oi_logging.error("Unable to create startup file " + file)
310            return
311        pass
312    try:
313        f = open(file, 'w')
314        doc.writexml(f, indent='', addindent='\t', newl='\n')
315    except:
316        pass
317    return
318
319def _HistoryRestore(mainhandler, file):
320    info = None
321    try:
322        info = os.stat(file)
323    except:
324        pass
325    if (info):
326        if ((info.st_mode & (stat.S_IRWXG | stat.S_IRWXO)) != 0):
327            sys.exit("The history file '" + file + "' is group or world"
328                     + " accessible.  It contains passwords, and"
329                     + " should be secure.  Not starting the GUI,"
330                     + " please fix the problem first.")
331            return
332        pass
333    try:
334        doc = xml.dom.minidom.parse(file).documentElement
335    except:
336        return
337    for c in doc.childNodes:
338        if (c.nodeType == c.ELEMENT_NODE):
339            try:
340                idx = int(c.getAttribute("idx"))
341                val = c.getAttribute("val")
342                mainhandler.init_history.append( (idx, val) )
343                pass
344            except:
345                pass
346            pass
347        pass
348    return
349