]>
Commit | Line | Data |
---|---|---|
5b4622f6 MF |
1 | // Written by Mike Frysinger <vapier@gmail.com>. |
2 | // Released into the public domain. | |
3 | ||
4 | function status(msg) { | |
5 | $$('[name=status]').innerText = msg; | |
6 | } | |
7 | ||
0e1cff99 MF |
8 | function popup_msg(ele, msg) { |
9 | var popup = $$('[name=popup_msg]'); | |
10 | var pos = ele.getBoundingClientRect(); | |
11 | popup.innerText = msg; | |
12 | // Might want to add some generalized "center in element" logic. | |
13 | popup.style.top = 5 + pos.top + 'px'; | |
14 | popup.style.left = pos.left + 'px'; | |
15 | popup.style.display = ''; | |
16 | setTimeout(() => { popup.style.display = 'none'; }, 3000); | |
17 | } | |
18 | ||
5b4622f6 MF |
19 | // Create a packet following the spec: |
20 | // https://en.wikipedia.org/wiki/Wake-on-LAN#Magic_packet | |
21 | function magicpacket(mac, pass) { | |
22 | var data = new ArrayBuffer(6 + 16 * 6 + 6 + 6); | |
23 | var bytes = new Uint8Array(data); | |
24 | var i, j, base = 0; | |
25 | ||
26 | // First 6 bytes should be all 0xFF. | |
27 | for (i = 0; i < 6; ++i) | |
28 | bytes[base + i] = 0xff; | |
29 | base += 6; | |
30 | ||
31 | // Then the MAC address is repeated 16 times. | |
32 | for (i = 0; i < 6; ++i) | |
33 | for (j = 0; j < 16 * 6; j += 6) | |
34 | bytes[base + j + i] = mac[i]; | |
35 | base += 16 * 6; | |
36 | ||
37 | // Then 6 bytes before the pass should be 0xFF. | |
38 | for (i = 0; i < 6; ++i) | |
39 | bytes[base + i] = 0xff; | |
40 | base += 6; | |
41 | ||
42 | // Finally the 6 bytes of the password. | |
43 | for (i = 0; i < 6; ++i) | |
44 | bytes[base + i] = pass[i]; | |
45 | ||
46 | return data; | |
47 | } | |
48 | ||
49 | function split_data(v) { | |
50 | var data = Array(6); | |
51 | var i, idata; | |
52 | ||
53 | window['sync_' + v](); | |
54 | ||
55 | for (i = 0; i < 6; ++i) { | |
56 | idata = $$('input[name=' + v + i + ']'); | |
57 | if (!/^[0-9a-fA-F]?[0-9a-fA-F]$/.test(idata.value.replace(' ', ''))) { | |
58 | status(v + ' byte ' + i + ' is invalid; must be 2 hex characters'); | |
59 | idata.focus(); | |
60 | idata.setSelectionRange(0, idata.value.length); | |
61 | return false; | |
62 | } | |
63 | data[i] = parseInt(idata.value, 16); | |
64 | } | |
65 | ||
66 | return data; | |
67 | } | |
68 | ||
69 | function send() { | |
70 | status('initializing'); | |
71 | ||
72 | var form = $$('form[name=settings]'); | |
73 | var shost = '0.0.0.0'; | |
74 | var dhost = form.host.value; | |
75 | var port = parseInt(form.port.value); | |
76 | ||
77 | // Get the MAC address & password to convert to packet data. | |
78 | var mac = split_data('mac'); | |
79 | var pass = split_data('pass'); | |
80 | var data = magicpacket(mac, pass); | |
81 | console.log('packet', new Uint8Array(data)); | |
82 | ||
83 | var checkresult = function(s, step, result) { | |
84 | if (result < 0) { | |
85 | status('error in ' + step + ': ' + net_error_list[result]); | |
86 | chrome.sockets.udp.close(s, nullcb); | |
87 | return false; | |
88 | } | |
89 | return true; | |
90 | }; | |
91 | ||
92 | // Create the socket ... | |
93 | chrome.sockets.udp.create({}, function(createInfo) { | |
94 | var s = createInfo.socketId; | |
95 | ||
96 | console.log('[create] socketInfo', createInfo); | |
97 | status('binding ' + shost); | |
98 | ||
99 | // Bind it locally ... | |
100 | chrome.sockets.udp.bind(s, shost, 0, function(result) { | |
101 | console.log('[bind] result', result); | |
102 | ||
103 | if (!checkresult(s, 'bind', result)) | |
104 | return false; | |
105 | ||
106 | status('enabling broadcast'); | |
107 | ||
108 | // Turn on broadcast support ... | |
109 | chrome.sockets.udp.setBroadcast(s, true, function(result) { | |
110 | console.log('[setBroadcast] result', result); | |
111 | ||
112 | if (!checkresult(s, 'broadcast', result)) | |
113 | return false; | |
114 | ||
115 | status('sending to ' + dhost + ':' + port); | |
116 | ||
117 | // Send the backet ... | |
118 | chrome.sockets.udp.send(s, data, dhost, port, function(sendInfo) { | |
119 | console.log('[send] sendInfo', sendInfo); | |
120 | ||
121 | if (!checkresult(s, 'send', sendInfo.resultCode)) | |
122 | return false; | |
123 | ||
124 | status('closing'); | |
125 | ||
126 | // Shut it down ... | |
127 | chrome.sockets.udp.close(s, function() { | |
128 | status('sent to ' + dhost + ':' + port); | |
129 | store_settings(); | |
130 | }); | |
131 | }); | |
132 | }); | |
133 | }); | |
134 | }); | |
135 | ||
136 | // Keep the form from submitting. | |
137 | return false; | |
138 | } | |
139 | ||
140 | function sync_it(v) { | |
141 | var smany = $$('span[name=' + v + '-many]'); | |
142 | var sone = $$('span[name=' + v + '-one]'); | |
143 | ||
144 | // Sync the two sets of fields. | |
145 | var i; | |
146 | if (smany.hidden) { | |
147 | var idata = $$('input[name=' + v + ']'); | |
148 | var data = idata.value.split(':'); | |
149 | ||
150 | if (data.length != 6) { | |
151 | data = idata.value.replace(/[ :]/g, ''); | |
152 | if (data.length != 6 * 2) { | |
153 | status('invalid ' + v + '; please fix'); | |
154 | return false; | |
155 | } | |
156 | data = data.match(/../g); | |
157 | } else { | |
158 | for (i = 0; i < 6; ++i) | |
159 | if (data[i].length > 2) { | |
160 | status('invalid ' + v + ' please fix'); | |
161 | return false; | |
162 | } | |
163 | } | |
164 | ||
165 | for (i = 0; i < 6; ++i) | |
166 | $$('input[name=' + v + i + ']').value = data[i]; | |
167 | } else { | |
168 | var data = ''; | |
169 | ||
170 | for (i = 0; i < 6; ++i) { | |
171 | data += $$('input[name=' + v + i + ']').value; | |
172 | if (i < 5) | |
173 | data += ':'; | |
174 | } | |
175 | ||
176 | $$('input[name=' + v + ']').value = data; | |
177 | } | |
178 | } | |
179 | function sync_mac() { return sync_it('mac'); } | |
180 | function sync_pass() { return sync_it('pass'); } | |
181 | ||
182 | ||
183 | function paste_mac() { | |
184 | sync_mac(); | |
185 | ||
186 | var smany = $$('span[name=mac-many]'); | |
187 | var sone = $$('span[name=mac-one]'); | |
188 | smany.hidden = !smany.hidden; | |
189 | sone.hidden = !sone.hidden; | |
190 | ||
191 | return false; | |
192 | } | |
193 | ||
194 | function paste_pass() { | |
195 | sync_pass(); | |
196 | ||
197 | var smany = $$('span[name=pass-many]'); | |
198 | var sone = $$('span[name=pass-one]'); | |
199 | smany.hidden = !smany.hidden; | |
200 | sone.hidden = !sone.hidden; | |
201 | ||
202 | return false; | |
203 | } | |
204 | ||
205 | /* | |
206 | * Storage logic. | |
207 | */ | |
0e1cff99 MF |
208 | |
209 | var computers = []; | |
210 | ||
5b4622f6 | 211 | var settings_keys = [ |
0e1cff99 MF |
212 | 'computers', |
213 | 'last_selected', | |
214 | 'theme', | |
215 | ]; | |
216 | var old_settings_keys = [ | |
5b4622f6 MF |
217 | 'host', |
218 | 'mac', | |
219 | 'pass', | |
220 | 'port', | |
221 | ]; | |
222 | ||
e54503ce | 223 | var default_theme = 'dark'; |
0e1cff99 | 224 | var default_name = 'Default'; |
e54503ce MF |
225 | var default_host = '192.168.0.255'; |
226 | var default_port = '40000'; | |
227 | var default_mac = '20:00:00:00:00:00'; | |
228 | var default_pass = '00:00:00:00:00:00'; | |
229 | ||
0e1cff99 MF |
230 | /* |
231 | * Set form data based on selected computer settings. | |
232 | * Uses defaults if not available. | |
233 | */ | |
234 | function load_computer(idx) { | |
235 | var computer = computers[idx] || {}; | |
236 | chrome.storage.local.set({'last_selected': idx}); | |
5b4622f6 | 237 | |
0e1cff99 MF |
238 | var form = $$('form[name=settings]'); |
239 | ||
240 | form.computer.value = computer['name'] || default_name; | |
241 | form.host.value = computer['host'] || default_host; | |
242 | form.port.value = computer['port'] || default_port; | |
243 | // We assume we only get called during init. | |
244 | paste_mac(); | |
245 | form.mac.value = computer['mac'] || default_mac; | |
246 | paste_mac(); | |
247 | paste_pass(); | |
248 | form.pass.value = computer['pass'] || default_pass; | |
249 | paste_pass(); | |
250 | } | |
251 | ||
252 | function load_settings() { | |
5b4622f6 | 253 | chrome.storage.local.get(settings_keys, function(settings) { |
e54503ce | 254 | set_theme(settings['theme'] || default_theme); |
0e1cff99 MF |
255 | if ('computers' in settings) { |
256 | computers = settings['computers'] || []; | |
257 | populate_computers(); | |
258 | load_computer(settings['last_selected'] || 0); | |
259 | } else { | |
260 | // Migrate old settings. | |
261 | chrome.storage.local.get(old_settings_keys, function(settings) { | |
262 | computers[0] = settings; | |
263 | populate_computers(); | |
264 | load_computer(0); | |
265 | store_settings(); | |
266 | chrome.storage.local.remove(old_settings_keys); | |
267 | }); | |
268 | } | |
5b4622f6 MF |
269 | }); |
270 | } | |
271 | ||
0e1cff99 MF |
272 | /* |
273 | * Update the currently selected computer then write out the whole thing. | |
274 | */ | |
5b4622f6 | 275 | function store_settings() { |
5b4622f6 MF |
276 | sync_mac(); |
277 | sync_pass(); | |
0e1cff99 MF |
278 | |
279 | var form = $$('form[name=settings]'); | |
280 | var select = $$('select[name=computer]'); | |
281 | var idx = select.selectedIndex; | |
282 | computers[idx] = { | |
283 | 'name': form.computer.value, | |
5b4622f6 MF |
284 | 'host': form.host.value, |
285 | 'mac': form.mac.value, | |
286 | 'pass': form.pass.value, | |
287 | 'port': form.port.value, | |
288 | }; | |
0e1cff99 MF |
289 | |
290 | chrome.storage.local.set({ | |
291 | 'computers': computers, | |
292 | }); | |
293 | } | |
294 | ||
295 | /* | |
296 | * If they try deleting all entries, make sure we re-add the default. | |
297 | */ | |
298 | function check_empty_computers(select) { | |
299 | if (select.length == 0) { | |
300 | var option = document.createElement('option'); | |
301 | option.text = 'Default'; | |
302 | select.add(option, 0); | |
303 | load_computer(0); | |
304 | } | |
305 | } | |
306 | ||
307 | /* | |
308 | * Fill out the computer drop down with existing config options. | |
309 | */ | |
310 | function populate_computers() { | |
311 | var select = $$('select[name=computer]'); | |
312 | select.length = 0; | |
313 | ||
314 | for (var i = 0; i < computers.length; i++) { | |
315 | var option = document.createElement('option'); | |
316 | var computer = computers[i] || {}; | |
317 | option.text = computer['name'] || default_name; | |
318 | select.add(option, i); | |
319 | } | |
320 | ||
321 | check_empty_computers(select); | |
322 | } | |
323 | ||
324 | /* | |
325 | * When a computer config is selected, load the corresponding settings. | |
326 | */ | |
327 | function select_computer() { | |
328 | load_computer($$('select[name=computer]').selectedIndex); | |
329 | } | |
330 | ||
331 | /* | |
332 | * Toggle between the computer drop down & new name input field. | |
333 | */ | |
334 | function toggle_add_fields(hide_obj, show_obj) { | |
335 | hide_obj.disabled = true; | |
336 | hide_obj.style.display = 'none'; | |
337 | show_obj.disabled = false; | |
338 | show_obj.style.display = 'inline'; | |
339 | show_obj.focus(); | |
340 | } | |
341 | ||
342 | /* | |
343 | * Del curent slected computer | |
344 | */ | |
345 | function del_computer() { | |
346 | var select = $$('select[name=computer]'); | |
347 | ||
348 | var idx = select.selectedIndex; | |
349 | // Delete the currently selected index. | |
350 | computers.splice(idx, 1); | |
351 | select.remove(idx); | |
352 | ||
353 | // Make sure the list isn't entirely empty now. | |
354 | check_empty_computers(select); | |
355 | ||
356 | // Load/select the next entry in the list. | |
357 | if (idx == select.length && idx > 0) | |
358 | --idx; | |
359 | load_computer(idx); | |
360 | ||
361 | store_settings(); | |
362 | } | |
363 | ||
364 | ||
365 | /* | |
366 | * Shows an input box to enter new computer name. | |
367 | */ | |
368 | function add_computer_start() { | |
369 | var select = $$('select[name=computer]'); | |
370 | var text = $$('input[name=computer_name]'); | |
371 | ||
372 | // If the box isn't visible, show it. Otherwise, they want to | |
373 | // actually add the current settings so create a new entry. | |
374 | if (select.style.display == 'none') | |
375 | add_computer(); | |
376 | else | |
377 | toggle_add_fields(select, text); | |
378 | } | |
379 | ||
380 | /* | |
381 | * Wait for the enter key in the add text field. | |
382 | */ | |
383 | function add_computer_check(e) { | |
384 | if (e.key == 'Enter') { | |
385 | add_computer(); | |
386 | e.preventDefault(); | |
387 | } | |
388 | } | |
389 | ||
390 | /* | |
391 | * Try to actually create a new computer entry. | |
392 | */ | |
393 | function add_computer() { | |
394 | var select = $$('select[name=computer]'); | |
395 | var form = $$('form[name=settings]'); | |
396 | var text = $$('input[name=computer_name]'); | |
397 | ||
398 | var name = text.value.trim(); | |
399 | // Make sure they've added a valid name first. | |
400 | // Options fields don't allow leading/trailing whitespace. | |
401 | if (name == '') { | |
402 | text.value = ''; | |
403 | toggle_add_fields(text, select); | |
404 | return; | |
405 | } | |
406 | ||
407 | // Make sure they don't try to add a duplicate name. | |
408 | for (var i = 0; i < select.length; ++i) { | |
409 | if (select.options[i].value == name) { | |
410 | popup_msg(text, 'ERROR: computer name already exists!'); | |
411 | return; | |
412 | } | |
413 | } | |
414 | text.value = ''; | |
415 | ||
416 | var option = document.createElement('option'); | |
417 | option.text = name; | |
418 | select.add(option, -1); | |
419 | ||
420 | // Let the load_computer logic fill out the default values for us. | |
421 | var idx = select.length - 1; | |
422 | computers[idx] = { | |
423 | 'name': name, | |
424 | }; | |
425 | load_computer(select.length - 1); | |
426 | ||
427 | toggle_add_fields(text, select); | |
428 | ||
429 | store_settings(); | |
5b4622f6 MF |
430 | } |
431 | ||
432 | /* | |
433 | * Startup. | |
434 | */ | |
435 | window.onload = function() { | |
0e1cff99 MF |
436 | $$('input[name=send]').onclick = send; |
437 | $$('select[name=computer]').onchange = select_computer; | |
5b4622f6 MF |
438 | $$('a[name=mac-paste]').onclick = paste_mac; |
439 | $$('a[name=pass-paste]').onclick = paste_pass; | |
0e1cff99 MF |
440 | $$('input[name=del_computer]').onclick = del_computer; |
441 | $$('input[name=add_computer]').onclick = add_computer_start; | |
442 | $$('input[name=computer_name]').onkeypress = add_computer_check; | |
6293bf85 | 443 | $$('input[name=theme]').onclick = toggle_theme; |
5b4622f6 MF |
444 | |
445 | load_settings(); | |
446 | }; |