pax_global_header00006660000000000000000000000064132317266340014521gustar00rootroot0000000000000052 comment=429e6e9b894f1d91e93aa643925908fa58b2a41a PyNamecheap-0.0.3/000077500000000000000000000000001323172663400137135ustar00rootroot00000000000000PyNamecheap-0.0.3/.gitignore000066400000000000000000000005151323172663400157040ustar00rootroot00000000000000env credentials.py .DS_Store *.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject PyNamecheap-0.0.3/LICENSE.txt000066400000000000000000000020701323172663400155350ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Bemmu Sepponen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyNamecheap-0.0.3/README.md000066400000000000000000000124711323172663400151770ustar00rootroot00000000000000Namecheap API for Python =========== PyNamecheap is a Namecheap API client in Python. API itself is documented at This client supports: - Registering a domain - Checking domain name availability - Listing domains you have registered - Getting contact information for a domain - Setting DNS info to default values - Set DNS host records ### Installation Thanks to @inomoz, as simple as: ``` pip install PyNamecheap ``` ### How to sign up to start using the API The API has two environments, production and sandbox. Since this API will spend real money when registering domains, start with the sandbox by going to and creating an account. Accounts between production and sandbox are different, so even if you already have a Namecheap account you will need a new one. After you have an account, go to "Profile". ![Profile](img/profile.png "Profile") From there, select the "Profile" menu again, then "Tools". Scroll to the bottom of the page to the "Business & Dev Tools" and select "Manage" on the "Namecheap API Access" section. ![API menu](img/apimenu.png "API menu") You'll get to your credentials page. From here you need to take note of your api key, username and add your IP to the whitelist of IP addresses that are allowed to access the account. You can check your public IP by searching "what is my ip" on Google and add it here. It might take some time before it actually starts working, so don't panic if API access doesn't work at first. ![Credentials](img/credentials.png "Credentials") ### How to access the API from Python Copy namecheap.py to your project. In Python you can access the API as follows: from namecheap import Api api = Api(username, api_key, username, ip_address, sandbox = True) The fields are the ones which appear in the credentials screen above. The username appears twice, because you might be acting on behalf of someone else. ### Registering a domain name using the API Unfortunately you need a bunch of contact details to register a domain, so it is not as easy as just providing the domain name. In the sandbox, the following contact details are acceptable. Trickiest field is the phone number, which has to be formatted as shown. api.domains_create( DomainName = 'registeringadomainthroughtheapiwow.com', FirstName = 'Jack', LastName = 'Trotter', Address1 = 'Ridiculously Big Mansion, Yellow Brick Road', City = 'Tokushima', StateProvince = 'Tokushima', PostalCode = '771-0144', Country = 'Japan', Phone = '+81.123123123', EmailAddress = 'jack.trotter@example.com' ) This call should succeed in the sandbox, but if you use the API to check whether this domain is available after registering it, the availability will not change. This is normal. ### How to check if a domain name is available The domains_check method returns True if the domain is available. api.domains_check(domain_name) You can also pass a list of domain names, in which case it does a batch check for all and returns a dictionary of the answers. You should probably not be writing a mass domain checking tool using this, it is intended to be used before registering a domain. ### CLI tool usage First, you need to edit `./credentials.py` file to provide API access for the script. The example is following: #!/usr/bin/env python api_key = '0123456789abcdef0123456789abcdef' username = 'myusername' ip_address = '10.0.0.1' Then you just call the script with desired arguments: ./namecheap-api-cli --domain example.org --list ./namecheap-api-cli --domain example.org --add --type "A" --name "test" --address "127.0.0.1" --ttl 300 ./namecheap-api-cli --domain example.org --add --type "CNAME" --name "alias-of-test" --address "test.example.org." --ttl 1800 ./namecheap-api-cli --domain example.org --delete --type "CNAME" --name "alias-of-test" --address "test.example.org." ./namecheap-api-cli --domain example.org --delete --type "A" --name "test" --address "127.0.0.1" ### Basic host records management code Here's the example of simple DNS records management script: #!/usr/bin/env python """ Define variables regarding to your API account: - api_key - username - ip_address """ api = Api(username, api_key, username, ip_address, sandbox=False) domain = "example.org" # list domain records api.domains_dns_getHosts(domain) record = { # required "Type": "A", "Name": "test1", "Address": "127.0.0.1", # optional "TTL": "1800", "MXPref": "10" } # add A "test1" record pointing to 127.0.0.1 api.domains_dns_addHost(domain, record) # delete record we just created, # selecting it by Name, Type and Address values api.domains_dns_delHost(domain, record) ### Retry mechanism Sometimes you could face wrong API responses, which are related to server-side errors. Thanks to @gstein, we implemented retry mechanism, one could enable it by adding 2 parameters to Api instance: ``` api = Api(username, api_key, username, ip_address, sandbox=False, attempts_count=3, attempts_delay=0.1) ``` Values of 2 or 3 should do the thing. ### More Look at namecheap_tests.py to see more examples of things you can do. PyNamecheap-0.0.3/img/000077500000000000000000000000001323172663400144675ustar00rootroot00000000000000PyNamecheap-0.0.3/img/apimenu.png000066400000000000000000001206161323172663400166410ustar00rootroot00000000000000PNG  IHDR} ggAMA a@IDATxx\Gv5rD"$)9M3]kߵߏ׏xrF)YD9y&0M ݸn{֭Ω*&'l?ݐ@__?/^+nzMPJ@ (%P3%`8yVJ@ (%PSrPw1\<EXBCcN>sB6ƛشq#VX7x K.1-\>أ`ٲO^*EhX(~;moģ7hoo/.\}gΞΝh_ħA (%PJ@  %=#؈V[(,,:WSO`L\<SaxҰR#<"jjjwLܴ}?ċ/oy7oġCP$E \.iPJ@ (%P7C@EPc:t䘾_u444@#232,V;iaP_/0.]8+B b{ >>aaU+/GXSR(sF֎_6tRDGG#!!XﺛVJ@ (%u6=i>;~⤸U>իW/?)l,k_~ٚK_PpvޫC~y7qysEGFf=/o/dg/îݻEٌ\$ 35% FHҝ;.8Lff|7%PJ@ (Wl\_Mܦ{vq3\~sNXef^Xdql^n^qL_Ќ 2c蚹mR%%%aAbD,b!;gOo/{/DOOOXj%jpA2oL9!8KVVR5ˤJ@ (%Ps_K`b} ٝio]}\ (%PJ@ 85K >ҥPPJ@ (%Pw7kD_rrMd便&Γh6PJ@ (%&kD_t&9u d%PJ@ (%n׈SDI9O<>OrPJ@ (%PJ`}2'5< 7eOw;S7v@`pHD-?8uI"ގN&F{wUbĥnS/ݮPJ@ (%k\Q0ӌohoĎ;gX0 ʗf_0tuu!,, 7m39o F06: £F>ݵ 'wR A)V69!`qXވ.V5K3lj,,'%)"kk""vp\/8 |T>:@Gllb*V/[Gwo%PJ@ (%HQ`hmkCaq1N9ψg,2;gWw=aagf$v6'oʬP0n\jcO<߆:ؽ}e`?BCڂ1 mwk[nqXawtt!\w_?|| !v475w+VӈMGme!<<Ї{>Ddx(z:DaQbl۶e蕼 tPJ@ (%PJ`>5*ѭsƍXr%eN+,* 3DXh%:8X_'U!s*f`%hmD|t^}ؼkzF!:24">]{zݳX )F%6<ÏB_>N\jK/=8 fog3AfZo2y]TԵ"kZ$}> y[/<-S!F՞Xb oΕɄ73V[ (%PJ@ (%0NeJpNZ9r*+ɚ}f%ha-5fa-H[VeC D'>t'RĦUؿk'.GC>>`u.ǎ'q##;-\LbJZ9*׺89 }kT6 sQJ. Bf ]O>ށ3I ;i%x&%m!"}{Nv^aIxpR T4}ƣPJ@ (%P3$p@FӍ/P >>>f)EuKZTlQVx};OyF=*?u6@&%ˎV`9rr 9HojL+dgd1>W̖J)@}]5?.RgiqǴr'= Uf:mjEKgt!)o{pP;t?PJ@ (%?.,CƾfLn)R Y酦KxPf,z̧l,oގF[LGhjkPVY#ݟmC}}#ы1 yB AWkef~2jiGUW,&mGKGHTw~*2P,aю %5jE[O,`j%.L>'i򤯪kx0=#S ݠPJ@ (%i»`t8TN]\!=-=](rnybhy9gƱyL8c,o) d]+ΟQ,tI 'aȺV\E,U&w tyJm-mnooK /|(%PJ@ (Y!0cїw$<>.&S¢bhoV$kwMq?t1{}m?vc;hgb0wƩw>.TטxPJ@ (% k-FSZZffRY!-52k豄0kpP\*o,]ܯA (%PJ@ (%\O`Fɱ,wu2Nf CfF:-5V6 DD,&>PJ@ (%PJ%0cg_gg'N<!&0سPJ@ (%PJp賒jU0ttL2-ֹPJ@ (%P%rg%bg}[[ (%PJ@ (% k3ի(%PJ@ (% @K(%PJ@ (%f"UJ@ (%PJ@ *dPJ@ (%PJ`-z]%PJ@ (% @K(%PJ@ (%f"UJ`oƘ~ն[cll| ; 03_Cfؙ5gbp>Y<+go^ϝ+CM,o]vo322b 'uB{{;f튀eޭ;;;%zeg#>uuu38YQXQg*7Uu&ﶉDo}w4J`^`/\r=;Iv.cڬ:v9+(ܻou7w}%%ر s{} 0y,o'9|g^8Çv ;^`8u vލJLmJ33*fOVJ&6#;6f7/QXDasՕA4 o6 XBtxxxϟaȆAii)JJ.7(x6'iVKC\'Liii0ikkkG\ic౴`0jruM fZبS}BiiφŋڍIv6aԊLy@̴77sAu V/>h]P3zi] ~+BJCGBsR!9=k3K*++]"Mku-5E1gx***Doi`.n;urΙ4Y ERRa-vq,]]8. Tg=#Xdg"ɿAEl#cﲲM}r&eY%", XTTrMf\ÆGyO<8P(eL.[SH9&W+0}`_^eq?[>/7딲g1;M+~ఔM "?gDP=f'>{dŴ8êkr¹!CKaf?HMM=yLȊ|U𸊊J, 3XxX\f<_$=| x{g+Ke (|,6//>#y>!!p6'6 u!G600l8xAeJlliؤ񛛓saοȋ">==KIf 8 e Csn) (LZZRɸ$]8pF4>ia{}I(gln}]RLo6 3d*Id&Mr/-!"im4"|ilg0)|Lc ZacbQ#6ĉ`Y`cgƍiFӘT\gf{^z62ׯ_+؎*q']S)~R^䞲,bJ6 'We>6X&L'||}IrOj)IҘfNwE)l Kc>"<4EK{56J}k`ΝUkm =^t{!ϝ{a`Yb<;ۤ6<㦱WBTt(ֈФpD#~do_Vٰ×.Ȇ8-fVScV<'@V`Zx2`1'Jk[9vx۷OzcPcCR:<,ca)3s/F^TGŽziO|̆6;XNdxy>rF6ݦM$_ͦacv x B>i[Xy#orL@6&g<mrӢˎ(1R_@.6``GϟIB 11 -0a[<ۜogCμg΁  /7{'?XO'Cҙ[:Kk>6E9spBZ3OKgY|*mYG60-<ywY@K O_9zi@`jZLlGZic3)2-.[TRNƌiTd9Ɣ '98 xDêW;XօVlr@g&|-"j覘&6rE(+$-\_~Gdi:xKǝ"6(*NH+E>ѥr"@ʼnI@+~zt?e7ESFQ3l]xohi:=otWuVZ{e:fXba#t}ʫ9οu/2^&w\avA|yVϥ(-M+s]Y`s/ayH,d2-RT iZ(nhU(5)$Nn5TfpEQqM:ZVJN _RpXpMe b)Kp-; R*2n{GgqlWWנG,i|&Xnxyhc`K!,aJgv-k >SuXWn2> tSfG ;3رE(9v,3h@ybTz's|`||6U+O,|ؙB˿y; vރ_S.h w)?H\4ыr(y^zҵD}&}3?˲Myog+65YǮBF֕VH9d?r8X0"&}ԩy5>9W"z->d9:[|}|-3sR}T"Sx>a;1[//Lul4)>WsBWoֿ܌E߽RyͲ27;'oʀP,[O'U L7D%\8.Q+,+&3Oyyyf.;g)tS^ee̲r]3MLJ?>#tsklL be~? ==@ZЂ ^|x(z0˯XJ†n`@pq52-B*w7I g|gbY}|^6?c?dJ|vYwн/q,V'չ⸿ ˙L"~30}P;2d{.ϕ2BM_ ۲<>#6e ,۶n5?pu#r)7a5)T.Xh 7(Y~xXWPdJYAsQ<Ȝzθ1˜3sXGőu%^39x٧b^iQQ^3ֽ6}tZ |6ٱNKI,>1=,73,|X/+G7v0z_d"c|G]gg?;O2BO AO va=:!C$ T)?U]'>Jy{S@c)]a˰ƏTY71XOao~(CĽDD m{EfN J`='`Y, nYkȇ'>@K&츰3N7x.ݴ\ͧp\lZ59Rp'*nM#*mS:ꀼP|*N. ˁ㷫=(&nR"7}X4*;4(G PZhaD7W ]4q= КGke9v,_]u7n#-[r,yTRy7wϳ>w7HnQ]Ňa?psÈ0+,x׍w;)(E`"ÁvX :fzPJ@ (%<%`>#hPJx;`O8(,lar4zÖx`[LpUJ@ (%T{)r>=}bo.,(z~'{-ԿmË"PFPJ@ (w -DwKG,DPSA'_ƬPJ@ (%vܡyvP5n@`Yj.Zn%bh?O _8D5[pJpO<6'^8g#vrzOڌKPJ@ (%:iPwYv>7;"(ܽK>콲P 8Ǐs=~Ǭem;??߬7~k;^^8 {s J@ (%oTͷ;'j׏ÞaݛoO#?" g2Fj&sdO,[$sgf+Z|~.E96E9]΃?ޏnZ'0]>eReq7`͚ըoǩS`c} DbBXc8τ t~_ޤ_J(>(ny>on.pr~%f7Dx8sgFEF"&:X233az99xI"0:: ō 5%>>>2&А,3P-^^rΨ*l/MI ̶aq4̸M-Ls> PJ@ 5.}lJ-_e҈guL mV iyKcs gc/K]FM7b߾}_ՠˌ5'N45']2 N# ͸"rQkibb"z5Ȓ8}{8RJ|b,|b%+qcTq! 1RSPJ@ (%p XO:ޛJTsV>/w323Jtn%;d9|ע!F۟wIXzJ&ѠPJ@ (yD{GcBh ,|o2QcDAWNҐ0$u!^4.Q65ّt _D#CX es /,q; 2Cr%=29Č#w_K|Pu}fo>&O߁/’nL=N (%PJ`L }' ݲ\霈(i'R0 S2-9n@/AG u1f\z=) >Q ac,7#M ycQAJXR-)Y(22O+ySi.m"h2\JmME *nqg?}"~>PJ@ (%0`fY(8CVZS|Shɸ Wˌx#rQ'E[MO5ɔa\akX&/Ŀ|K3:e,YT"$>/{L1,,XO0OƾcptX d!Pb t@tޅwO`tNrk1yJ@ (%PHJMC#u8i%6Eώ`Dz{иvbH$VztoNjc۹ے.#؛T>XXsn2 (X,jeN~{xD[XFDp !;t-7⎹**C`?EXEteFĊwQE,W}2&ps[hd/'ao.|ݙ&31D`g?S؏q|8y. <\ƣPJ@ (%pL9Ƙ[2ѽ0Q?,%w3r\z&"@f`bK2&mD8n FZ#=@OFi3'ϑU11fwJlnn,"JEY 2aw <$,}јPJ@ (%\F'T@IDATvZ qO<20TuC&ܬcTrR \׉2'nసmRY. .iu-d-*n:+zPJ@ (%nvJtҌ$)a{]+K7pvZYS&YZ~y"6'$l\{ُ߉Ug|m2m f $q1l)NO2,?a$ҝ*%PJXNZGYq/£Q|HeIWؖŏǫ3hn?#/: Y)|_-xPޖҥ3*Gj Λ;yFD!<H]  nG,>ZKj齰PkMM=L (%PsKD3GAG9 D''GYǘm%@!>߂}:f O,AفNs{(} ό-sl[K[[z~,OOML=L (%PsD9c6;K`1[~00lj/ѱX>AzkJ<ݰ{D0!@LzwZY5~JV/iә<ǹ%PJ@ @Fa}܌&w@flvk'4-eꀣ*!MΗu$M^>O|"v4%PJ@ (Y"p,}|%@At] rhK^.k0[`Nɮa{O. [ڒ{mV?q{g7y@YC-&I&Hc_1PJ@ (%4[lUh3\k|7a{E3Y$K3P,8<[R <+>n5&GW?}ɮGϪ2؊+ݦPJ@ (%0 7HwOa RqA-}l/g 6mљhsDg [IoXھg@cG?R PתGH=U (%PGඎ雽lqv.@~~̂PƤ+L L8ko%PJ@ (7!Mn]Ƌ2N-!>f [D_dLcG$ rc-QS TXA J@ (%PnGEhnoM_:{dq21POr!YSPJ@ (%0'q)ؤ'^iޗpQ]֌vQz4%p-]c6fr8APJ@ (%8444w`1440$&疋>hnawIft6hɿ<'DbBJ@ (%@oo/둙 __~Vʋ/5**%Pm#)Kx2Z4}ZemC}w#J@ (%P(G䎁IZjkko賹 oH35}>4=2k<Ȋ[ HDZΊ&^ (%(@7Iw LKp]h bE`h&sXy>ʝ;+8h{ciSPJ@ (;Gs5kD{]} o ?)~뛳pu] };j\bP:WRPJ@ (%(Ϥ܁ttϛ*k׈ީnD`2ۇ}s&ųfpp { Vtshkk73q {z/ 3rk6>2;4DנfH ,[Da;88i@Ƣ D J@ ( PP5rx ocxx?ӹ3<ukDCO]㾿LwǖOs9aG~f,p]8q$XebV&q8yzA7[_Ã>|ɧwc*׉I;r(.\?dtA]O=ČU?*\jqe1q$%-@YY; )-[6˱1:2? +WxifP"p#aEVZZëMiGG1_}h?cw#`*xŝ1$$X2+Lex֚,_-ٺgg#00J~!} 99IkI=/vHY^nxC}ߦ?PJ Hڊ[t|-|fjD!tnf:1ǟJ"Zֻ]g(ya_o>s֌hdsX~U]@e]$gvb?a?a?~_Zw- > &E_X8ŹsQʐ abonCdY~ӟ#$4q}r0F4al\qh~{'78 {ҥ7-kIga鹓`Lқlk7ߒVa:^PPYxq4<<̰  K.Ogː zpBccM3E wnGc=xرN3Xf5JA&jo0"Qs"#݃.^/g{\t{,tLDcu7Vc쫟c]c4M?2yV+G{\lL=fe+TFT,7>ab{_-nv/ 9ftXDJNQ񛁃 N*y ukg2L 6>VyqL7LerYyd$]Ɓ (%%2,'o0.=55F9gz>o%>ֵR7}|/mX-,bsi{}ٲxFup2z =}ye<8q$m gʤ-Hȸ>/W HeO?v&; +Vҍ~Ý/?ɌDSN@7p <o Bt#puWc%,ܵB  +`~};u]H}W߃Y8Yf *}`g3p}1Bs*O~fl}k'oţw"探Bdγ {^:gOfNuw1Rjs)SL (%p9w}2mGaI H)N[;{d SrB G\D *d T-@W+ں&~rM ɬ=6@RE R]n?}s^b3Y߼PJ@ (%0 (%M{sYvM[eimp ya,." rV2fzUCÊF5[ ÿ5ZRrfm39N˝âhsX7r\hܿu=<@ #0ubQR-ǎʱ&}23 gYa ߼Z@MǴ1c\ *HD ||eM*a{u"!3{"D9xY؇{Vm܊UiQ(lrY8[zBPXRk|iy#f.ŃɺXehų NV"QDfC9u=ec,EU>BK/@$ gw\5j,JnQ}⪪A (%PsYibYb Z,0\)USM\f$薙l9px@EԔJ; յ"Uށ+/btxgO8NYz1FDcg,v|X"V1ԕ]^w Yc#3DT_ ۮ68YPV<9_EU]#GqM'~'Yy^uxq}pu!rg,b{9Wƪq*|ۢefʨ[=SWJ@ (%%5vsyjFJgs.UpvO8]'0l@LRUׂ@Y䡽2|WPYwϨAqkl!TTK6x{y`hd ǚD\Tj\7tw_Mog% ?3>GQ#b"]wB㝜,ې(wN~n1-GhJ@ (%P'0\oho3Z3(ª{Ng'nfԴ!"<g[k7:hdLP[Wo%8[Kx܇/5",!PpL{Ғno+6nw6:=]`%˄42MU~qj̭L}h"}РMpbGA2z6Cgg'*++]!3!"eNUQ655ؿ e>}LV¼H%<"0#KߐXGEykgĞ 5#[GkM):Vhn*EKcŢD e:~+u 4, 9QTф G084<}P Zׇ oT7 vIb-S\sr/KH$E=*0zs9 @t>Wk*$w]C̔ɨ$10- "Vs3F@2ZK1s111sr@c"nbzCq {-@fUUfgfkkk=f'Ykf6?ϋfgg:397϶tqmm6pI9y$+ c33]} d_Z_X_~kϤ&%o :[ٞ-{cf:io̒47Mםl_Uxw;| fzȇoBnuZN@G!@Ô7M,Us%҉* 2/ ;'5ݬW7oq|}H8hn):t̟,_;?r*ʦMJI_`;Yk~Rl0́uusl:2ޥbذ7wln[.lg r,ŒeX-NaPzm]`mwB <{n.]n}Ϊ}GFǏǺu.z]D@D"DjI3c,e԰ܴ󙥮~s;a[,?7 .  )X88IK^s晗 ]1{X|>?$`3 WGZIq 6nĹc}%_|GC!,3ׯ7f%6Hu٭0{OͮB;'@E dL+۵JMR [znK:Bi%0֦WVaڷa[x^D@:zSZxuԽF,}||).TkAw}iSi|ނR[ \c@hQD HyPl5Fq[ZԮ$ >.5Fq;a˫Ӱ/WrmJZ%" "ЀEaRv/*4kOc-l[J Z6}y1o>|8Bz-;5"Ċ$ w#M'0,&/$Qc")' u~?fc*[`>C,-߲mY 7'=D@D@D@DhO|?"6 gB}v j@:wCSKhf|`6m5RD@D@D@DB\ZGbVV8;%?.)" " " ZyP+B־Х)H2c;m[}Զ4}{KifΞ DK׬D@D@D@D@n^ @4C nwc;GC" " " "Q Hu3|S3 tAD@D@D@DHHUs4 ~j'3TuIK ʒ䨈}TvЎ8N45{(" " " ": ϽZg7ժvMn@d:wק~*" " "  pwoοsf,}BnGm]KLvAoΎI" ؀RQiN>̬(//þݻiB. Sg=ME {6f⺦`Ѣ90)v)xc TX3i&v1qBX*Ʀ='+"N끄Y7#bҷQVU#bD*w܎zB;|NFwO9Z!|h} XpgLTD@D@D@:.?^鸝oӈFjTTT ̘>3Քc{`Ąx0f g6f"FeזĐ10w*ܲuN?Xsx]\h݋GΝC(IJwVg){tOĉcY8ulX5k?Ani/ua;.9Cc9,~ʡ/ w` jYD@D@D@Dhֺg1t(̻u1:E$s1 q{u5U# ()FY1:V~JBZGgU5p iզ`KJKP|[v#p,UQv;mzyr$AؠQK2yIәj6I٢ҒRlXj}?rDA,{o{FX^u)y3yE~ȨLXOY9VU`cd@Fڅ #oż]q>WT6Kθa~E؉-G"¾;`׃hUH[XT =9')YLE0槮g0}, ŅX! MmCY};ʆ#챷5.0SvD|ܫ" " " "`.QI)O\Lm96ףg R7oډsg}2 9,pGr֑(AQwTN 0d Ԗu_XťiXÇtJ΢s:xU$:uMFרj|~3|& 2Z=h뫫k0kLhVy(pdW kG0-N_w_zp֋|6~ WoY M#@ ;'իGߌqpں 5<{6=2ώazPuUĀ~1q$l_Ҧ8HyQVV<X u~-!gKڷs,S.o? @!;+-Tzj 4uɽke )\.Q龿͆_S(^0+tB""2Q1f!;>ŶYNz].Kg[3߅7` xֽ#?ni?}SOD@D@D@DN ׁӁ9ngճKoCY67;V==uwH0g-7f]pݮ~NKtgf7K͸a7Jйjɭ:`>6>mwk%D17>]1| Obbm馉Bk`OYn=['9ر?gCuGxT]M䯟$kGTͭx;w?;6Zmٳ(ڼe+-tRRW_Cf'Gk0tȐ6C5UD@OzM̝шb(ߏg~ƌps=?ȑ#9r 3gö#mye Xլ#bYYYx՘?/^-N8d`Pf; /U6FAM3ɓ'9tڹsz+;;;vD?~ehuVH k]7nhma RYW]S1axCuMKg ࢼe 8'N{+ުU{/{ yLI+&ڍwmw 1g3ƍ8~(,(ĠAM ?qǥسgzbر8t-A^i&<2xl5 pvB? 8mllzqiVJ9={ns|99((4tuuu~97x ^,#/?'v96~)6[~qYQ(**v%K*W_W뽜OJ\@ n'ά@qp.b-yV=d|.6r6q~söu͘1YՉwC^TeC=̳o 3 wa=x|4>D'%udz66Djtٳ~g@g^&¢qBʋ{VQ#w?k&w|8ycg֑,tÇsm7r}MEJT|䶺q#jM8 2c^ze7m(u\((r+YKK/;+C=`"w1UWW xDdoLs>Ќ-_yA[/eȤ֬.y'ONњxaDs]HO/ O Nΐ;n_|輌vpu9Өϛ_zͥ1щ<8Sy !kl@ܹ&[l7|ԯ+J{_Nq'@ys"XD xYAM℉+h\#} c1Ȥ03O_ν1VWAFA;&`o~Q13%\MGgns4Zu4NAEEftLlڼ ӒM0Z߾wfqEaIg u>mSf}|, CwYܲh!$[_Y<`%:TZ]zi- -Yzޯ;w܆m." "|J'M|Vb>+,^|n6|iTT#,v , |'=8.\yǏw.t秕YjQb_!ݠ^KnOXƛ.FfUiY<{feeP B. K߾}[Q;U8kppz1 t?6spr&b Ix?3gRyv>x^D7gm[ ` @3w =p &#s\dePx Ќs75j\c7Nmw~v.EXJGka.F踹aR`qbwt}b" ]VTT3g]C:܎M!_&kKJ5Lƛoo_+k* \nIO}S( S/r=3p*&KU:U" "Z u rB"@ M}h|i'DzX*7TtKNAH=~zX|M3+*aCesFUZLCs[D@D]GUD@D@D@Z';i<|,Ǝ *@{툷I;šg2M\Gd~iC[ƲT_Ea . g7\K" " " " " "А'{~lK|{[oD7AZRNZ&ueAKQm) `ܒt둂n p>9HJ 1}Ӓp&ɪFn_4H*" " " " "  0 e2Їm{-}K)U,. sL헎)F9 ˻~dPmG`Cwaѝ-bt ǰIɨ F=w݊ب+fip؁7՜r,^8W[טp˽HHOm0f2ry G]\x&O9bblz J{Te؅^} $Y Q{+~A$t?>t;a r%Ig50F"R^_^|ÆØ1ڴOZ:؜hL}?XN矷$ naCb4M)[DI,t2d0>wםfu.?SN[nJ,7RX-++H4K[!;xc{/ap,0߅JN l:{9|p(6+'ODEeg'm CR$<4p-af82˜Py9ZKw1ֽV܉*m9W l3΍ֳg-wyyҵ :il^/}>7KJJbg\rl: bUGIi)}m\oˎy匍|Ut=ݱΞ9 ¾8vl/GTD@D@D@D-ˠA|CRH|5֘ј$8Sgt{/ce2q!fg&fx]%rϖ`q8CR8mw oҒR$-[m6NCx{r@uˆ&>#;M}[̱ɬ/ysV;wGF3f xozo,v6w nwtWQ;_ /^1| |&wS(P).mrٟ  ׿" " " "  Ϧ!2./%E|6(=!(BEc8r8 dboiy*+VYeVE:-֬^eͼY19|6?s;\Xڐ6YVW01K4kL?h.1(6KaK~[ؼy oہ1c;:K`8u, ƻ}馹h3?ܜ {A 7w2D6N7DZj_zǏs[wBb0I账k:ܲOK._>46cVCQegg㦉1mTLv>>@&F^q>WS kkkM韶h9}سmH1~\2f;q`/Ni?,}@Iy%N=*{_ s%c'ukWcśj#˗-u=z &Y*3\US['Hbrםwb/֮].p;WtvCfqH\կ>xshn=zp7V߱'H)~c1鍯km9&ya ~,+p`A|+:1Iλ y ѣGc{BqᄑNur1,⼑ey)[ɓPߘ~tpĺ~!ziii_wm7h}9s=}\CkD@N`ݺ w~6}h׮͎[޿v9µנ}),ɧ6\af-x|=~ ܖxݽ{|6OӦ`U4o\88siGԩS= mmd)\A7}I{[bի2;`e`syz4Ϗ~aӦnna9u4>ݴɹwAc߾2|0 @O;1b֬=ѓ%ɲeV|=j{[yU1\6x7|Xnw#" !lǺjk9E?Y=$S:f s1y܂eQ>*WhyCkZ,3Yi:c58WZ\ؼm.!nI͞5Dرxױg^;/pV`]M)ILJ#&4%%Q'(~b65-xrCZN8Fݻwoz$n P,ɌLK{qtOi iN;~A=1om u| l`e`=pɇg{زuYҗ8t:;v"=VxY(%)4Dl;WYYDKAAe1EII@ 6ſcr3|L1^~xg 8Y=aq]$8p;Ay%'' [~+B%~l/㏽`^+'ץvoiEŸyNxy;O'k۶o31vc=~Xe .4\󶨴b.^a_Nv`] NHlX<3Κf?0&ƟbT'O>m5i}s/ 8'6Ƹ/=cl=!c-N|f.СC,6k'\"V@#  I=,7 Qm}MD\SP oun{"D ]e{357rjY#L+c}srm"٘\]`V;HQxdNDFF{1-[wĮ 195Z(ib¨t`0bk ?36e @JCO aZh 5 (8Ř#G8O :^)ȅ#ik0~Ç31;zwPuD w^W-^1,$ u<5Y)a<yqwضvnݱ)^:hn~nwdJz)n9Hߎ%? 4/\mo?م]]AҬhW]]mn۝Et6nrm^(;lm0\3)"8?@n*׳-߉# % $kYi0nv>zjp.3Skb7(,< -\:yط _q=/ھ@2W#G֋d[yޒ- (i1J,(6G-Ez0a'ֽ.mwf6f;;)UE@&OJb((>*,s0M,ø9Z};iLAdt<|KvL^B!ȤPcpO&fJ0wp&ծ0$[&]CL M8:1DV0b쒚mˆ1!%G![fqwt5bzŹu{*?ƺ躸byaÆx = }QD*)G<K?0it_TgOG\Z{UUWc{ ӂ8}h {'K4}hc~aIM~8>~I< <#"i0iP _*}?O۞~~acoG2 Zx*" " " " " " M&ТS6Y|^ʠ? ۾a5^ZgLM碨0Oǿ!m(B߇:sǿ TXr@_{D@D@D@D@D@D@Тֿ"DtAJbgTW.<+*Dz^J_M%*.f[KXװx%~EXcPh{0{GԡX~w|?{K|{%x/'E67Ϝc (!,=>zx$" " " " " "X!>7z]~8QPlp<&Os9Y72`=w.Ą?~G7k_"Ѳx5u3O<8 <"f6vGNf=NQYYdo<,)76'_Dd"8GqK龯2wO c=u`ULeCZoWmb?x1@.}1ѡתn8:?ox;K7y"ETVV"55ER%" " " " " m%oިcڴiAD@D@D@D@DcD|TAyu.TD@D@D@D@D D"@." " " " " D_k>;jH@/D]D@D@D@D@D@Z3|v6D_f}m" " " " " ""jwh$ZQD@D@D@D@D@D D}!" " " " " "К H泣@$BE@D@D@D@D@D5kgGm HP@k& ךώ&" " " " " ! vL@5MD@D@D@D@D@B$ "@." " " " " D_k>;jH@/D]D@D@D@D@D@Z3|v6D_fCIENDB`PyNamecheap-0.0.3/img/credentials.png000066400000000000000000000531641323172663400175030ustar00rootroot00000000000000PNG  IHDR5gAMA a@IDATx|]y$H;ER(ĢlI^cgxlewee&-^)R*P{s`>$ #sϙ{f;3)LAD@D@D@D@DHm29UFE@D@D@D@D@xD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7LxD@D@D@D@Dob7Lt!$P^^nǎ'Oŋ3z;==Znm:uzMz8띤E@D@D@D@D.JKKmƍV\\l:tD())) 2d5X$ m۬Ȇ f)))7Em׮]6vX:aRm*M(,, QFeeeIMر9r>l=zd&^+D|jjeee_xuVPA"+]#.\inTCydoOg@! K|ʈܜefdX4rkmܖU ={]R?o٢E5zԩ gϝ"@Ѷm%N8*ҬM[36z/7"0K6*b㹯Z… )k*.N9gQM:}tHe˖^/"p|s4E/yc,O_vZvvvK(7Lj&5}s9-9'Ȏ?Զ:$ui>eIh$?"nEO;˖>sQz("gM^R$2%UjF!/P6&zFЭ+]MT:$"PCԗ%Q=˪6꾣{s΢KKK ,:kjڔ';ѯ *?d=s6edg,oϱ?A{;~mԻ^?ygZgCo۾~ڧ{krz/"p|swLE2>,'NV`+Vo}+GlWYW}.߱c=g ٜйmq;3킋OSA/ϻ߳ 6UZ{g'#G߰mf%%:| v/}156j}$9 .;vYFf=Ѓ,\vZ3g}_ 匍7f%w!6(b[n}9" [lE~<3#3#y]954j{sDUt17ei_yW>{_y<ŋ{6b`lyCJ޸>'Kwq{}w^|ֿ_?g=6…qii^ܹܹfz+t8hx'A/kMz:WDvhSXSW_ ;r5{fLh|M;Sp&D\ZRjӧO u…B[nm&i[} /{N;lѢwC# u\ O?d'k"kWğ`"J}աBߴyS! XhhYZzK/09* 3g7Og |`u׿.Ν쵢]kM}=*hYl(y!g~}}vkP|_}q1_r%fe6f-^WL~^ʫ6@eʞW|KRMo}mym%^~5{iMN_7֯__ݻٲuk).S͛F*‹>Xb={{y9r`G6|Gvה)6|0sHDmB_#6rR8AD@D:2I6n:mU}{:ֻ1fA+*.6FY6ydN߈ H֭ld%3x<_8Њ#FjךS9 &iw{mnAjƍ, byh{y=Ɣ idiOqЮM0o݌Fp.]}߾ |v;p!*ߋ.N_mSBTR,U.| sZbehp +?Y%GQldGP0`@>Be۱^zKO}&40<_)Aw?ʼ` 7aٴiS[y*8Sn{tΨ2dF?݀ >5k;~O#FNzg|cDSNVq"rCGvӦzݡsGɓ 36R4wE-[ĬXg{|wyb{ɒ%n+#Ew=%tN9cy^(w܍:w:=}ᒰ m@W׹NE@*Frn6,XԱoڼ&N`t:ܯe˗۫>Dw+6.t'حkʤ.؎TiXt^y54p[{&Cā Jq0׺" ܹAd3P"䱆HgotL2:d{u.\#=`(|7FQOuL^WKT%Ql3~WmguB|w`BMGcw`B|\&G aHqs}^3qlݶ=L)sq; s^>VH#a ]p!Ͽ;v4/J2y1@AD~?n 7LxYYy趟(s+dVV? P?f22ܪ1><}¥S~-X{ "X\U: 3\;w޼` @("#$IOBV\I#O v:Yӡy'뼁dbv^3ҁ) ܼy B9[lI> ߃_HnQ>ZpѢ0 q :r;oy;w&ݕciw:V> GB}I`]o~Rq@ v>qlʗ~0J#nJ " 5'@uz+6&OxOZ\i#2|:kw;a}PWO?./S{'D}n0.JMv(Tj CX_-;0YOՌU`\ڣ( LJ!@CLλetE#}t\3ޗ۰q O~`ƵgmKO* z\vWa;!cyE<L$GrG>`G3"x[$>wC?nyӭg\N|Br3^q=lW Xۙ<-͙^&X\p8_o%HL-'4Qt^z}1:^2DM66"73 OHߺu[U/їzgM/oa>U߾}1zҝwڨQ4tY)a_P̏~*H?壏!zo "~r>YbŊU"zP6ps裏J>ܼ.]>GghٸI0|V%P:5tIŧʲ tSVdg7#ug%pB3\<0p#ڢܩb]YEC8vDž7 :+-B(^+$6׋C~#;wsW/:؀EU|o}lh֩}.v'O2'_VB9v;*}|&GXGBN q~XRq{,iR^~7=yM<*T`?=DL 2qd k`Eݢ1| GY\ s7Cmܸ)3XǎcϿbh NO=uiB<,kIC`{|t3r q`&TOu{ppDGT~t$Q,C}$`[̳aĐnwlKryVat'c<iUp\VNuQFF2p& , ؼ <(Ȱ-4];׈Wxa‰'b|ry^phG{JV3c(0Qo+xӱٸqsQN: QM cЙKYQӠYxժ5]qFx |ueae5{!gC"+v|l_|1ʛ^E@Fgw1HbqgF~ hd^'Ѿ-űMaCo?ι'#aiɱt$|DI\id,- %IeLe h !,Tp#c[p/qc?Ɩ0P, ;7Ç*+Du/寮H76Nr{rA׹['Q /9(|~ .1#|U)8:%" W#X~Ǎ !D6m.rk.ˍϬӍ%dledg{m?atVy{OpPO8EP96Lb|rX3#Ae5?{⌋7{~I hd$% g=C!/t`X7KC i܌sTGykD2X2Fh1i :=ůzzª?D þ}U&b:'u4~l&Dž9:@{&P3 26뼊Aϼ\$ a>D.Fҩ"N 'f"-{D;ĥ/]9myV::Խet,SEc}8N]{ΧGm@60t864oE\U .KI4m]E@D@D@D@C tk#mx: %N2@C_K+ SQD@D@D@D@f/f CUH,W"054{ϭglچI(UDf }*vh\(}%ƨt@]D\i 呈o ID@D@D@n^vQi<7b@c#:]6mJOgnOTo8Haph "tE@D@ a7V-uʟdAU٤)': 6bx6cSNkCmP䕮4aB ym۷[K[n5*|:$ځFI[mV-+wu:=$\Y); rǟV; lΝaGBVͺk 7[黒#F VTV ~핻ڽ;g_(.1vfۑ1;FĿ^DNtgAs)veWmB|åmJjkzja't_rmۥ]VsvE%tF4lG|C|'(ȑKP@oFFZCe,;weIzߌ Sk.ްE4gݰaC۴ivKn<_ Μ9m:sE4 ^ @%pEs窈ݻXf5lq}ǎCs {Z*n权6+k v:xD-  -;+ˆn7nC[vҌg@|r| ;w~vu.z-Dllʕ6cs֮֬m۰q.hothݿq*ߟ:uf͞D.9]vۄno9˾W g~kJryg=',//Ε,7nOSҸaVXQa6lhtX /+D<ʓ'NZnn^VÃ/oNNvwʍ?Sp&t6b @^^k/lwZWJk<ۑ#GNɓ', ܃WW֦M-D|W"4 . M'iӦ]:v[Ǎ a]KFV*Cb~]VDZZvp!Նm>|XW.Nd}+-X@ׯ[oZvj" b- R)L>?^ay?'|"Ѷm{W]oټ5V%n9 CyǏgٽ{qJG" lw[?JW:+=H8gϞr*x0t4ByD} d19qCS2\߳G99Eﲵ~KnXxzGjlqn{ 8!q)(#. :Vdefy9vTk:eCm["z7:?uMZs]u-1/"puMb9j!?tW ;DZ` 46dBb <ܖҁUyΜwK.6N.]D'Q 6ulsͳx 4>v :TsϷW!Q:@֯_bFzZjp\XǍ>ݭO{n%'6Dv)& r.B?A^>jH8v`m{ :Q`h1:񡬴E0ԹS?{ӪU=w{\ir-# قopa XOرw^+.*>D;Ravĉ. Xqa<`m9sb/ @$ۿ?w|kME.!~g(Q21SOE?׭Ņsk߮}H?ڵkmvόB};,v+In .g[Wkl۶-{}K\(ӡ?=+c0,Ni<#<##Ғ*0~_ /~o{gt_יKpό{D= k׭3>?m7F83ϽSE^"nox`/=x͉m.VΟ@ " "P'"A O" F"-mdAFF!++Wz3P0_eH88Ftiӂ5<0DUld)rG8V3U$^&Ξ`1s~|ӧs3;Xq׻wNf~lX8!8Mᠻ\-0]~꩐6e!gL (/vyp:=]Q ; vOiE{s((8͜9+|O{Getj+},Qo6M2-eO KDk>p$h:{ɩzˆx- Mp_mMN9MFYb9N+eCΛ?~ţppްC=_E=sx }l 䕴N>1(|Ͻ|ܿs}l玝~+vE\cQ^:[wuFE q^4 O}1!nuUwjFTr V䛀;>0]THLuQ-|}k$pɒXKB';"dB޺u[کS Lw?oz '"~.[V=@!F 9yXtyXk|D`!qoOR?V+D|Ν;|Oled[Yn y'bAFšk mwW"[.6t|v=zTps_-Q@s[q30Ǐ{(t:V\=>ٖ{-˗/ViӦVYq ["=ܟ3.Wxdus d$qG& 㒴c .:a1ok[F.PNF5(Gl3dU:gePԷo B@wNJp\V\ә(&%o=&|2P3q IMjO3ޟ!,$*x.[܆bG#suX%+G> a@D Vх*2}K23-c}H4DU|" ͞Vc,ۑeO8RE(" " " " K@/_." " " "  ' pPD@D@D@D@ꗀD|U" " " " "ps鈄cT" " " "psHo΂5Rk5ʙ,5¤D@D@D@D@Do`Mlm2?eTD@D@D@n.Ν-[ZJJJ+gϞm|K7z%," " " " u# wqU" " " "  P^^n_KJ!'V+HYR1ԁ@cl&АIĉa9֭[F>qɳKf(X/Ie~]jpBmv?,iD>rZGZEz̒NtZւ|la/^䳲uVFY~L v ٱk̙3a} R?j+a a¬ukaaaU<<~ssN<ꎨY u3Z1, >O,"< PE5!NKO*asҪjAhڱtBm{}Uu=?^cNя~bO}I;Bl3ߜe7nom~;W앫V۫f1֮[grso߾g/6uv߽{o]lo檍={$6b%>WYec?U^^|2~؉M~qOT~1&گ_/~,V@8s,)֫W\XXd=g2b.j\Wύ>o߾önf=Hdu#G__zAСeee]ݵPyӟܖUN]l-["䷠>60a|sG[w6_o/u-4^4Few " "И ;ڋ¢rsXbx{ceӣ{wۭ]۶<Qӭ:4h+ڟR7NO}&5 %"P> Q{)k`wV3C#va]}1}"+S|ذ־7܂^q^yUuE<7m}˼F? HbOC~ǟȿ|?7":kl0"JMCz{{;>H2,T,:<X*)1+.$x>,5|IX=:+R,:wr9͛g<)uߓs_a-<~Oȑ#/ؠAxW%ދX/:k.C>}úuK9}h6::ٍAD7Rth>XAܵsmݺ J3ław6[ m~eC5xޢES6rZ&y95)ˍtOfo̝g "~ވu.ӧO["+pXt}z>`@eˬk. wmu CXX=`F?^['Ge0<--\l/^F~V\靚6nX{m\dcnׇH#:ϫV+VpZj} Lq߾yڝ&O<HM2R 3ԡcbCawMRl|~=B%GV\pî/`ka?!HKs6::( ԫ^_^,թk{mb/9yG.rN/ޕ*JO" "P 8B;y$7tIr|qO2eRzwޙk_WBC{ݳ+1Rsό`z&y^7}❿k#c޻o_݇4bc7s!6N{xg۷o^~%>i֗:ܨ nӧOCdxckX)~=n, /X:\ӟI[nŖxg^Ѿ9o|3i ׯ__)Zbӽk]v;& axᅗu^u^##Zt&p3׬ٳmQCNw6pஆ ʳ=_1+U^aVۼ<'if;p`ea+C; ?y.Q*^݆jSC;Xc;/!,Y t0 ^pXq3dh^/k_ocD@D! P_^~O Ǩ71nQ=Ggz uMIWUUI#4\4wnu\ Ѡt%zIWX,Xzq1S?-DSMgqa>֧B\K\sKù}he@|y/7ZthN_tzuC[폿02 q+enڭ^6+_R0\8\/#~@?/C5s&:L{m0R"бCGUg׬qOWfcM:-1FN:X'9VbM9bg <#bA1;ڷ FwN9(_-MN4wc0ЅMó|*pD9 ÇQ5A}w,Ӹ"ύ*>z`寠l^P& `>[sSR钓yʇxVfxy~viC QF<1 ,ܿ3r zO|Ϲ>J܀m۶^#ʬ " AS{:Qp-b/|NIIunYx^cǞ@mΏ6QD02!aWZGJ⃅ Z+X((=n]+`|A\5ѱjG+L0wzd GnhpB}i<{l~" 4.F (tuϏ< 4AC0\p4$LJOQH{n> 9/FnϻO68%-`Yp LJJWL&e):Tl^qۋWg>Wp)s4b95rպRa,g~\0pu%vX9b?wKp'z+ tqˁA| F*iܾկs?Sf:*cǍ _TNwa4O>ar9GE@D 9*,3}yb ,DBq@^&'{IJAD鵽ѧ [ bcȄWkS&O;ݺ5kvaEE*= 3NU6qڴ}JpIaM|NU'ayd80>fU tL3&0 }g^H(ܹ+<ѹn߾ρX}!d&_+뻻\J/<;ov3͇ cFtT*Π6xV}dEC΍|V/&NˆJ!cu^_I0{;~%V Z|EL>@] PE׋hCj?ڶmcnu#uz2_nF@G[R8@;dMӏu|m05_'OWWح^UVa[iE"`{1c޲?}S/{{AogetdɧXјF4XLzPg#&FK’_l ͛7ۿ ">*ox2?h?@QCA|`e^z9Su'\}d"~QzӧO_~ öq.@q1ibg \E\F,ɮV%BK5B|K0NwP㟆%%9@N3?+l0~ΜA&o<i|9ɶcseG^GlurLeI}o>>7iFG@?QU(wr0(>wc={ t ,0*+夞.0gQg7*p>u4+¢Xݰ^&LNu[ܓ-jKuQ4\uI7r^V=` Q@f42ǣ:7pV}i冱 ,ku3|xX q L4kKT' i/m,V|`n.LEЊ,;$,ㆰǚF ?!CweXS)H`A^^~Xp0 '3 ,E@T<#!$(,(נA@|=x> :([|@K74ӁpvIDATGu u ef01s;gvm|n"sҳD[3@XTn$Z, A: .P2bx}xq7gw>< 7qGyL@18cT! ^V5PVzenHIID@^RU>D8K4Pd8V|h 3v"\GQtc*Z^th#]ԃ^olx_MqPW]E@D@D@D@%{J*o +Y[!}#a;򭹓N׊$@'HD@D@D@DI1)94J7韼2/" " " M@$ohri;E@D@D@D@Dd%0." " " "8M# *&Z`H;1W"nM}>̅|aA^>!';O6q'" " " "T] 0fr5O:6cS 7y+" " " ͜@zz=6mdg.yuzʳYuIIi'ku;'J'#GѣGYca-W^֦M+ʒɤD@D@D@D@ ЪU+ǹVVo|vǝ }@D HN0KDžhFAYD@")-UD@D@D@D@D~" " " " " =UvE@D@D@D@D _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ H'& _߄$D|*:oMX@ '8>E'" ͆@+=qK)7y˽);t{MODoP%H2sηfŅ]W1u;K;,{-I&D@D@")z6;YvvKXʼRl:Z2@O|`U" 7+ oE g[NlKIOre^Wj9Yy:NJTrjKD@54ۣ̉46o.޳[֒o3|5VzwJZJHD@D,%@-_<k@$ʋ|.@,5SD@D dO8RE("pSD~\7‰4IM)"  JUyE@D:r@#& ߈o&" " " " ;MuTtLD@A ZN+W+^E." " " " ' xQD@D@D@D@ꕀi" ,5oWE@D|e@D9(++K2[OeuӪנ2@ HמBX/nJ/R,ed[ZZ5 H_+ `?+[SYsKkkiEVr]Xξ{+?q22*Ǧ}DZ=ƞc " " M&6{,RR-kK̲st'muo@++.,miiYŋV\Rr (--bkN)*ryEkD@D@6YSE@VxKwK;]vmBZ>UkY? ;w߳ ^ڮej-ZX_SgeQީYVcvfX|+ڼ2z\[PϸwG,=_'A]5" " D~" "PKGҪ?Crӭܭg_eb9i{[Ѷ9QkͿN]CRnE?kv?[nxZ?fƙW~ggOVgk? *9ql*KSk@D@DNSWrND@I 5ʊ.}Զ-m0V9?܎Ew{3s߲2{ ;̏V"[J붖qt=;7CEKCoed7<D@D@K@Vj" 7 l*}Zm[NRsZ0[3]o!#=Z sYZ?˺k1i[-gdYESn-[KK,k0Kʎcj D#" Mj&tU%_VRj95rVSheǏZZ*6YAė;kǎZր2Pn6S~BmTgwa)n/s<+ed #" MD|aʮ@Hn颋T_Zv_A?JCMk [Q>u9n3IUNdp-޵5aWHVydΝ>D@D@&uY2;@?Ugr-w?KoΊ֯ӿWt;mk1SAgo%GʤS^CaY'`ufyz6bYCOY~{{؉V~˽&GD@&uY@ X.C[ߋyݳOEZev~,+p.Δ>$oZ _&eVw߿¥ed[w|-+=8f%VZP`1g%o#A]R|VpU&$" W%p/}sjElՋ+Ln,{M|HKmR|wײab*\CBS8eEffe\ゾZ ӣҷRRD@Df%֤f?*|\KD~Yܻo[VVt/"%2u#a}`*" "p]ED@Dz%VVpƬA䴰ԖQOj%$" " " " ! pT," " " " "4ICD@D@D@D@D 1$Q@H' O G"" " " " I# 4JHD@D@D@D@C@">1$D|P+!H pT," " " " "4ICD@D@D@D@D 1$Q@H' O G"" " " " I# 4JHD@D@D@D@C@">1$D|P+!H pT," " " " "4ICD@D@D@D@D 1$Q@H' O G"" " " " I# 4JHD@D@D@D@C@">1$D|P+!H pT," " " " "4ICD@D@D@D@D 1$Q@H' O G"" " " " I# 4JHD@D@D@D@C@">1$D|P+!H pT," " " " "4ICD@D@D@D@D 1$Q@H' O G"" " " " I# 4JHD@D@D@D@C^* fIENDB`PyNamecheap-0.0.3/img/profile.png000066400000000000000000001043151323172663400166410ustar00rootroot00000000000000PNG  IHDRsIi[YgAMA a@IDATxxUGﻄ9 D9cl&㜰9uݹ'ܙwބ;=9s 䜃9DxWRÑtUp]_jڵcs!`U@e[k(;"uMz!`@#"2gֵƊ+Ҩѵ._ߊlq=#k 1C0*DD^A"K<ȵٓ&?"9| C<gsoXy3-OE6n*tȼva@%#pȂEgrZ_V}Ea 3}#Bp؅!`C"Y%pŹ 52/]!PdYt&ΩTd̋!Po RO-VrJU*(ۨI ]م!`@XrrrT}Y8٩aLoJb LvV>ĉo^ a!`\K$+KI.W ;>%(2'_"] 55K!80 dggW ,/7n$#8JFi&ڈ?2z ȥr8u~,Wnz~V_cǎqɽfC\mQi5Ky43Sh_HT_۶o+ȜkII"t]vnIZp99YΛ'g~RrR"o޼E. ͚5Sɐm:Aȑ#'ʅ ݴkȐnܱsm2۴n]1UR*T2{]^HO?&|$2|:M ʻt8%g [nv_9ڏ;+w&2kr *x= -;SS} E̴|rY{Ա OdΔ`3Z_ oqNdV,^Gj vNGĦԴP/i8#aOrFM6~݅ uz$$$*L>^$ PmqT?x4YVDYwZgqAۮbT[$\>ÄDŵgQ ǂw\VP}g hJlΙ6j$]ޏisyz1yV9yL:U.Y;Yjީ#}TM3G%C\ IpN[t8j:ݻӜ^}_T?z1WFx,3fLw@T:Ocnjqb25]ğ C [6SW擏?]0o߾yaI@5$1cǎ%$.-/V/_.gΜuí͐޽vYi:dq3PvumȮ4yw4DO-az<WϞrXgvԓO 3g'ttS ue駟:ۑ>"B΀c ?p击}`Eq J*}Y&oA؟l,gyֻt -ۏ*==tf~X)ph7Tuе#479|mjG$+X u?||ŗcŶs5eM:CT3UBREVX2߰qޭ$srgYrz 9mTFE;{AmhF!HgFD#췺?acT G =x~Wu*fZ'wDSZG}~X*1 ~tRdWI.z@J!Uc!ey饗?tԇ Hc;vpzlZv 2g6:Mq~B 謪C?5g}& y1W.g_vN\mڇu|xѭZJViN:ED~]ׯQB(7ثݙDBBG%ҦY  W$ MUz=cQ.ֿжDm3"qN|mT7 * {I>:`J[UB%C&$q"?/R|БI} V:'G *3oakU gg@"_ʃFim۶QnnCjtY ׏wxһw/t 2Z]7Α jeuHFr*]S\ڴwCMIܐ IIIp;CU .-mN_ygryѮ0@"|TCBfС?万: wX ;Qg/Hotp'9DLBA8})8M %վ:8@*FUʄ8;u ?`3q))NTNθ R,x CCMx]jVg=t1rC8!aٛU2u "#g'~VrS[Ț9I([OU"N[xY]^.GtK.4 %,X/}uرyo>_7?jLA+"dȢ$IgHT-\sHpc`A@2#fX"ΕO,'Pp"uPg,We*O8ó0udUV"B|S3}&.kNp$).""\X6-m#M<-ͯ7z|<fٿ@AD`kӦ-~莘CP;u,};^M$t58t ~c鯋6YY8h!rlEg΄* wQB. c=ws3TU p݅cӠCrphT2GK`A##r?!-- >t\tKrx*/ At~pPy(= L0*SBłr"{%*\{]d``1qYH8̝ZG_qmשlG4 K9 fdBe.}!> ,VCH,iП1mݺ \pI?1]hZ͕]S R`Zg@vS kg1SUL!$k!uqwi:e=u;HW_#P'E /pzT魱QO/_!+ՂoujuEAfrr5t[nV5!w~N'=)<{ 9B[ ":>XŦq/V̼>gYC,1`Sv tA\Yx:v~Sgp O+ɱ`.{|}d 0XYD~BԪp,`e0r5њ^MEU$o_XG8S` 摐1|Y MC tM(܌yTW,ǣjbu, C"mf+c QsQ>|-[X@ uÖOԱ+pj %L1#]vX1#f\lew-7|˙J2SM}u}ŴF9t Ζ:Cr~5J|췱ۆC]$ XCqBے7OéNQdiS6v:|P5 /$AuL &HAd E`h8GszEЁ1&BX =B< ;_A? Jr./>l[%~!aɯ$I8 t~'m+>@ffιM׏j3GsH^/?4HA6 JrCP H.p:c+ŭ~אyY b%9²WH$aKJ,QEBZM+h{+~% !svۧ;X7T%jX;@rX8T:׍'F7Dȹ:ѯkc^3/ |(U4A,)lYbq`RgQ}I(Jg^*μ,[l1,מ8 PH ~O7=R5Ñ2~Eq|?'\u0nEμ ^`9ס"?5g4nS[?#n3$o畏@{λ]v]cf/}nKaϧcE z?O᎔*s+Ùμ2P4p㼨ЋO}Z> 4IL v э69曧U??9/;$\<\32-F).Jx鯃@ϙ3}6!`Q,_"陫\hyy,+0Uqnd^([U{-C'9<|}}~K|ܷqFar?σCޟч|8~W3yejiV; kC<|@|urArsUWg=s.͢&qzi޼KT y ]WS.UC{" ;W32,d-jE7מGk˖s4q|9JV  >rםwH^= $sٷ5}&xI?.yE#jE#.x<#֭wu5 9=a'p<ؓ?VS+UK3z++VJ~ވ<꛱ B6uֻ<}ƟWYaȼ`̻"Gl>L}; w^7c: wQ eed^f,bMA ^etyRVqaל{?wR̫dˢzTCjXyb~5 }Lh5/x+ IKF z'P] nu pϫ#@RCEA*-eV/󪬛yUmyE^3ZPT ]</.?UU̫ i˧ZA -ate^-LkEw?Xa*ȼ"P4H&BVjCE}(HQ}(*ye!kF%Һ,* (:^R)~EVȼ"Ѵj )Erԩ4ML"N(pQG;'8pPΝ;usmq<&0FUeRW`'>IVĊff۷_`JK+%a3fLA˯&+WrpθE//^p8UyNyGޗ|eÆQSĢ2osϓ9a ST>=;y]CL3t[~ ӦNɓo;{{c o>GִiS9;V<J>]vIU6O(>)q,QWeGӝ4p>9Nry똋 GZѺuk0$$4q~WTQ jʄy`H@drAwB}{=dŗ_:m͐:H||#w_@N~LzITxHZ@?~.=[w.$4PGt``ٳ.-pM7ʸcCsu`ZbN~ 2:qWjjĉ/D0a|ɧp"6l߮m['I0;RCf !d QڷoHQxiR-[erR |.~{H~lܴI3 $ cǍ#ʺ =tp;SSeڵm'ڵse?eC$f 2!*#l4)I7ouSOv)ݫ4TGߎF~P W{`1 Q?tPin͚.~·V5o&Gt7Ȟ={<|0yeڴn8. npʺd ݻKga O>\I{eꫯr?[ȗ׎!`dN3'5Kri!=C!#޼U-:8*-IM$wE1J.\ȑ#GgllS {Mo>٫޽{Ks зiR޴i YS^mF7-zĻ7?ijtՋu",OK#:JYJ6ʎ~9vJp3gާq._B- TOYzH嗔Y@ϞBHT㝗ڱ+i@~[(?ṟĩhRST=4_7ys_f9aiϯ2@N: 9eG+GNU8l@;Oy'qiRfA;מ Wh`X5`(j Y\Cye s0h84P}^u=9|=hܦ D7GO$wt" Ù='s % J;7j wc%pq_e{s( C0#o"+!`%#`d^2F0 G< h@0 C 02&!P2F%cd! Cẓ!`yYC0#o"+!`%#`d^2F0 G< h@0 C 02&!P2F%cd! Cẓ!`yYC0P71|&))Q.}|>/}2T^ˋů5B/9b5<2oȼMl 6sQmXפ'Lg-c2 C, j@"`d-c2 C, j@"`d-c2 C, j@"kjY+qs9is@ C .7kv +(^Sa| vœ6\\]^vF @\9_# j4"B @yBc|1:?_s5URJ\6}s#5˖2 [.-_C0*# Ӓ2 C02.-_C0*# Ӓ2 C02.-_C0*ϼ ˗}GHE=a+ ~G'G\llԫWraב!P*<-!P#5*Dhvvl߾>r9yܙ*Bd ݻwsɮ]i|ȡaÆҿ?iժ\pQ=*SS酫F*U&{]wJ)? !3ȲqأJ||C_$===,IB;vzR(ۿG暂ӃZn%< R7n7|ȁ #Re !| 9~Djŋ@#R @?#̝T7ϟw~>,'NȜ9sɓ a9 H wy#CɦM%QO>PNemPt8ʘWX~5p ~M32ɭgez/^n*q-J"E*M̙erرҼys'qs41QڵoT$,7@r*G.]tNILLg3$33I.\SNWCHϸG];t,ݟI-ssi׮ϕ7R>y+'tY$ѽ7Z>Ҥ=7kLڴiD}xHi-[J~ܹs.F<(OU12e jՉ. X= 4,Kgy~\ d!hbO;nwpzĈҤIc'w}ϩT6=SoF>}|=k4Q5̤I]z{_go>'dz?J9׳pJK^{CN>=q2c-z!VZʟg> 34iDz-GuקN* ilnqqRA "˖-+oA7T#ҥ>97H>LIN$}KS+ ]JZ_%Lt& iJHPKG(ByQ#GMJ'Nt G\TH>p*@ڳG5j#nݺe˗ߨБf vy8ݺ~O$q SLVuJqn3g&N 3gΔX-3M"wn<74p9maW񉋋%KJc:h''L2-kVX@.<gҵk#܌L wӎSѧr*y._B~˟;=99Y \89OX0sIuӦM+9cƌv Q[rl9(3*]t)~'N'G.~R9|7f9Asղv:hбx9}{u0*֢U,\ C"͓y \8$nT"8FWKMMs?fTET5kwFiO?R~UrER߷oR).G݂)y&С+;v2ֵMLnܝ? $'uP$} \+!Uqٻ'C&^5w ~v ~IB)ii# X(%[ 9TgOz 7P/z(}thH._v)$/bP{惚) IL1ɼ5U' !rASRdO"#RA{8 7x |]pn ˔}8+^گuy~1ɼv*J[%SI;hWV_p42 hu:!EI9(7JRT2 #E-Z,Xqܬ !u[8p#P]PgM oA?j?(䟩-/M` opKM%| ߹jr%Ɖ2~u. iV3}|V.U4q\VJXȹz5dÛ V,y58̲MxzYEI!Any:Z3O?Aj]B i2P`GYɆI&}#}XL9XlE #+!:,_5)iD N|ʂmH;! GޘRn^t5\ G|8CQ?Qő~ euKU=+QПv&W4!df+$A၇T (sÔ/o%戴aO4p> 5> #q}8Ȗ#)Sr7TX70`^b#L\v|z;wA7ڿ/ϫ.O_h8LJpxFgd^x* {?gGҐ-?J\> qаסWҷO Htڟ8W\YWTJ3nm872 huU(q`$_IAz9"]c'ީS'8R:d_:/mCJ[J o1 ѽ{9Db:[nݺ횙Bu:1̣E<@> ,HfUUǚ4T[o"`dn0 Zy-hD!`F CׂF*!`dn}0 Zy-hD!`F CׂF*!`dn}0 Zy-hD!`F CׂF*!`dn}0 Zy-hD!`C ܰ: yk1b$i~И-Wy,(F['$4v_[أG[Xy +WL}yDtf 7iX+ #1:)g"@e熀!`PkhY C"`dD CІb!D<!PC02 g6 C y ;7 C"`d^CΊm@# vn@ Eȼ60 FA40 y m8+!`Ãhع!`5#pVlC0ѰsC0j(y m8+vt#pe]AFX^=Ư԰Wwve-_ŕ]NzlO~*ZŔR1j bll1\SsIz19~옜;܄YJeРrٰacwuoР_A233 !]~7n'O 4~p(.n]gd^ZZ%@ӦM:\ѣGe9r*w/zMJ۵k+{8p\dz iެ۷OΞ=7_;uII"'Nc:1c wFdڵpq겟y]n}{!Λ/GĦM[׮2`@yчYf2kB:q$p*6WA/4}2d>2溸Aȇ͇'߸:t]C WPww\ܬcVdgj>}IIFP؉!PSUö[iW)'ӦN۷˞={e=)1Q:u$@IDATI͒rA9вe f;K&%5upҥaÆһw/iL'=?ݻW~FO=e; =̋/Y%9dYϞ=^k#tOW_}-?{'NszN]_n فO.+VWC|B=W rߑ+WIJO=񸓐*/۵%wR3O?ryif7Mr* ^x;;z4]"F*>A"KfwulݺM N;*xyR<|>曧9"_w sa'/_ò+dw 0Fעm!PiĨdzQ F;iwM7:>wdzA'NAv8W,_-[J. >#Ҹqcyd…N]ѯo_G7$`D!P${"$?m.OU~NsE hزɂ 9{2dN"G|%^izq{)'(U>Dl߰H)Hp qH~[ou3,ErSV8g.<Lx\ A=7Hz ԚGyU ևDM:'?pNU2?|)'/X@BvIndʔ>z#d7HUzc03/TJG8vGTjVײeK;U[H_=٠cةSG9|tٙ;(q%o_Py[WSFT#$ g.^0͢8>Tݓe7Y3h=䓪j(;vdԶ<=?E۷Pkdyeᪿǽpt>C;uCTUѨA6o"v%K%Cy)rYf2]z#]TElrZ_,:j#ibv7~BCJ/oox7M,axj뎕 .4FrPg@ڔ/#9k.R6ӷ@O$WZcFCwO\=h7s$F-k&mڴqRzLQbr.~Xչ:>H\ټysSFÛfgdfɋ*qKe j:"tqƃ2UV322qχ!7’G^>} Ppɽ\&jr)%n#`:ix\ ix)î>H> iP@:Sʀ 搧Nv9ŹҶb8U(+[4<#~/N|hyYx@1`x4z$fW$R4f/VO;/44|9BycSP6%E1\y뼝c]=P~s"`d~-&c,.Bn~$k_pr8+)/C]<W[lMe^ZNW(?syI: U"WТ[ pxi=t,Fݏ4n˦.5| G[]0? W~^ C, +jfKZ )(d/;5 5G yEŮkZ3Ji@ 4 Cf `d^3Ji@ 4 Cf `d^3Ji@ 4 Cf `d^3Ji@ 4 Cf `d^3Ji@ 4 Cf `d^3Ji@ 4 Cf `d^3Ji@g㊅nE72eu3)~zUȼ1Vib԰~ߤjX* >ݤI#/~4FlCB?s6}>ؚj)1d$!jy#׬&@׉*(=Kv /.mc%3 C b#!GoX C#󈡲!`D/F6V2C0"Ft'bcc+/_\ϜTG0ʏX 錗#9hq,ԾTSpkhT>`@ۏ3J6MĘ(qHzzܙ*ϟw}2H#F vڵСWI$M ÆIdBe~֭]9?.V.Y1/QA/ɓrl\pQ.^$ :[=X> ԗF񒐐 -[4VZa 2ĉCcϞoѣG 5ƌ-r ˇϦ Mu~'+(yJ5ir)׹XDht`9r$]tپct[#PjmdmJJ1J`oҫgOU4ui8*OVi%_BBHSm6KIN()))nfH"w'%%INq"{U$$&t=Bm۶4]-[jwѓU0|$ʖ-[~cǎc8tŊq&]0lnյ 2ۧqN;oWUqcw^UKYgɡÇKΎ!%KJ-T"^vf^P)u{șy|Z ]kҹY-$Eg(s_-$G*kYu-$"@H3kֲ~ЁOTcڍCB?z*qЁxq{ׅJm۶u~su?941Q^{u~kt1U9<ԓ2ewP3ڢŋ>r!o~+ir%I&\|'`V^Ƃ|j2ԑeE^%5,h∃;++5_@ =O\.B”|~rO_yCM6׭]+pΝ^͛IcY).> Q \>dN']zmy^S#mHGڡWS+!L?e=3t!C3O?YKACAgWL~SI C_z5HJ.t1Eh;>Hsߠ=28E'Q;ai Q#G 5 ݻu+Ȃ<) 8L\ &S-C|ȐN7_W_#8`ۿY`eӻlڴY>DV4Q3hԨS>hOxț FCH_'3gκ(,2s9}bU`̧,瞐iמ9{3 &/ڞUjXmw |Ci龍,^Ў.CtyuXSuNKa:oAH;ztd޾õ/VYy;ȑ#Kp,Ax/oBI1?xL8GZhKGWh>0GΑ,]*}˯~ ٫/5oL ;9O>ԥl ]TW#ݻ 8./z!-Z/3wYTrg֐85EF=֭Z ۿlTbeaі8IL]Grםw܋yݺ Hu 0Ҝfbw[FYsU%mbY ;/L@_YOu.fd%lQir3ՏvUR֭[i3萑 H?1zl[yRRS}z 7Vi'L SLv!S;wJ֥,]ߧ\(.5̔f /7U.dPWw7l(3f"z 1?0Hn}2:]*Mf93U$e˗zuU[ 8H"/{E.$],KU}vgMv#uJ@R|Nzq$-l9:^Ji*Զ yBn8/uزeλNB};mۨ-z mU'P 1!3L?8\r2T2)~'z5߾}{s-_B\s'4\⟘ .;}{:rrGqvAs̓eKxzIJw1WP\яq8BBHZ; JŵE>9{| CSVikOr>O Ӈ]9O;\>H퉹Sq8v]{3nW5_x:%Vbɔ4>/k-Ѝcxv7=TB`;thtl_u'rם;:Bgu`Fa!C:@Nnd^.:ftÐ~nz*3DZy:EʒyRH8i܇FB uy2gkVz]GZ{KsXb!=,EƏ.l l"܃`'\a;ybN+ꄼ&MQb=1cy,3nZ_CEcN0t`[fQt"-sG}Fw]Qc #=rZLhV2*>|Iztͱ2ۛ"B,T1"M'.4Y EMjyG:d8߰aVI[~qAcY(3 d.,xPB_ %r$/)/rMCi5 G/OGߠ_l^EtԑOG<@3YrGxsrF K,lKꉝt)1 aP+zK%qıkgC(T*XhlϑKSHm Cz?Jΐ93HRyr^Cg VXRYD~H۞ |" :/A| 6.Bl$e0!PZС+>ҦXg:^͇U>O_Y1I⥋ٮi1cw~9&|sv'D9#i5RI$KHj Ct*2Nݨ3:sT!dCV-S!wNPoU`¾,1[Y$EŃmEgd+s!PR2?y!stرq)~cE`H=Vxij2kaxaRf[yE8rJ7:"yg[~a % cĉ`GE !E`@8PL]_Kc^t?lfKb[H,F]AN̼\ιk㳤+>]UMkES CHlS ?-Â-Xo ><1(z/]XHNv @7_HƧI|Yb _gVably}6)ή|] _}5yC|kW[O޽{9\?9 k܂ْx}w)$YQ9~~ 2C Z@ڢL 1ƉL{Ld2EV\G8 Fx=IT1AʼϢe}%;Ct))J1Nf`hۦSQ&8S}EG/E }hZQifA}ȟzq#"Lx /1,XmAcߢܸ$s4;9*.,NbT Ƚt8U$s֧Cid;ra97K9xJ)}In~ @az~$ߗH"/GsD7oH& ֭1Б7o,Q G ɟѷAj?L~ X]eKG)iq}?RGTjiݪUkt1nи7x>sX1s] xŕ~=|ʒeKlٲd%7Xf!'n!!!r-!$6K6`ll|elY)3H##k^uwuuu~=yhsn?f|5"#-W =rg$`GSy yĉ_ UH}\wJϑڷJBiܽH8`e?IQc.>"wRBXOۿ^ wNw8'ȑ/K satO:tt%Lms*7PyPW͒1ϧcD>2 #H@QrXշbq vA+\Diz:ʃ*YDU U"7:~M)sҀlwFI|/~K6 &6vڴo1?n2A'hav0EISs ׯyO@?r{E9E ~N FȷaF筒}ɧfk/"x\ҧ߽b4z_0 S)/=+W+ײ;BԮX&@#NAya لA^Cu]H-'rO+ZWݐ)FrN@9INJRuw5UH4OTC=cwDo&٣I),iMBɼڒ,*xqQ`m贠V1Ɛ8 \}K^ʺ 3ΒC^׮#Pnkq=t(`B"tq1'6D9{HVq4-eiشAj/&[LFaXpypA&'j< 8G<~wYxGPKs4=NErHBsEULSb7DWA6%>Q2 뎆Mۘ/ª-XO#FG02ǎ2:&3ɲ/dIѦ÷"Hr^eszڌƿ9ck :nEA<׿g}B MH7)OŦ"dް} ,A%_9μLP,yǜzh=[]Xz5SKfaav3&r!dKDl5THV]> |Ek Y>K++qJdѽ'&zA91=?쉫)dGu #ZN/frg؋i !ǙTpQ3uX*&>L4d 3 2dQRcH$x$'=hݒ5|;faVH>5{_⤬}R ){"jjp,1TP6d4HFޯL|Z.0_n "?T92vlZ~a5obs(*K8ޙ8SGU>B*'VwpgG#pQj@ԧ)9و3$,0gTXEF2E}kaÆ/W{qlɧ|5Ї /`fΘ>0Ӓzsft%>oYV* u!ĀDʠ_nfӳ4URK7)#zR| %7˛rvB}Q8e@雒}NQ 2c?9{@kٰN^9 n>NWE,)؃u!ԏqzs1S3J?2' bƎ:ȖΡٺuqEVHA= ͘9~XhRy%I$HTqKXٳ>H対6ǤΏoJӦ9c {ȲF5[O~޽)𐎙rTN @E%_DCJb1IJ&_pn*.jT?t!N6_oKrt"X'dH-T@eX2ry+~RڄoI3悁yH uF(KN6{T_Q}N:uT9{ʩNETY9paHr .O&fO"% Lcu䩬uq3蓩ŗMI~Mȹ2yJ*g?(r=ZA v2t-f>mfN3ڣtD fj)snjig9/}G} ,0Ĺ'LX\qc>&'6v%l: zjӄdNr-:fځꅾ@wN{̀I6PFٚC=2zJSTD #IZ|ͷTQ*JD@ >-zZxy''9:L_fmBu {dFkF!as0+BJpn5E-˶!Pr;^¸1|T Ed пv0Ls"r*kѐeMq h釅:sKԹ9PI:,c$-g4oШT2?֨$@!ƇKk (AI'3u78TlX^cpb=O1E٩)Cդt*0)Z Zmއc(6=ɜȐ'NobuʅflHm00D"tmUZ-Wn5ݖYpI r9:HfH˖lS(sIutsi}"*Fs,nNxH-зOx[@pR}Pq޹gX%tSE꬇X?Q6TB2sH-$5 ?-3iC>~))"7m6&kI,fM HaYVf*QfbCxM'| }Ne /+ρ%ƦŽI.dn()FHu ɷ/\%2?opYsD3ne!rAYzbr}cfS߬Qd \saER^P gz<8_ 7m"a@ %t?lF-u x7qpӯg@IqXscP E t$?Ƈ;BB %R 1%K&&yo/Ϟ UJT,Fqxn7 &^\RB~&: 9[AS/s~_ ,cp}qXhtKH)¯O^8=hmBxHy*ϫWF轹7K֙&j\YQa:ww2*S 1dM"&GBvq y^xБ J)}@Гf'o?S&}(FMR$qn! skBUTmD3Ff!?dKɛ=A9PP·9HʉR7D1¨lx LkOTK $oI?Q%qt771 Z]y@2<ɚj GSa(Cz֙8HjEI<Lx64L1jvEc/?(j)8JZ_?wΆefaC%"؍ QJ~"g^iSF8ه:v=HƩ3~'$#PXNggJPޝTJ*CglG+<ĽJq_޵:dLm"oi&rR5K Todۀj-aڲ,B_ 0I=`\7iRN˖*H7mTeݬ^$ uᗎ-s/7u\6f ;N.?{2sNo6M)F7 q^ܱs"#_}¯}Uq?%~ύTl{c-EP.ށNo$\ZЮ0&ߔԟ}X:,I1 V,DK*{d2 X-kb Q\ll9ZNc%f=Ce"@K#oH@q{acL9r@(LIqX'=M@@8̖ں?}ǞX?S,LJjZjU't<~>dtYyz9cm4.<l]")+"G >3䢚2eMzh2}*p@śMϛ[R(I!%x-}Vn,AvۖK6?(hZgYFJ붣 kh-P2^8$L>GG_ >@SE@Ldޙ$b-ݎYow+E#hwT|D 5]A s/kR1d><,Ӣy\Gw:]u!9S Im"&#%nKM>ݶ͘uWMuh |831 3]&u4#@;FBrKiTW tR'G&Χ2Zn`ki%u\פ$B=" ~4b;Y%"ӯ({=F\NK֟ RGλ{cFbPP[MlIDAT\-q_}T'EP(y*ץJ^b]ڇzp9G83_*ΘAЃoxW?Dvmn8t+ NN`d޾[GB%)tIԓ̴;Q"2Z[2mݗ y/x\KD7 4oٔ["Xl~j.[.&) sJGQjb"XL>_\Z,ˏc2'-Y}P2]2l]U ?I_!IF i%q^];"Wȫ;wpj>'-ҋh8])gホ{SK-6ivr9;6[M;h3aWǩ§#|r`qֶ Fb9ڥ+A/Kj׽mDe &9 ûn6Q=@キv#.c>DVɪ(Cep]g!(4jR8n&w7יKo):%o׽Kカ|!XOER/ |?KQL0-.kkb+_ ]v)K]^LAc/,K۟l)r%"cQS->s/󛳱$-TEd"`h> n7,Y ` N/L/yL("xQ &-TEd"6uS}D 1:sgLuqA ! 87Rm ?8WRE Ċ@ ApbMnoݧ΍@= s[n}.=vuA&i[ɜn)'dA{EOJlC=jE/%]{<6y1shDnX45 cU;VypЩ~ĢrRLТ7"cpgyĺ@O¼jKĖv=TFNdΟWBW)aP#%w2q׾%~,"Io[K\q}D#2B->/Trb%,t- DEn {0ū[F;7{m{_]F\cG@Էso%X4@|?k/ F ~ 4֊{`3,Y-}́FҖM[ΖeCJ቞^5?cIb`]/dwIENDB`PyNamecheap-0.0.3/namecheap-api-cli000077500000000000000000000047121323172663400171020ustar00rootroot00000000000000#!/usr/bin/env python import argparse from namecheap import Api, ApiError try: from credentials import api_key, username, ip_address except: pass def get_args(): parser = argparse.ArgumentParser(description="CLI tool to manage NameCheap.com domain records") parser.add_argument("--debug", action="store_true", help="If set, enables debug output") parser.add_argument("--sandbox", action="store_true", help="If set, forcing usage of Sandbox API, see README.md for details") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--add", action="store_true", help="Use to add a record") group.add_argument("--delete", action="store_true", help="Use to remove a record") group.add_argument("--list", action="store_true", help="List existing records") parser.add_argument("--domain", type=str, default="example.org", help="Domain to manage, default is \"example.org\", don't forget to override") parser.add_argument("--type", type=str, default="A", help="Record type, default is \"A\"") parser.add_argument("--name", type=str, default="test", help="Record name, default is \"test\"") parser.add_argument("--address", type=str, default="127.0.0.1", help="Address for record to point to, default is \"127.0.0.1\"") parser.add_argument("--ttl", type=int, default=300, help="Time-To-Live, in seconds, default is 300") args = parser.parse_args() return args def list_records(): return api.domains_dns_getHosts(domain) def record_delete(hostname, address, record_type="A", ttl=300): record = { "Type": record_type, "Name": hostname, "Address": address, "TTL": str(ttl) } api.domains_dns_delHost(domain, record) def record_add(record_type, hostname, address, ttl=300): record = { "Type": record_type, "Name": hostname, "Address": address, "TTL": str(ttl) } api.domains_dns_addHost(domain, record) args = get_args() domain = args.domain print("domain: %s" % domain) api = Api(username, api_key, username, ip_address, sandbox=args.sandbox, debug=args.debug) if args.add: record_add( args.type, args.name, args.address, args.ttl ) elif args.delete: record_delete( args.name, args.address, args.type ) elif args.list: for line in list_records(): print("\t%s \t%s\t%s -> %s" % (line["Type"], line["TTL"], line["Name"], line["Address"])) PyNamecheap-0.0.3/namecheap.py000066400000000000000000000412311323172663400162070ustar00rootroot00000000000000import sys import time import requests # pip install requests from xml.etree.ElementTree import fromstring inPy3k = sys.version_info[0] == 3 # http://developer.namecheap.com/docs/doku.php?id=overview:2.environments ENDPOINTS = { # To use # 1) browse to http://www.sandbox.namecheap.com/ # 2) create account, my account > manage profile > api access > enable API # 3) add your IP address to whitelist 'sandbox': 'https://api.sandbox.namecheap.com/xml.response', 'production': 'https://api.namecheap.com/xml.response', } NAMESPACE = "http://api.namecheap.com/xml.response" # default values for the retry mechanism DEFAULT_ATTEMPTS_COUNT = 1 # no retries DEFAULT_ATTEMPTS_DELAY = 0.1 # in seconds # https://www.namecheap.com/support/api/error-codes.aspx class ApiError(Exception): def __init__(self, number, text): Exception.__init__(self, '%s - %s' % (number, text)) self.number = number self.text = text class Api(object): # Follows API spec capitalization in variable names for consistency. def __init__(self, ApiUser, ApiKey, UserName, ClientIP, sandbox=True, debug=True, attempts_count=DEFAULT_ATTEMPTS_COUNT, attempts_delay=DEFAULT_ATTEMPTS_DELAY): self.ApiUser = ApiUser self.ApiKey = ApiKey self.UserName = UserName self.ClientIP = ClientIP self.endpoint = ENDPOINTS['sandbox' if sandbox else 'production'] self.debug = debug self.payload_limit = 10 # After hitting this lenght limit script will move payload from POST params to POST data self.attempts_count = attempts_count self.attempts_delay = attempts_delay # https://www.namecheap.com/support/api/methods/domains/create.aspx def domains_create( self, DomainName, FirstName, LastName, Address1, City, StateProvince, PostalCode, Country, Phone, EmailAddress, Address2=None, years=1, WhoisGuard=False ): """ Registers a domain name with the given contact info. Example of a working phone number: +81.123123123 For simplicity assumes one person acts as all contact types.""" contact_types = ['Registrant', 'Tech', 'Admin', 'AuxBilling'] extra_payload = { 'DomainName': DomainName, 'years': years } if WhoisGuard: extra_payload.update({ 'AddFreeWhoisguard': 'yes', 'WGEnabled': 'yes', }) for contact_type in contact_types: extra_payload.update({ '%sFirstName' % contact_type: FirstName, '%sLastName' % contact_type: LastName, '%sAddress1' % contact_type: Address1, '%sCity' % contact_type: City, '%sStateProvince' % contact_type: StateProvince, '%sPostalCode' % contact_type: PostalCode, '%sCountry' % contact_type: Country, '%sPhone' % contact_type: Phone, '%sEmailAddress' % contact_type: EmailAddress, }) if Address2: extra_payload['%sAddress2' % contact_type] = Address2 self._call('namecheap.domains.create', extra_payload) def _payload(self, Command, extra_payload={}): """Make dictionary for passing to requests.post""" payload = { 'ApiUser': self.ApiUser, 'ApiKey': self.ApiKey, 'UserName': self.UserName, 'ClientIP': self.ClientIP, 'Command': Command, } # Namecheap recommends to use HTTPPOST method when setting more than 10 hostnames # https://www.namecheap.com/support/api/methods/domains-dns/set-hosts.aspx if len(extra_payload) < self.payload_limit: payload.update(extra_payload) extra_payload = {} return payload, extra_payload def _fetch_xml(self, payload, extra_payload = None): """Make network call and return parsed XML element""" attempts_left = self.attempts_count while attempts_left > 0: if extra_payload: r = requests.post(self.endpoint, params=payload, data=extra_payload) else: r = requests.post(self.endpoint, params=payload) if 200 <= r.status_code <= 299: break if attempts_left <= 1: # Here we provide 1 error code which is not present in official docs raise ApiError('1', 'Did not receive 200 (Ok) response') if self.debug: print('Received status %d ... retrying ...' % (r.status_code)) time.sleep(self.attempts_delay) attempts_left -= 1 if self.debug: print("--- Request ---") print(r.url) print(extra_payload) print("--- Response ---") print(r.text) xml = fromstring(r.text) if xml.attrib['Status'] == 'ERROR': # Response namespace must be prepended to tag names. xpath = './/{%(ns)s}Errors/{%(ns)s}Error' % {'ns': NAMESPACE} error = xml.find(xpath) raise ApiError(error.attrib['Number'], error.text) return xml def _call(self, Command, extra_payload={}): """Call an API command""" payload, extra_payload = self._payload(Command, extra_payload) xml = self._fetch_xml(payload, extra_payload) return xml class LazyGetListIterator(object): """When listing domain names, only one page is returned initially. The list needs to be paged through to see all. This iterator gets the next page when necessary.""" def _get_more_results(self): xml = self.api._fetch_xml(self.payload) xpath = './/{%(ns)s}CommandResponse/{%(ns)s}DomainGetListResult/{%(ns)s}Domain' % {'ns': NAMESPACE} domains = xml.findall(xpath) for domain in domains: self.results.append(domain.attrib) self.payload['Page'] += 1 def __init__(self, api, payload): self.api = api self.payload = payload self.results = [] self.i = -1 def __iter__(self): return self def __next__(self): self.i += 1 if self.i >= len(self.results): self._get_more_results() if self.i >= len(self.results): raise StopIteration else: return self.results[self.i] next = __next__ # https://www.namecheap.com/support/api/methods/domains-dns/set-default.aspx def domains_dns_setDefault(self, domain): sld, tld = domain.split(".") self._call("namecheap.domains.dns.setDefault", { 'SLD': sld, 'TLD': tld }) # https://www.namecheap.com/support/api/methods/domains/check.aspx def domains_check(self, domains): """Checks the availability of domains. For example api.domains_check(['taken.com', 'apsdjcpoaskdc.com']) Might result in { 'taken.com' : False, 'apsdjcpoaskdc.com' : True } """ # For convenience, allow a single domain to be given if not inPy3k: if isinstance(domains, basestring): return self.domains_check([domains]).items()[0][1] else: if isinstance(domains, str): return list(self.domains_check([domains]).items())[0][1] extra_payload = {'DomainList': ",".join(domains)} xml = self._call('namecheap.domains.check', extra_payload) xpath = './/{%(ns)s}CommandResponse/{%(ns)s}DomainCheckResult' % {'ns': NAMESPACE} results = {} for check_result in xml.findall(xpath): results[check_result.attrib['Domain']] = check_result.attrib['Available'] == 'true' return results @classmethod def _tag_without_namespace(cls, element): return element.tag.replace("{%s}" % NAMESPACE, "") @classmethod def _list_of_dictionaries_to_numbered_payload(cls, l): """ [ {'foo' : 'bar', 'cat' : 'purr'}, {'foo' : 'buz'}, {'cat' : 'meow'} ] becomes { 'foo1' : 'bar', 'cat1' : 'purr', 'foo2' : 'buz', 'cat3' : 'meow' } """ return dict(sum([ [(k + str(i + 1), v) for k, v in d.items()] for i, d in enumerate(l) ], [])) @classmethod def _elements_names_fix(self, host_record): """This method converts received message to correct send format. API answers you with this format: { 'Name' : '@', 'Type' : 'URL', 'Address' : 'http://news.ycombinator.com', 'MXPref' : '10', 'TTL' : '100' } And you should convert it to this one in order to sync the records: { 'HostName' : '@', 'RecordType' : 'URL', 'Address' : 'http://news.ycombinator.com', 'MXPref' : '10', 'TTL' : '100' } """ conversion_map = [ ("Name", "HostName"), ("Type", "RecordType") ] for field in conversion_map: # if source field exists if field[0] in host_record: # convert it to target field and delete old one host_record[field[1]] = host_record[field[0]] del(host_record[field[0]]) return host_record # https://www.namecheap.com/support/api/methods/domains/get-contacts.aspx def domains_getContacts(self, DomainName): """Gets contact information for the requested domain. There are many categories of contact info, such as admin and billing. The returned data is like: { 'Admin' : {'FirstName' : 'John', 'LastName' : 'Connor', ...}, 'Registrant' : {'FirstName' : 'Namecheap.com', 'PhoneExt' : None, ...}, ... } """ xml = self._call('namecheap.domains.getContacts', {'DomainName': DomainName}) xpath = './/{%(ns)s}CommandResponse/{%(ns)s}DomainContactsResult/*' % {'ns': NAMESPACE} results = {} for contact_type in xml.findall(xpath): fields_for_one_contact_type = {} for contact_detail in contact_type.findall('*'): fields_for_one_contact_type[self._tag_without_namespace(contact_detail)] = contact_detail.text results[self._tag_without_namespace(contact_type)] = fields_for_one_contact_type return results # https://www.namecheap.com/support/api/methods/domains-dns/set-hosts.aspx def domains_dns_setHosts(self, domain, host_records): """Sets the DNS host records for a domain. Example: api.domains_dns_setHosts('example.com', [ { 'HostName' : '@', 'RecordType' : 'URL', 'Address' : 'http://news.ycombinator.com', 'MXPref' : '10', 'TTL' : '100' } ])""" extra_payload = self._list_of_dictionaries_to_numbered_payload(host_records) sld, tld = domain.split(".") extra_payload.update({ 'SLD': sld, 'TLD': tld }) self._call("namecheap.domains.dns.setHosts", extra_payload) # https://www.namecheap.com/support/api/methods/domains-dns/set-custom.aspx def domains_dns_setCustom(self, domain, host_records): """Sets the domain to use the supplied set of nameservers. Example: api.domains_dns_setCustom('example.com', { 'Nameservers' : 'ns1.example.com,ns2.example.com' })""" extra_payload = host_records sld, tld = domain.split(".") extra_payload['SLD'] = sld extra_payload['TLD'] = tld self._call("namecheap.domains.dns.setCustom", extra_payload) # https://www.namecheap.com/support/api/methods/domains-dns/get-hosts.aspx def domains_dns_getHosts(self, domain): """Retrieves DNS host record settings. Note that the key names are different from those you use when setting the host records.""" sld, tld = domain.split(".") extra_payload = { 'SLD': sld, 'TLD': tld } xml = self._call("namecheap.domains.dns.getHosts", extra_payload) xpath = './/{%(ns)s}CommandResponse/{%(ns)s}DomainDNSGetHostsResult/*' % {'ns': NAMESPACE} results = [] for host in xml.findall(xpath): results.append(host.attrib) return results def domains_dns_addHost(self, domain, host_record): """This method is absent in original API. The main idea is to let user add one record while having zero knowledge about the others. Method gonna get full records list, add single record and push it to the API. Example: api.domains_dns_addHost('example.com', { "RecordType": "A", "HostName": "test", "Address": "127.0.0.1", "MXPref": 10, "TTL": 1800 }) """ host_records_remote = self.domains_dns_getHosts(domain) print("Remote: %i" % len(host_records_remote)) host_records_remote.append(host_record) host_records_remote = [self._elements_names_fix(x) for x in host_records_remote] print("To set: %i" % len(host_records_remote)) extra_payload = self._list_of_dictionaries_to_numbered_payload(host_records_remote) sld, tld = domain.split(".") extra_payload.update({ 'SLD': sld, 'TLD': tld }) self._call("namecheap.domains.dns.setHosts", extra_payload) def domains_dns_delHost(self, domain, host_record): """This method is absent in original API as well. It executes non-atomic remove operation over the host record which has the following Type, Hostname and Address. Example: api.domains_dns_delHost('example.com', { "RecordType": "A", "HostName": "test", "Address": "127.0.0.1" }) """ host_records_remote = self.domains_dns_getHosts(domain) print("Remote: %i" % len(host_records_remote)) host_records_new = [] for r in host_records_remote: cond_type = r["Type"] == host_record["Type"] cond_name = r["Name"] == host_record["Name"] cond_addr = r["Address"] == host_record["Address"] if cond_type and cond_name and cond_addr: # skipping this record as it is the one we want to delete pass else: host_records_new.append(r) host_records_new = [self._elements_names_fix(x) for x in host_records_new] print("To set: %i" % len(host_records_new)) # Check that we delete not more than 1 record at a time if len(host_records_remote) != len(host_records_new) + 1: sys.stderr.write( "Something went wrong while removing host record, delta > 1: %i -> %i, aborting API call.\n" % ( len(host_records_remote), len(host_records_new) ) ) return False extra_payload = self._list_of_dictionaries_to_numbered_payload(host_records_new) sld, tld = domain.split(".") extra_payload.update({ 'SLD': sld, 'TLD': tld }) self._call("namecheap.domains.dns.setHosts", extra_payload) # https://www.namecheap.com/support/api/methods/domains-dns/get-list.aspx def domains_getList(self, ListType=None, SearchTerm=None, PageSize=None, SortBy=None): """Returns an iterable of dicts. Each dict represents one domain name the user has registered, for example { 'Name': 'coolestfriends.com', 'Created': '04/11/2012', 'Expires': '04/11/2018', 'ID': '8385859', 'AutoRenew': 'false', 'IsLocked': 'false', 'User': 'Bemmu', 'IsExpired': 'false', 'WhoisGuard': 'NOTPRESENT' } """ # The payload is a dict of GET args that is passed to # the lazy-loading iterator so that it can know how to # get more results. extra_payload = {'Page': 1} if ListType: extra_payload['ListType'] = ListType if SearchTerm: extra_payload['SearchTerm'] = SearchTerm if PageSize: extra_payload['PageSize'] = PageSize if SortBy: extra_payload['SortBy'] = SortBy payload, extra_payload = self._payload('namecheap.domains.getList', extra_payload) return self.LazyGetListIterator(self, payload) PyNamecheap-0.0.3/namecheap_tests.py000066400000000000000000000202011323172663400174230ustar00rootroot00000000000000# Run "nosetests" on command line to run these. from namecheap import Api, ApiError from nose.tools import * # pip install nose api_key = '' # You create this on Namecheap site username = '' ip_address = '' # Your IP address that you whitelisted on the site # If you prefer, you can put the above in credentials.py instead try: from credentials import api_key, username, ip_address except: pass def random_domain_name(): import random from time import gmtime, strftime domain_name = "%s-%s.com" % (strftime("%Y%m%d-%H%M%S", gmtime()), random.randint(0, 10**16)) return domain_name def test_domain_taken(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = "google.com" assert_equal(api.domains_check(domain_name), False) def test_domain_available(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = random_domain_name() assert_equal(api.domains_check(domain_name), True) def test_register_domain(): api = Api(username, api_key, username, ip_address, sandbox=True) # Try registering a random domain. Fails if exception raised. domain_name = random_domain_name() api.domains_create( DomainName=domain_name, FirstName='Jack', LastName='Trotter', Address1='Ridiculously Big Mansion, Yellow Brick Road', City='Tokushima', StateProvince='Tokushima', PostalCode='771-0144', Country='Japan', Phone='+81.123123123', EmailAddress='jack.trotter@example.com' ) return domain_name def test_domains_getList(): api = Api(username, api_key, username, ip_address, sandbox=True) iter(api.domains_getList()) @raises(ApiError) def test_domains_dns_setDefault_on_nonexisting_domain(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = random_domain_name() # This should fail because the domain does not exist api.domains_dns_setDefault(domain_name) def test_domains_dns_setDefault_on_existing_domain(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = test_register_domain() api.domains_dns_setDefault(domain_name) def test_domains_getContacts(): # How would I test for this? This needs a known registered # domain to get the contact info for, but in sandbox won't # have any. pass def test_domains_dns_setHosts(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = test_register_domain() api.domains_dns_setHosts( domain_name, [{ 'HostName': '@', 'RecordType': 'URL', 'Address': 'http://news.ycombinator.com', 'MXPref': '10', 'TTL': '100' }] ) # # I wasn't able to get this to work on any public name servers that I tried # including the ones used in their own example: # dns1.name-servers.com # dns2.name-server.com # Using my own Amazon Route53 name servers the test works fine but I didn't # want to embed my own servers # Adjust the name servers below to your own and uncomment the test to run # def test_domains_dns_setCustom(): # api = Api(username, api_key, username, ip_address, sandbox=True) # domain_name = test_register_domain() # result = api.domains_dns_setCustom( # domain_name, { 'Nameservers' : 'ns1.google.com,ns2.google.com' } # ) def test_domains_dns_getHosts(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = test_register_domain() api.domains_dns_setHosts( domain_name, [{ 'HostName': '@', 'RecordType': 'URL', 'Address': 'http://news.ycombinator.com', 'MXPref': '10', 'TTL': '100' }, { 'HostName': '*', 'RecordType': 'A', 'Address': '1.2.3.4', 'MXPref': '10', 'TTL': '1800' }] ) hosts = api.domains_dns_getHosts(domain_name) # these might change del hosts[0]['HostId'] del hosts[1]['HostId'] expected_result = [ { 'Name': '*', 'Address': '1.2.3.4', 'TTL': '1800', 'Type': 'A', 'MXPref': '10', 'AssociatedAppTitle': '', 'FriendlyName': '', 'IsActive': 'true', 'IsDDNSEnabled': 'false' }, { 'Name': '@', 'Address': 'http://news.ycombinator.com', 'TTL': '100', 'Type': 'URL', 'MXPref': '10', 'AssociatedAppTitle': '', 'FriendlyName': '', 'IsActive': 'true', 'IsDDNSEnabled': 'false' } ] assert_equal(hosts, expected_result) def test_domains_dns_addHost(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = test_register_domain() api.domains_dns_setHosts( domain_name, [{ 'HostName': '@', 'RecordType': 'URL', 'Address': 'http://news.ycombinator.com' }] ) api.domains_dns_addHost( domain_name, { 'Name': 'test', 'Type': 'A', 'Address': '1.2.3.4', 'TTL': '100' } ) hosts = api.domains_dns_getHosts(domain_name) # these might change del hosts[0]['HostId'] del hosts[1]['HostId'] expected_result = [ { 'Name': 'test', 'Address': '1.2.3.4', 'TTL': '100', 'Type': 'A', 'MXPref': '10', 'AssociatedAppTitle': '', 'FriendlyName': '', 'IsActive': 'true', 'IsDDNSEnabled': 'false' }, { 'Name': '@', 'Address': 'http://news.ycombinator.com', 'TTL': '1800', 'Type': 'URL', 'MXPref': '10', 'AssociatedAppTitle': '', 'FriendlyName': '', 'IsActive': 'true', 'IsDDNSEnabled': 'false' } ] assert_equal(hosts, expected_result) def test_domains_dns_bulkAddHosts(): api = Api(username, api_key, username, ip_address, sandbox=True) api.payload_limit = 3 domain_name = test_register_domain() api.domains_dns_setHosts( domain_name, [{ 'HostName': '@', 'RecordType': 'URL', 'Address': 'http://news.ycombinator.com' }] ) for i in range(1, 10): api.domains_dns_addHost( domain_name, {'Name': "test" + str(i), 'Type': 'A', 'Address': '1.2.3.4', 'TTL': '60'} ) hosts = api.domains_dns_getHosts(domain_name) if len(hosts) == 10: return True return False def test_domains_dns_delHost(): api = Api(username, api_key, username, ip_address, sandbox=True) domain_name = test_register_domain() api.domains_dns_setHosts( domain_name, [{ 'HostName': '@', 'RecordType': 'URL', 'Address': 'http://news.ycombinator.com', 'TTL': '200' }, { 'HostName': 'test', 'RecordType': 'A', 'Address': '1.2.3.4' }] ) api.domains_dns_delHost( domain_name, { 'Name': 'test', 'Type': 'A', 'Address': '1.2.3.4' } ) hosts = api.domains_dns_getHosts(domain_name) # these might change del hosts[0]['HostId'] expected_result = [ { 'Name': '@', 'Address': 'http://news.ycombinator.com', 'TTL': '200', 'Type': 'URL', 'MXPref': '10', 'AssociatedAppTitle': '', 'FriendlyName': '', 'IsActive': 'true', 'IsDDNSEnabled': 'false' } ] assert_equal(hosts, expected_result) def test_list_of_dictionaries_to_numbered_payload(): x = [ {'foo': 'bar', 'cat': 'purr'}, {'foo': 'buz'}, {'cat': 'meow'} ] result = Api._list_of_dictionaries_to_numbered_payload(x) expected_result = { 'foo1': 'bar', 'cat1': 'purr', 'foo2': 'buz', 'cat3': 'meow' } assert_equal(result, expected_result) PyNamecheap-0.0.3/setup.cfg000066400000000000000000000000501323172663400155270ustar00rootroot00000000000000[metadata] description-file = README.md PyNamecheap-0.0.3/setup.py000066400000000000000000000014531323172663400154300ustar00rootroot00000000000000 import os from setuptools import setup def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() setup( name='PyNamecheap', version='0.0.3', url='https://github.com/Bemmu/PyNamecheap', license='MIT', author='Bemmu Sepponen', author_email='me@bemmu.com', description='Namecheap API client in Python', py_modules=['namecheap'], platforms='any', install_requires=['requests'], classifiers=[ 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules' ] )