#!/usr/bin/env python
#
# Wake On Lan - Magic Packet 0.1
# Copyright (C) 2009 Damian Pasternok <my_forename at pasternok.org>
# This code is free software under the GPLv3+.
# For more information see <http://gnu.org/licenses/gpl.html>.
#
# Used some parts of code of:
# Brian McErlean <b_mcerlean@yahoo.com>
# http://mail.python.org/pipermail/python-list/2002-August/159592.html
# Marc Balmer <marc@msys.ch>
# http://gsd.di.uminho.pt/jpo/software/wakeonlan/mini-howto/wolpython.txt

### config
use_remote_tld_list = "no" # if set to "no" tld_local will be used
tld_remote = "http://data.iana.org/TLD/tlds-alpha-by-domain.txt"

### do not change tld_local unless you know what you are doing
# tld_local (Version 2017011600, Last Updated Mon Jan 16 07:07:01 2017 UTC)
tld_local = r"""aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|
ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|
adult|ae|aeg|aero|aetna|af|afamilycompany|afl|ag|agakhan|agency|ai|aig|
aigo|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|
allstate|ally|alsace|alstom|am|americanexpress|americanfamily|amex|amfam|
amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|
aq|aquarelle|ar|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|
at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|
avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|
bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|
bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|
berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|
biz|bj|black|blackfriday|blanco|blockbuster|blog|bloomberg|blue|bm|bms|
bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|
boots|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|
broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|
buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|
cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|
career|careers|cars|cartier|casa|case|caseih|cash|casino|cat|catering|catholic|
cba|cbn|cbre|cbs|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|
chase|chat|cheap|chintai|chloe|christmas|chrome|chrysler|church|ci|cipriani|
circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|
clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|
college|cologne|com|comcast|commbank|community|company|compare|computer|
comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|
cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|
cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|
cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|
deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|
design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|
dish|diy|dj|dk|dm|dnp|do|docs|doctor|dodge|dog|doha|domains|dot|download|
drive|dtv|dubai|duck|dunlop|duns|dupont|durban|dvag|dvr|dz|earth|eat|ec|
eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|
enterprises|epost|epson|equipment|er|ericsson|erni|es|esq|estate|esurance|
et|eu|eurovision|eus|events|everbank|exchange|expert|exposed|express|extraspace|
fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|
feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|
fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|
florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|
forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|
fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|
game|games|gap|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|
gh|gi|gift|gifts|gives|giving|gl|glade|glass|gle|global|globo|gm|gmail|
gmbh|gmo|gmx|gn|godaddy|gold|goldpoint|golf|goo|goodhands|goodyear|goog|
google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|group|
gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|
haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|
hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|
homegoods|homes|homesense|honda|honeywell|horse|hospital|host|hosting|hot|
hoteles|hotmail|house|how|hr|hsbc|ht|htc|hu|hughes|hyatt|hyundai|ibm|icbc|
ice|icu|id|ie|ieee|ifm|ikano|il|im|imamat|imdb|immo|immobilien|in|industries|
infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|
intuit|investments|io|ipiranga|iq|ir|irish|is|iselect|ismaili|ist|istanbul|
it|itau|itv|iveco|iwc|jaguar|java|jcb|jcp|je|jeep|jetzt|jewelry|jio|jlc|
jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|
kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|
kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|
kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|ladbrokes|lamborghini|lamer|lancaster|
lancia|lancome|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|
lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|liaison|lidl|life|
lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|
link|lipsy|live|living|lixil|lk|loan|loans|locker|locus|loft|lol|london|
lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|lupin|luxe|
luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|market|
marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mcd|mcdonalds|
mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|metlife|
mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|
mn|mo|mobi|mobile|mobily|moda|moe|moi|mom|monash|money|monster|montblanc|
mopar|mormon|mortgage|moscow|moto|motorcycles|mov|movie|movistar|mp|mq|
mr|ms|msd|mt|mtn|mtpc|mtr|mu|museum|mutual|mv|mw|mx|my|mz|na|nab|nadex|
nagoya|name|nationwide|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|
neustar|new|newholland|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|
nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|
now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|off|office|okinawa|
olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onl|online|onyourside|
ooo|open|oracle|orange|org|organic|orientexpress|origins|osaka|otsuka|ott|
ovh|pa|page|pamperedchef|panasonic|panerai|paris|pars|partners|parts|party|
passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|philips|phone|photo|
photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|
pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|
poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|
prof|progressive|promo|properties|property|protection|pru|prudential|ps|
pt|pub|pw|pwc|py|qa|qpon|quebec|quest|qvc|racing|radio|raid|re|read|realestate|
realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|
reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|
reviews|rexroth|rich|richardli|ricoh|rightathome|ril|rio|rip|rmit|ro|rocher|
rocks|rodeo|rogers|room|rs|rsvp|ru|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|
safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|
sap|sapo|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|
school|schule|schwarz|science|scjohnson|scor|scot|sd|se|seat|secure|security|
seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|
shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|
si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|
sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|
soy|space|spiegel|spot|spreadbetting|sr|srl|srt|st|stada|staples|star|starhub|
statebank|statefarm|statoil|stc|stcgroup|stockholm|storage|store|stream|
studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|
sv|swatch|swiftcover|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|
talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|
tech|technology|tel|telecity|telefonica|temasek|tennis|teva|tf|tg|th|thd|
theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|
tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|
tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|
travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|
ubs|uconnect|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|
vana|vanguard|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|
video|vig|viking|villas|vin|vip|virgin|visa|vision|vista|vistaprint|viva|
vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|
wales|walmart|walter|wang|wanggou|warman|watch|watches|weather|weatherchannel|
webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|
win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|
ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|
xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--3oq18vl8pn36a|xn--3pxu8k|
xn--42c2d9a|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--54b7fta0cc|xn--55qw42g|
xn--55qx5d|xn--5su34j936bgsg|xn--5tzm5g|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|
xn--80ao21a|xn--80aqecdr1a|xn--80asehdb|xn--80aswg|xn--8y0a063a|xn--90a3ac|
xn--90ae|xn--90ais|xn--9dbq2a|xn--9et52u|xn--9krt00a|xn--b4w605ferd|xn--bck1b9a5dre4c|
xn--c1avg|xn--c2br7g|xn--cck2b3b|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|
xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--e1a4c|xn--eckvdtc9d|xn--efvy88h|
xn--estv75g|xn--fct429k|xn--fhbei|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|
xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--fzys8d69uvgm|
xn--g2xx48c|xn--gckr3f0f|xn--gecrj9c|xn--gk3at1e|xn--h2brj9c|xn--hxt814e|
xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1aef|xn--j1amh|xn--j6w193g|
xn--jlq61u9w7b|xn--jvr189m|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kpu716f|
xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb9awbf|xn--mgba3a3ejt|xn--mgba3a4f16a|
xn--mgba7c0bbn0a|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbayh7gpa|xn--mgbb9fbpob|
xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgbca7dzdo|xn--mgberp4a5d4ar|xn--mgbi4ecexp|
xn--mgbpl2fh|xn--mgbt3dhd|xn--mgbtx2b|xn--mgbx4cd0ab|xn--mix891f|xn--mk1bu44c|
xn--mxtq1m|xn--ngbc5azd|xn--ngbe9e0a|xn--node|xn--nqv7f|xn--nqv7fs00ema|
xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pbt977c|xn--pgbs0dh|
xn--pssy2u|xn--q9jyb4c|xn--qcka1pmc|xn--qxam|xn--rhqv96g|xn--rovu88b|xn--s9brj9c|
xn--ses554g|xn--t60b56a|xn--tckwe|xn--tiq49xqyj|xn--unup4y|xn--vermgensberater-ctb|
xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--w4r85el8fhu5dnra|xn--w4rs40l|
xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|
xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b|xperia|xxx|xyz|yachts|yahoo|yamaxun|
yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|
zip|zippo|zm|zone|zuerich|zw"""

### code
__author__="Damian Pasternok <my_forename at pasternok.org>"
__program__="Wake On Lan - Magic Packet"
__version__="0.1"
__date__ ="$2009-07-01 16:57:06$"

import pygtk
pygtk.require('2.0')
import gtk
import re
import struct
import socket
import sys
import os
import urllib

tld_local = tld_local.replace("\n", "")

class TLDParser:
    def __init__(self, tld):
        self.url = urllib.urlopen(tld)
        self.html = self.url.readlines()
        # no version header (probably 404 or something)
        if not re.match("^#.*$", self.html[0]):
            self.err = 1
        else:
            self.err = 0

    def get_header(self):
        self.header = self.html[0]
        return self.header

    def parse_tlds(self):
        self.tlds = ""
        self.tlds = self.html
        self.tlds.pop(0)    # remove header
        i = 0
        for domain in self.tlds:
            self.tlds[i] = domain.lower()
            self.tlds[i] = self.tlds[i].replace("\n", "|")
            i += 1
        # remove "|" from last item
        self.tlds[len(self.tlds)-1] = self.tlds[len(self.tlds)-1].replace("|",
                                                                            "")
        return self.tlds

    # args: type
    #   regexp   - used by 'use_remote_tld_list = "yes"'
    #   variable - used by '--update-tlds' (create new tld_local variable)
    def get_tld(self, type):
        self.tld_local = ""
        self.tld_local_list = []    # or list()
        if type == "regexp":
            for domain in self.parse_tlds():
                self.tld_local += domain
            self.tlds = self.tlds[:-1]  # remove last "|"
            return self.tld_local
        elif type == "variable":
            self.tld_local_list.append(self.get_header())
            self.tld_local_list[0] = self.tld_local_list[0].replace("# ",
                                                            "# tld_local (")
            self.tld_local_list[0] = self.tld_local_list[0].replace("\n", ")\n")
            self.tld_local_list.append("tld_local = r\"\"\"")
            # create fancy variable string fitted to vt100 based terminals
            i = 1
            for domain in self.parse_tlds():
                if len(self.tld_local_list[i]) > 70:
                    self.tld_local_list[i] += "\n"
                    self.tld_local_list.append("")
                    i += 1
                    self.tld_local_list[i] += domain
                    continue
                else:
                    self.tld_local_list[i] += domain
            self.tld_local_list[i] += "\"\"\""

            for lines in self.tld_local_list:
                self.tld_local += lines
            return self.tld_local

class Group:
    def __init__(self, l, size):
        self.size = size
        self.l = l

    def __getitem__(self, group):
        idx = group * self.size
        if idx > len(self.l):
            raise IndexError("Out of range")
        return self.l[idx:idx+self.size]

class MagicPacket:
    # calculate broadcast
    def get_broadcast(self, inetaddr, submask):
        try:
            self.inetaddr = socket.gethostbyname(inetaddr)
        except:
            self.err = 1
            return -1
        self.inetaddr = self.inetaddr.split('.')
        self.submask = submask.split('.')
        self.broadcast = ""
        self.broadcast_list = []
        i = 0
        for sbyte in self.submask:
            self.submask[i] = int(sbyte) ^ 0xff # xor the binary subnet mask
            self.broadcast_list.append(int(self.inetaddr[i]) | self.submask[i])
            self.broadcast += str(self.broadcast_list[i])
            self.broadcast += "."
            i += 1
        self.broadcast = self.broadcast[:-1]
        return self.broadcast

    def mac_into_list(self, macaddr):
        # check MAC format
        self.macaddr = macaddr.split(':')
        if len(self.macaddr) != 6:
            self.macaddr = macaddr.split('-')
            if len(self.macaddr) != 6:
                self.macaddr = []
                # group MAC bytes into list
                for pair in Group(macaddr, 2):
                    self.macaddr.append(pair)
                self.macaddr.pop()  # remove last empty item from list
        i = 0
        for pair in self.macaddr:   # all letters in uppercase
            self.macaddr[i] = pair.upper()
            i += 1
        return self.macaddr

    def send_magic_packet(self, macaddr, inetaddr, submask, remport):
        self.macaddr = self.mac_into_list(macaddr)
        # prepare magic packet
        self.hw_addr = struct.pack('BBBBBB', int(self.macaddr[0], 16),
            int(self.macaddr[1], 16),
            int(self.macaddr[2], 16),
            int(self.macaddr[3], 16),
            int(self.macaddr[4], 16),
            int(self.macaddr[5], 16))

        self.msg = '\xff' * 6 + self.hw_addr * 16
        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.err = 0    # set to 1 if failed to resolve hostname
        if inetaddr:
            self.broadcast = self.get_broadcast(inetaddr, submask)
            if not self.err:
                self.s.sendto(self.msg, (self.broadcast, int(remport)))
        else:
            self.broadcast = "255.255.255.255"
            self.s.sendto(self.msg, (self.broadcast, int(remport)))
        self.s.close()
        return self.broadcast   # returns broadcast address

class Errors:
    errors = ""
    def show_errors(self):
        dialog = gtk.MessageDialog(self.parent_window,
                    gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                    "Unable to send Magic Packet due to following errors: %s"
                    % self.errors)
        dialog.set_title(__program__)
        dialog.run()
        dialog.destroy()

    def combine_errors(self, error_message):
        self.errors += error_message

    def check_mac(self, macaddr):
        self.res = re.match(r"^[\dA-Fa-f]{2}(-[\dA-Fa-f]{2}){5}$|"
                            "^[\dA-Fa-f]{2}(:[\dA-Fa-f]{2}){5}$|"
                            "^[\dA-Fa-f]{2}([\dA-Fa-f]{2}){5}$", macaddr)
        if self.res:
            return 0
        else:
            self.combine_errors("\n- wrong MAC - correct MAC formats are "
                                "(case-insensitive):\n  00-74-F1-37-4B-59, "
                                "00:74:F1:37:4B:59 or 0074F1374B59")
            
    def check_inetaddr(self, inetaddr):
        if use_remote_tld_list == "yes":
            try:
                parser = TLDParser(tld_remote)
                tld = parser.get_tld("regexp")
                if parser.err:  # wrong tld url
                    tld = tld_local
                    IndexErrors()
            except:
                dialog = gtk.MessageDialog(self.parent_window,
                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                        gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
                        "Unable to get the list of Top-level Domains.\n"
                        "Using Top-level Domains from tld_local.")
                dialog.set_title(__program__)
                dialog.run()
                dialog.destroy()
                tld = tld_local
        elif use_remote_tld_list == "no":
            tld = tld_local

        self.prog = re.compile(r"^((\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])" # ip
                    "(\.(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])){3})|"
                    "([A-Za-z\d]+((\-|_){0,1}[A-Za-z\d])*"               # fqdn
                    "(\.[A-Za-z\d]+((\-|_){0,1}[A-Za-z\d])*)*"
                    "\.(%s))$" % tld)
        self.res = self.prog.match(inetaddr)
        if self.res:
            return 0
        else:
            self.combine_errors("\n- wrong internet address - correct formats "
                                "are:\n  IP address (e.g. 192.168.0.1) or "
                                "fully qualified domain\n  name (e.g. "
                                "bgp-0-2.br0.nyc1.isp.net)")

    def check_submask(self, submask):
        self.res = re.match(r"^(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])"
                    "(\.(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])){3}$", submask)
        if self.res:
            return 0
        else:
            self.combine_errors("\n- wrong subnet mask - correct format is e.g."
                                " 255.255.255.0")

    def check_remport(self, remport):
        self.res = re.match(r"^(\d{1,4}|[1-5]\d{4}|6([0-4]\d{3}|"
                            "5[0-4]\d{2}|55[0-2]\d{1}|553[0-5]))$", remport)
        if self.res:
            return 0
        else:
            self.combine_errors("\n- wrong remote port - use one from range "
                                "0-65535\n  (typically 0, 7 or 9)")

    def __init__(self, window):
        self.parent_window = window

class CommandLine:
    def version(self):
        version_message = r"""%s %s
Copyright (C) 2009 %s
%s comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of %s
under the terms of the GNU General Public License version 3 or later.
For more information see <http://gnu.org/licenses/gpl.html>.""" % (
__program__, __version__, __author__, __program__, __program__)
        return version_message

    def help(self):
        help_message = r"""Usage: %s [OPTION]...

Available options:

--update-tlds                   try to download list of Top-level Domains from
                                URL defined in tld_remote variable and update
                                tld_local variable in script
--help                          display this help and exit
--version                       output version information and exit
""" % sys.argv[0]
        return help_message

class MainWindow:
    def destroy(self, widget, data=None):
        gtk.main_quit()
    
    def wake_me(self, button, data=None):
        self.check = Errors(self.window)
        self.check.check_mac(self.edit_macaddr.get_text())
        if self.select_target.get_active_text() == "Internet":
            self.check.check_inetaddr(self.edit_inetaddr.get_text())
            self.check.check_submask(self.edit_submask.get_text())
        self.check.check_remport(self.edit_remport.get_text())

        if len(self.check.errors):
            self.check.show_errors()
        else:
            self.send = MagicPacket()
            broadcast = self.send.send_magic_packet(
                                                self.edit_macaddr.get_text(),
                                                self.edit_inetaddr.get_text(),
                                                self.edit_submask.get_text(),
                                                self.edit_remport.get_text())
            if self.send.err:
                self.statusbar.push(0,
                    "Unable to send Magic Packet. Can't resolve hostname...")
                return -1
            fancy_mac = ""
            for mbyte in self.send.mac_into_list(self.edit_macaddr.get_text()):
                fancy_mac += mbyte
                fancy_mac += ":"
            fancy_mac = fancy_mac[:-1]  # remove last ":"
            self.statusbar.push(0,
                "Magic Packet sent to %s (%s)..." % (broadcast, fancy_mac))

    def show_about(self, widget, string):
        dialog = gtk.MessageDialog(self.window,
                    gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK, string)
        dialog.set_title(__program__)
        dialog.run()
        dialog.destroy()

    def select_send_option(self, button, data=None):
        if self.select_target.get_active_text() == "Internet":
            self.edit_inetaddr.set_sensitive(True)
            self.edit_submask.set_sensitive(True)
        elif self.select_target.get_active_text() == "Local Subnet":
            self.edit_inetaddr.set_text("")
            self.edit_submask.set_text("")
            self.edit_inetaddr.set_sensitive(False)
            self.edit_submask.set_sensitive(False)

    def __init__(self):
        # main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.destroy)
        self.window.set_title(__program__)
        self.window.set_position(gtk.WIN_POS_CENTER)

        # root vbox
        self.root_vbox = gtk.VBox(False, 0)

        # menu
        self.menu = gtk.Menu()
        self.about_item = gtk.MenuItem("_About...")
        self.menu.append(self.about_item)
        self.cmd = CommandLine()
        self.about_item.connect("activate", self.show_about, self.cmd.version())
        self.root_menu = gtk.MenuItem("_Help")
        self.root_menu.set_right_justified(True)
        self.root_menu.set_submenu(self.menu)
        self.menu_bar = gtk.MenuBar()
        self.menu_bar.append(self.root_menu)
        self.root_vbox.pack_start(self.menu_bar, False, False, 0)
        
        # frame
        self.frame = gtk.Frame("  %s" % __program__)
        self.window.add(self.root_vbox)
        self.root_vbox.pack_start(self.frame, False, False, 0)

        # statusbar
        self.statusbar = gtk.Statusbar()
        self.root_vbox.pack_end(self.statusbar, False, False, 0)

        # labels
        self.label1 = gtk.Label("Mac Address")
        self.label1.set_alignment(1, 0.5)
        self.label2 = gtk.Label("Internet Address")
        self.label2.set_alignment(1, 0.5)
        self.label3 = gtk.Label("Subnet Mask")
        self.label3.set_alignment(1, 0.5)
        self.label4 = gtk.Label("Send options")
        self.label4.set_alignment(1, 0.5)
        self.label5 = gtk.Label("Remote Port Number")
        self.label5.set_alignment(1, 0.5)

        # edit boxes & selection
        self.edit_macaddr = gtk.Entry()
        self.edit_inetaddr = gtk.Entry()
        self.edit_submask = gtk.Entry()
        self.select_target = gtk.combo_box_new_text()
        self.select_target.append_text("Local Subnet")
        self.select_target.append_text("Internet")
        self.select_target.set_active(0)
        self.edit_remport = gtk.Entry()
        # combobox action
        self.select_target.connect("changed", self.select_send_option)
        # hide by default "internet address" and "subnet mask"
        # (they are not required when send local magic packet)
        self.edit_inetaddr.set_sensitive(False)
        self.edit_submask.set_sensitive(False)

        # default values
        self.edit_macaddr.set_text("00-74-F1-37-4B-59")
        self.edit_remport.set_text("9")

        # table
        self.table = gtk.Table(5, 2)
        self.table.set_row_spacings(4)
        self.table.set_col_spacings(10)
        self.table.attach(self.label1, 0, 1, 0, 1)
        self.table.attach(self.label2, 0, 1, 1, 2)
        self.table.attach(self.label3, 0, 1, 2, 3)
        self.table.attach(self.label4, 0, 1, 3, 4)
        self.table.attach(self.label5, 0, 1, 4, 5)
        self.table.attach(self.edit_macaddr, 1, 2, 0, 1)
        self.table.attach(self.edit_inetaddr, 1, 2, 1, 2)
        self.table.attach(self.edit_submask, 1, 2, 2, 3)
        self.table.attach(self.select_target, 1, 2, 3, 4)
        self.table.attach(self.edit_remport, 1, 2, 4, 5)

        # vertical box
        self.vbox = gtk.VBox(False, 8)
        self.vbox.set_border_width(16)
        self.frame.add(self.vbox)

        # add table to vbox
        self.vbox.pack_start(self.table, False, False, 0)
        # add separator to vbox
        self.vbox.pack_start(gtk.HSeparator(), False, False, 0)

        # wake button
        self.button = gtk.Button("Wake Me Up")
        self.button.connect("clicked", self.wake_me)

        self.vbox.pack_end(self.button, False, False, 0)
        self.window.show_all()

def main():
    cmd = CommandLine()
    if len(sys.argv) == 1:
        MainWindow()
        gtk.main()
    elif len(sys.argv) == 2:
        if sys.argv[1] == "--update-tlds":
            try:
                parser = TLDParser(tld_remote)
                if parser.err:  # wrong tld url
                    IndexErrors()
                new_tld_local = parser.get_tld("variable")
            except:
                print "Unable to get the list of Top-level Domains.\nExiting."
                return -1
            f = open(sys.argv[0], "r")
            script = f.read()
            f.close()
            script = re.sub("# tld_local(.|\n)*[\w]{2}\"\"\"",
                new_tld_local, script, re.M)
            os.remove(sys.argv[0])
            f = open(sys.argv[0], "w")
            os.chmod(sys.argv[0], 0755)
            f.write(script)
            f.close()
            print "Top-level Domains updated successfully."
            while 1:
                print "Do you want to run the main program? [n]",
                key = raw_input()
                if key == "y" or key == "Y":
                    MainWindow()
                    gtk.main()
                    break
                elif key == "n" or key == "N" or key == "":
                    break
        elif sys.argv[1] == "--version":
            print cmd.version()
        else:
            print cmd.help()
    else:
        print cmd.help()
    return 0

if __name__ == "__main__":
    main()