mozrepl.py 9.53 KB
Newer Older
Melroy van den Berg's avatar
Melroy van den Berg committed
1 2 3 4
"""
This module is a helper module which connects to the Mozrepl plugin of Firefox, using telnet.
And futhermore send all the repl commands to firefox and read data back.
"""
Melroy van den Berg's avatar
Melroy van den Berg committed
5 6 7
from time import sleep
import telnetlib
import socket
Melroy van den Berg's avatar
Melroy van den Berg committed
8 9
import sys

10
# Internal modules
Melroy van den Berg's avatar
Melroy van den Berg committed
11
import process
12
from text_color import TextColor
Melroy van den Berg's avatar
Melroy van den Berg committed
13

Melroy van den Berg's avatar
Melroy van den Berg committed
14 15 16
NR_RETRIES_DOCUMENT=1000 # 200 sec (3.3 min. time-out)/ 0.2 sec = 1000 tries
NR_RETRIES_FIREFOX=360 # 180 sec (3 min. time-out)/ 0.5 sec = 360 tries

17
class MozRepl(object):
Melroy van den Berg's avatar
Melroy van den Berg committed
18
    def __init__(self):
Melroy van den Berg's avatar
Melroy van den Berg committed
19 20 21 22
        """
        Contructor, only create a telnet connection
        """
        # Default socket name (repl)
Melroy van den Berg's avatar
Melroy van den Berg committed
23
        self.SOCKET_NAME = b'repl'
Melroy van den Berg's avatar
Melroy van den Berg committed
24
        # $ telnet localhost 4242
Melroy van den Berg's avatar
Melroy van den Berg committed
25 26
        try:
      	    self.t = telnetlib.Telnet("localhost", 4242)
Melroy van den Berg's avatar
Melroy van den Berg committed
27
        except OSError as e:
28
            print (TextColor.ERROR + "WARNING: Maybe firefox is not running? Trying to start Firefox." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
29 30 31 32 33 34
            status = process.startFirefox()
            if status == 0:
                # Try one time again
                try:
                    self.t = telnetlib.Telnet("localhost", 4242)
                except OSError as e:
35
                    print (TextColor.ERROR + "ERROR: Could not connect to Firefox. Did you install Mozrepl plugin in Firefox? (Install: https://addons.mozilla.org/nl/firefox/addon/mozrepl/).\nIf so, did you also start the service. In the menu: Extra->Mozrepl->Start." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
36 37 38
                    exit(1)
            else:
                print ("Exit")
Melroy van den Berg's avatar
Melroy van den Berg committed
39
                exit(1)
Melroy van den Berg's avatar
Melroy van den Berg committed
40
        conn_log = self.read_all_data()
Melroy van den Berg's avatar
Melroy van den Berg committed
41
        split_log = conn_log.splitlines()
Melroy van den Berg's avatar
Melroy van den Berg committed
42
        sockname =  split_log[-1].split(b'>')[0].strip()
Melroy van den Berg's avatar
Melroy van den Berg committed
43 44
        if self.SOCKET_NAME != sockname:
            self.SOCKET_NAME = sockname
Melroy van den Berg's avatar
Melroy van den Berg committed
45
        self.READ_UNTIL_CHARS=self.SOCKET_NAME+b'>'
Melroy van den Berg's avatar
Melroy van den Berg committed
46

47
    def load_page_blocking(self, url, blocking_url="", exception_url=None):
Melroy van den Berg's avatar
Melroy van den Berg committed
48 49 50 51 52
        """
        Load a website, busy waiting until submitted
        url - page to load
        blocking_url - url (beginning of url) that waits until changed differently (blocking until changed to another URL)
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
53
        # Open url in current tab/window
Melroy van den Berg's avatar
Melroy van den Berg committed
54
        self.open_url(url)
Melroy van den Berg's avatar
Melroy van den Berg committed
55

Melroy van den Berg's avatar
Melroy van den Berg committed
56 57 58 59
        # When no blocking url is defined use input URL instead
        if blocking_url == "":
            blocking_url = url
        blocking_url = b'"' + blocking_url.encode('utf-8')
Melroy van den Berg's avatar
Melroy van den Berg committed
60

61 62 63 64 65
        #  Switch to the document active tab (web-page)
        self.switch_to_content()
        # From this moment on, the write_data/read_data calls COULD be important to
        # run in context of content (enter(content)), use read_data(true) when applicable and check the valid flag

Melroy van den Berg's avatar
Melroy van den Berg committed
66
        # Blocking wait until the current URL is different from the 'blocking url'
67
        self.wait_until_url_changed(blocking_url, exception_url)
Melroy van den Berg's avatar
Melroy van den Berg committed
68 69 70 71 72 73

    def wait_until_page_complete(self):
        """
        Busy wait until the page is completly loaded
        """
        tries=0
Melroy van den Berg's avatar
Melroy van den Berg committed
74
        while True:
Melroy van den Berg's avatar
Melroy van den Berg committed
75
            # Get the page load state
Melroy van den Berg's avatar
Melroy van den Berg committed
76 77
            self.write_data(b"document.readyState")
            (state, valid) = self.read_data()
Melroy van den Berg's avatar
Melroy van den Berg committed
78
            sleep(0.2)
Melroy van den Berg's avatar
Melroy van den Berg committed
79
            if state == b"\"complete\"":
Melroy van den Berg's avatar
Melroy van den Berg committed
80
                # When completed wait a bit longer, in order to set repl again + HTML is updated fully, strange right?
Melroy van den Berg's avatar
Melroy van den Berg committed
81
                # Why is complete not completely fully loaded HTML
Melroy van den Berg's avatar
Melroy van den Berg committed
82
                sleep(2)
Melroy van den Berg's avatar
Melroy van den Berg committed
83
                break
Melroy van den Berg's avatar
Melroy van den Berg committed
84
            tries+=1
Melroy van den Berg's avatar
Melroy van den Berg committed
85
            if tries == NR_RETRIES_DOCUMENT:
86
                print (TextColor.ERROR + "ERROR: Too many retries. Do you have internet connection at all? Exit." + TextColor.ENDC) # Time-out
Melroy van den Berg's avatar
Melroy van den Berg committed
87 88
                exit(1)

Melroy van den Berg's avatar
Melroy van den Berg committed
89
    
Melroy van den Berg's avatar
Melroy van den Berg committed
90
    def wait_until_url_changed(self, blocking_url, exception_url=None):
Melroy van den Berg's avatar
Melroy van den Berg committed
91
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
92
        Busy wait around is_url_changed(). Only breaks when URL is changed.
Melroy van den Berg's avatar
Melroy van den Berg committed
93 94
        """
        while True:
Melroy van den Berg's avatar
Melroy van den Berg committed
95 96 97 98 99
            # Check if URL is changed
            (changed, valid) = self.is_url_changed(blocking_url, exception_url)
            if changed:
                # URL changed! Move on.
                break
Melroy van den Berg's avatar
Melroy van den Berg committed
100
            if valid:
Melroy van den Berg's avatar
Melroy van den Berg committed
101
                # Otherwise, sleep a bit, when data was valid
Melroy van den Berg's avatar
Melroy van den Berg committed
102 103
                sleep(3)

Melroy van den Berg's avatar
Melroy van den Berg committed
104

Melroy van den Berg's avatar
Melroy van den Berg committed
105
    def is_url_changed(self, blocking_url, exception_url=None):
Melroy van den Berg's avatar
Melroy van den Berg committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
        """
        Check if the current URL is changed, and is different from the blocking url.

        input(s):   print_wait - Boolean which only prints once a message
                    blocking_url - Blocking url until this is different
                    exception_url - Where it should not trigger on (eg. cookie pop-up URL), which is a false positive trigger
        return      (changed, valid) -  + Was the check valid (containing valid data)? True was valid.
        """
        changed=False
        self.write_data(b"window.location.href")
        # Be-sure we run in content always (see valid flag)
        (cur_url, valid) = self.read_data(True)
        # Only look at cur_url when there is valid data (data running in the current content)
        if valid:
            if exception_url is not None:
                if cur_url.startswith(b'"' + exception_url.encode('utf-8')):
                    # Ignore the exception_url (if applicable)
                    valid=False
            if valid:
                if not cur_url.startswith(blocking_url):
                    # Page/url is changed
                    changed=True
            sys.stdout.write('.')
            sys.stdout.flush()
        return (changed,valid)

Melroy van den Berg's avatar
Melroy van den Berg committed
132 133 134 135
    def open_url(self, url):
        """
        Trigger a change of URL in current tab
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
136 137 138 139 140
        self.write_data(b"content.location.href='" + url.strip().encode('utf-8') + b"'")
        self.skip_data()

        # Blocking wait
        self.wait_until_page_complete()
Melroy van den Berg's avatar
Melroy van den Berg committed
141

Melroy van den Berg's avatar
Melroy van den Berg committed
142
    def create_popup(self, message):
Melroy van den Berg's avatar
Melroy van den Berg committed
143 144 145
        """
        Show pop-up to user in browser
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
146
        self.write_data(b"alert('" + message.encode('utf-8') + b"')")
Melroy van den Berg's avatar
Melroy van den Berg committed
147 148 149 150 151 152 153 154 155 156 157 158 159
        self.skip_data()

    def has_document_focus(self):
        """
        Return if the document has focus or not 
        return True if it has focus otherwise False
        """
        self.write_data(b"document.hasFocus()")
        (focus, valid) = self.read_data()
        if focus == b"false":
            return False
        else:
            return True
Melroy van den Berg's avatar
Melroy van den Berg committed
160

Melroy van den Berg's avatar
Melroy van den Berg committed
161
    def write_data(self, byte_buffer):
Melroy van den Berg's avatar
Melroy van den Berg committed
162 163
        """
        Write data via write. New line at the end.
Melroy van den Berg's avatar
Melroy van den Berg committed
164
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
165 166 167
        try:
            self.t.write(byte_buffer + b'\n')
        except socket.error:
168
            print (TextColor.ERROR + "ERROR: Connection problem. Did you disable the plugin or closed Firefox? Exit." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
169
            exit(1)
Melroy van den Berg's avatar
Melroy van den Berg committed
170
        
Melroy van den Berg's avatar
Melroy van den Berg committed
171
    def read_data(self, inContentImportant=False):
Melroy van den Berg's avatar
Melroy van den Berg committed
172 173
        """
        Read the buffer via read until, strip 'reply>' from the message
Melroy van den Berg's avatar
Melroy van den Berg committed
174 175 176

        REMINDER: Don't forget to check the valid flag, when isContent=True!

Melroy van den Berg's avatar
Melroy van den Berg committed
177
        input(s):  inContentImportant - Should the read_data only work when the content is entered (repl.enter(content)?
Melroy van den Berg's avatar
Melroy van den Berg committed
178 179

        output(s): output - the buffer from repl itself.
Melroy van den Berg's avatar
Melroy van den Berg committed
180
                   valid - if valid is invalid (false), execute the previous write_data (followed by a read_data) again.
Melroy van den Berg's avatar
Melroy van den Berg committed
181
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
182 183
        valid=True
        i = len(self.READ_UNTIL_CHARS)
Melroy van den Berg's avatar
Melroy van den Berg committed
184 185
        try:
            output = self.t.read_until(self.READ_UNTIL_CHARS)[:-i].strip()
Melroy van den Berg's avatar
Melroy van den Berg committed
186
        except OSError as e:
187
            print (TextColor.ERROR + "ERROR: Connection problem. Did you disable the plugin or closed Firefox? Exit." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
188 189
            exit(1)

Melroy van den Berg's avatar
Melroy van den Berg committed
190 191 192 193
        # During a context switch (page change) repl will go back to home, 
        # should the read command be part of the Document context (eg. is enter(content) required in order to work)
        if inContentImportant:
            # If a context uploading is seen, switch back to the content again: enter(content).
Melroy van den Berg's avatar
Melroy van den Berg committed
194
            if output.startswith(b"Host context unloading"):
Melroy van den Berg's avatar
Melroy van den Berg committed
195
                # Ensure to switch again to the content, and try again.
Melroy van den Berg's avatar
Melroy van den Berg committed
196
                self.switch_to_content()
Melroy van den Berg's avatar
Melroy van den Berg committed
197
                # Mark as invalid, since the data is not what you expect to be
Melroy van den Berg's avatar
Melroy van den Berg committed
198
                # re-execute the previous write_data() agaom/
Melroy van den Berg's avatar
Melroy van den Berg committed
199 200 201
                valid=False
        return (output,valid)

202
    def set_hidden_input_field_form(self, form_id, name, value):
Melroy van den Berg's avatar
Melroy van den Berg committed
203
        """
204
        Append hidden input field to a form (id)
Melroy van den Berg's avatar
Melroy van den Berg committed
205
        """
206
        self.write_data(b"input = document.createElement('input');input.setAttribute('type', 'hidden');input.setAttribute('name', '" + name.encode('utf-8') + b"');input.setAttribute('value', '" + value.encode('utf-8') + b"');document.getElementById('" + form_id.encode('utf-8') + b"').appendChild(input);")
Melroy van den Berg's avatar
Melroy van den Berg committed
207 208
        self.skip_data()

Melroy van den Berg's avatar
Melroy van den Berg committed
209 210 211 212 213 214 215
    def scroll_down_page(self):
        """
        Scroll page fully down
        """
        self.write_data(b"window.scrollTo(0,document.body.scrollHeight)")
        self.skip_data()

Melroy van den Berg's avatar
Melroy van den Berg committed
216 217 218
    def switch_to_content(self):
        self.write_data(self.SOCKET_NAME + b".enter(content)")
        self.skip_data()
Melroy van den Berg's avatar
Melroy van den Berg committed
219

Melroy van den Berg's avatar
Melroy van den Berg committed
220
    def read_all_data(self):
Melroy van den Berg's avatar
Melroy van den Berg committed
221 222
        try:
            return self.t.read_until(b"> ")
Melroy van den Berg's avatar
Melroy van den Berg committed
223
        except OSError as e:
224
            print (TextColor.ERROR + "ERROR: Connection problem. Did you disable the plugin or closed Firefox? Exit." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
225
            exit(1)
Melroy van den Berg's avatar
Melroy van den Berg committed
226

Melroy van den Berg's avatar
Melroy van den Berg committed
227
    def skip_data(self):
Melroy van den Berg's avatar
Melroy van den Berg committed
228 229 230 231
        """
        Also read data until, but ignore the output,
        get the buffer clean
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
232 233
        try:
            self.t.read_until(self.READ_UNTIL_CHARS)
Melroy van den Berg's avatar
Melroy van den Berg committed
234
        except OSError as e:
235
            print (TextColor.ERROR + "ERROR: Connection problem. Did you disable the plugin or closed Firefox? Exit." + TextColor.ENDC)
Melroy van den Berg's avatar
Melroy van den Berg committed
236
            exit(1)
Melroy van den Berg's avatar
Melroy van den Berg committed
237 238

    def close(self):
Melroy van den Berg's avatar
Melroy van den Berg committed
239 240 241
        """
        Close active connection
        """
Melroy van den Berg's avatar
Melroy van den Berg committed
242
        #self.write_data(self.SOCKET_NAME + b".quit()")
Melroy van den Berg's avatar
Melroy van den Berg committed
243 244
        self.t.close()