From c32fd6e7b71855cb42a2980b11343e3894b55c50 Mon Sep 17 00:00:00 2001 From: ayame-git Date: Thu, 11 May 2017 05:56:59 +0300 Subject: [PATCH] torrent upload --- router/apiHandler.go | 67 ++++++++++++++++++++++++++----------------- service/api/api.go | 51 +++++++++++++++++++++++++++++--- test.torrent | Bin 0 -> 16156 bytes 3 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 test.torrent diff --git a/router/apiHandler.go b/router/apiHandler.go index 2939ea50..991ae7ac 100644 --- a/router/apiHandler.go +++ b/router/apiHandler.go @@ -6,6 +6,7 @@ import ( "html" "net/http" "strconv" + "strings" "time" "github.com/ewhal/nyaa/config" @@ -100,22 +101,22 @@ func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { return } + token := r.Header.Get("Authorization") + user := model.User{} + db.ORM.Where("api_token = ?", token).First(&user) //i don't like this + if user.ID == 0 { + http.Error(w, apiService.ErrApiKey.Error(), http.StatusForbidden) + return + } + + upload := apiService.TorrentRequest{} + var filesize int64 + contentType := r.Header.Get("Content-Type") if contentType == "application/json" { - token := r.Header.Get("Authorization") - user := model.User{} - db.ORM.Where("api_token = ?", token).First(&user) //i don't like this - if user.ID == 0 { - http.Error(w, apiService.ErrApiKey.Error(), http.StatusForbidden) - return - } defer r.Body.Close() - //verify token - //token := r.Header.Get("Authorization") - - upload := apiService.TorrentRequest{} d := json.NewDecoder(r.Body) if err := d.Decode(&upload); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -126,27 +127,41 @@ func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), code) return } + } else if strings.HasPrefix(contentType, "multipart/form-data") { - torrent := model.Torrent{ - Name: upload.Name, - Category: upload.Category, - SubCategory: upload.SubCategory, - Status: 1, - Hash: upload.Hash, - Date: time.Now(), - Filesize: 0, //? - Description: upload.Description, - UploaderID: user.ID, - Uploader: &user, - } + upload.Name = r.FormValue("name") + upload.Category, _ = strconv.Atoi(r.FormValue("category")) + upload.SubCategory, _ = strconv.Atoi(r.FormValue("sub_category")) + upload.Description = r.FormValue("description") - db.ORM.Create(&torrent) + var err error + var code int + + filesize, err, code = upload.ValidateMultipartUpload(r) if err != nil { - util.SendError(w, err, 500) + http.Error(w, err.Error(), code) return } - fmt.Printf("%+v\n", torrent) } + + torrent := model.Torrent{ + Name: upload.Name, + Category: upload.Category, + SubCategory: upload.SubCategory, + Status: 1, + Hash: upload.Hash, + Date: time.Now(), + Filesize: filesize, + Description: upload.Description, + UploaderID: user.ID, + Uploader: &user, + } + /*db.ORM.Create(&torrent) + if err != nil { + util.SendError(w, err, 500) + return + }*/ + fmt.Printf("%+v\n", torrent) } func ApiUpdateHandler(w http.ResponseWriter, r *http.Request) { diff --git a/service/api/api.go b/service/api/api.go index 3c5a3547..9abae7dd 100644 --- a/service/api/api.go +++ b/service/api/api.go @@ -1,6 +1,9 @@ package apiService import ( + "encoding/hex" + "errors" + "io" "net/http" "net/url" "reflect" @@ -9,6 +12,8 @@ import ( "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/service" + "github.com/ewhal/nyaa/util/metainfo" + "github.com/zeebo/bencode" ) type torrentsQuery struct { @@ -60,9 +65,9 @@ func (r *TorrentsRequest) ToParams() serviceBase.WhereParams { } func validateName(r *TorrentRequest) (error, int) { - if len(r.Name) < 100 { //isn't this too much? + /*if len(r.Name) < 100 { //isn't this too much? return ErrShortName, http.StatusNotAcceptable - } + }*/ return nil, http.StatusOK } @@ -105,8 +110,6 @@ func validateHash(r *TorrentRequest) (error, int) { return nil, http.StatusOK } -//rewrite validators!!! - func (r *TorrentRequest) ValidateUpload() (err error, code int) { validators := []func(r *TorrentRequest) (error, int){ validateName, @@ -128,6 +131,46 @@ func (r *TorrentRequest) ValidateUpload() (err error, code int) { return err, code } +func (r *TorrentRequest) ValidateMultipartUpload(req *http.Request) (int64, error, int) { + tfile, _, err := req.FormFile("torrent") + if err == nil { + var torrent metainfo.TorrentFile + + // decode torrent + if _, err = tfile.Seek(0, io.SeekStart); err != nil { + return 0, err, http.StatusInternalServerError + } + if err = bencode.NewDecoder(tfile).Decode(&torrent); err != nil { + return 0, err, http.StatusInternalServerError + } + // check a few things + if torrent.IsPrivate() { + return 0, errors.New("private torrents not allowed"), http.StatusBadRequest + } + //import cycle + /*trackers := torrent.GetAllAnnounceURLS() + if !router.CheckTrackers(trackers) { + return 0, errors.New("tracker(s) not allowed"), http.StatusBadRequest + }*/ + + if r.Name == "" { + r.Name = torrent.TorrentName() + } + + binInfohash, err := torrent.Infohash() + if err != nil { + return 0, err, http.StatusInternalServerError + } + r.Hash = strings.ToUpper(hex.EncodeToString(binInfohash[:])) + + // extract filesize + filesize := int64(torrent.TotalSize()) + err, code := r.ValidateUpload() + return filesize, err, code + } + return 0, err, http.StatusInternalServerError +} + func (r *TorrentRequest) ValidateUpdate() (err error, code int) { validators := []func(r *TorrentRequest) (error, int){ validateName, diff --git a/test.torrent b/test.torrent new file mode 100644 index 0000000000000000000000000000000000000000..5b48d5ae0d32e251a401bb69158970d9a60e01be GIT binary patch literal 16156 zcmbVzLvSVxux;#2Y}>YN+s+r;wrx%9WMbR4ZQJJitM1$0&0V%mcb`o!PPHi~w~?Kl zy{nxGfQ6OY)zpETfx*Se$iy1pL~mm6-~e!Pc6BoQ&v3EkX6It(Vq)YraRL~*089yu zJ(-xfT~+O!oB(z%3@r3)Of3Hsv$VG(H2p8dl8Kdzg`JI&jg*%Qim+FM!LQ4y*z6VeefG85{E zyV_Zbx*8KQF>*3G5PC4Pvl0pkiRjVWTDt)N|KZyi*#bCN{}1s08}fgYI9LKq0EGX4 z8)il>4kk8c0Q>*AotfA;I2pN9aaSUB$1wu3uGOe%JhQ+<@Q|+fh!P*= z3WIYC21TFucQ%u3|K3<=iu7QOXv-lvXm(@p94v#t}`ZWX* z;2T_C%tB5B5ktGB;Jv*F>iH4iSAmuB76w-$w4Es>d|FcWntMb}&(` zvDqF9{g*|GV@|X$PY7LN3rb--G6T;Io+}0@S8B@6nQ2G&C*whb#8XYwBCdUp*8iM| z(B|>}3iSyl!^~_(zxcpdeOBqsc?S%-7bBj3aV!OpWEJZM=j}pM;_fcG_q53J^(=#Qr$+BT;{rTz+)n<@H#n8*1I?^kdkUQln z)T)-H-Q?x~R&7&34+pUX4(+ zdJEn#j)*3q>-b1fuOVR*APn0Mog6TnVcDFROW20_plWZ`aBFwjJw+1T0LWt~@7D7f zxh)Jzlk<{A`+BC}gay0p#Dd|n%lshclzN(X2&Zm+Wl#Ogjw}Q9Mrb%4G3jYv zC6M=*PMOCnctcKS1WpLaij(n^P-R$k^hIH{DyOsegQ=atAfiAQqhlF}f{cX#k!EXy z>;7Y+N#Tl{Kw?|RGS{pYqESeHR;@I7fwlfS#^`q9FP(yM?j}2I7TkgATyY#=Pmb}; z+H1);mT$3w5HJiXm2)+I{Wdkykb__Zw@&;Nd{#Ppp81McZ)XReo!;z{)lKFW&^A)M zeMA%DtWdhh>a4?O0(z3KV>7prm=$`_)_T;8eS4UH9|v=?=XFr&{K2C#$^qo;c*6w-3`) z&#E+)+;?=dYhprTJ&|OLQuB*sf86JwgrTd112Q)~`$H^StrkCIpqKOo?aD0%^mUW! zY;8tCmAdZRsA|1+`XD^kKI*55ZUk z31EsQxD{_hjeih5l^)n`!7s*r?kUFBFDnQTu4Q6xr$XH1s71l!!eW%3O7ipg@*r}& zqZ{$L`B06qOKC#tJSi2W@-CH^f3;dR-_$F@0tt6GwMQ_s5|?R4ph0O6-6=Iyznr?u zdSE}zxq8m|Fvsu@2Z)*@o-`wvZ}Mt0${wf;N~1u(FW|W{Wo0VexzZ)7`<%E^ies2M z7Y2;KsMRCH8u$6--jn&cOu*RH={mx)r?m=s<+as2Z1`?x>cpLo1T?G7tGze<7NARW zyL6ov`fiqaxqXF1h#=H!?BxtFrX3##c{yurRRIBdz46;6_-JcN)};50J(3rn+p45N zfmJif3P9e?%L}Ec=J(nH8st}6r-MkRd939puXrP&_Z)Rk%HMIhn5|mwftm7edNqtq zEv@)VKvXq+=2-~)`dI~abgW}DVDTyqf&&kc$lkdeb>?kX zqS8sD~dD9nRF2>_dN{ML*MEe|KN#{1+?snHyPbP;DLpz+i+Bnrn-6swX74ylc z=27D&qU#9M+Hbm3Z4vQF8YSM338S>G-A{D#5-5;81N!e#^c7t;;B2z%rFGX-7;cNy zvSDL{#v_woI(}J}BC%}R`0@P=_9{{~4e zg?xH(sa&p)b`+T^Dp#dD))cFV3|mQuBCw4UBoisOiVvV8=}ED#ND@CMjNauXVx(Em zcUS5DE`?9_mCkAB>2WUKGI1}Qh7WZOj;I-8&c<8dT7L`iV>>bh)5n$AIQHC_rW_}0 zZk>PBY+F#+?Pwh1U}D_8MK`>(STuMA_-xfS?%&M-eN!6^aQ;^0Q^P&eGK{sQGpi;X zxl&|KG^1ZPdf0)sB;sn!KOW{PK$eNx5RqQ-Bcm3A`ragT&+$RP%c>o{e}T| zOwWe2m#L}fXT7?tMrcYA3KOlWcQFiS5~gg24-SXb7+v%R5G}U@9*1rUig%8cd=p!q z^nlx54#tQg2v?W9$v|}zdeNZ^!5N7qE`$uiT%-+Mg^72MwI*Q;SPW{~0_HvAlaJD) zw^o$G1IYlsJ9ePGvutlL7jDD%mplqc#^dC!@N&p>l!4g_3L2EDPap{TYC`BXOTl0* zP&1Zp1upEgy&Jn-x!zc&Dj!zJNwLcCZLDIkCy8xyP1L9#?PY%x<2s$e3p$o1W1U0v zEs*Blq1U+)O5LoN;^3!M_FI|YD+d;4Ydx#U!gfxn(wrn~ae5pGzHyLGjYMz9pG|^@ zBmuc9hik|U>pCNhu};9{#U^?CpUvgWYm>wP;1+<<;PW>MRS5s z4$$H7^;`3&`Z017y$uCqU}MhATj37tjo{Kp!5t8=t|nDD&lCBAu6!Ygs0D(;j)v1? z6kzd;r~>R#-HtkN8c6uE^uBL+&V)O&D=AF{y0FR24t8K_Hcg*?t6H#%G7X6;xIAYX zg3;%Ar4&jL^V{o7xH~NF5$H7uRhCWuP+%RJQ9!2ai_z`KlS!FiL#yT2!bgc7-=b^cUi<=UNF4h^oF@zL(#v^pnn6JdRtVQu18Ct9hb zi-LATLrI>HoUysl=4|oMs|8QOL_(+Eh}`+|vuGu{t7!wt6h5+Z7T!x+8C54Q3=74B z)(IGZ?hO#$tdB;C;S`8cDsGe1uWe}OEO^LRBSLD~qOqahB67!ib>Xk_`+MY^eHvzG zs9oy);!VlCB?0V-Bz}&H?PP||{;lXg`&hGwVuob*qBM@18snRv+Zpv2 z1rCemNnGPqJb`VyPT*y``bP>}MXOym5U>b%t9W2(m!DKwrmFd+p71=JWT2olh00zJ zyeX=hN{2)*bjtJYc8{r@{bCjYRLTCSc64Z3rBus;Z^|pU=mbwfl+bu^8G4Uljz@7N zmZ|jPG>`;FBt{*maV~A`O?lK~p6UCl!+8g`_<-0;mlPsastYif#1=hi%l9-UeeY zk?ehm__6aHlV0%}5|Wax=i0E(e5;XZQ)oLk*DNdS_y2E<%r@ zZn}yhg*8;c5+KkPt{jpa3#<00?kt;f^8?Dozm)32 zSC<{Cyt?$xd5M|c=xje0G%$H#6bQb%vnf|xL&4E91X0~4svnbtpsCim#mz4-1i0sN-&0iX-u7gF_y7EGBHBP8`LVt8N3h>@)N|6}%46Bz6Z6iSKEjA;2SEZ; z>B=bQ_Ih0Kj4AWpnVcO%%5+&28zP>o;TpIcP6nBgY=&E8Q6xzT?>+TfHgj|8Qpx2N z=1P0ozYTZ!uI--+`Vt6rZCD#jDDxV5Vd=>jxX}bUPB<9;(^JVO_y**qQQOlqYeVf) zcyhE9>Kv?dl86$Rn%e7eMZgIEt&1lY%T;(@`a+O7jp$rdeWkI^P&r={pIzEA2hr2D z`$7}vIaj2!kYdy~`2E9Yg@iH>VAsPotn|tNL{91+9OXY2y^EeJ@avbPIkwXLbT;z9 z`|-ob8kp96k|Gu)Pg&=}B&D|j*Xr{wo`#QEVvJJgCH@@%df%M>wCwt4?>Y&mS(#&w zUs)v09|FExR%_kNks2^F!5c*C^T|&}Kri;d^;NY&ev2Et(IF^x4fdrRXoZ45`6OAC_|0nr0F@{r*fLb1RWXFP_N}6OVYXp=a+5j2HU9Nn1Z>7gg%Mb3| zGs}{?eKP`werSSdSV^?=GoQRT;aBD$yztzK1md>a!{-=WKd2qTtgVZoVo*#!VuKK)GClSp?Od z8lfzEjb~C19jx^V-niIaV53y)h7BDW^HEpN0fjX82EJ8(-;`(%9c)A6lC{)LB&i>t zv+1fzneE=DJe%u^&!+G5NXak5kqmr^aYWadFnx6|O&9d7@pleI)^j7x#k6`nL6p-6 zT0^=qW<^=F;HuF0g5qgN|3v~wh51C}FhANvTlC@(`D;8hidw?pPhxm|$?+*Fz z0!y65Y_VQKcwP~JQl5H#o1;z`^U=C_QM&rj;jh+TMPzc%8V<5dlJUCo4pb{E5mliv+@ zMj7Am+6^5RdKoB&cYWf0F$8)6Vc$T;=u=pPjKII^+?+YHHH*HZjJb&^7XYA6us@|( z7MqkwdV|;;imdy_0bIsKh+}ZT!>I%O`p{al_(%Qqq?fwye=bbq=ickKA*38~Ja-JX zTCJ>=0rCS-%ot9Y6qW`!_deK+%*sX71=VGjI8l4|9(Z#}+QoL%Jl7{TVKui!V0u42 zG$)X1HPzAZ(0Y|qnU!9_y~9DY3Z;pO7-;iwCY!${O#-{xY~mP`+e$T(1z zvV3J4=JgB_-j|0@A;gtdVw>Hcp5G#3{O+GXbK;k=%PIfxRqWGe{zOq`FEDCXziUmP zAxOS2vN+}pCduvfX&B3=%h*yH?!^daJN{vk3U?b*qTB4>prr`f&Kip3P`I`;@;Jep z^aJ^6r6dTC;p1zQtgI2WlT=owW6b~A0@J0SaTo;Yw28_JPoK34$gB@WfC8d{yCjXP zfE+EK=RHGTybx&Ea*3+KziJn1i0>2&aX6~WMC{Bm=+uRkh%0q`Cni!@i3O;S5N~8| zJqe&#!!%cy0TL47W19d5d2a*}H^KS)q}Vs|N8^D_9xETgn3H$innb^dI`O`40RWav z6KRCq&&BxBP#93!Upd`H1wnBY<`wDPYorfkzqu^T~OgmDGj&UrH5?$hV=%5MZ61wv1K*>l{eCcPvZ5^0rB z0pLTRTeuhQzIH>)(|F}C*$?y8J8OTNkDy;j@b!8sa4+3^#UZb%hL3&4oq7ZGVb%J& zu*7+{80@H2rUF*w#2@U(AZ3gHg(FkY%6D`#iNET%n_F&bT{m$-Sef(U;;p@j>w~G3$&9r3l7q@mkP*T)DEA}3zl7@>@S|VxcY$IOR-kqMHQMv>c>CRA z+be;<9Cky#Gm;B}`6`tRa9`0vf78&^C{F!rw;riM56;lZv|6-4yWIPrVHuJ_WU`HR zeT(TrNhEF{@*%h24hnrK746&1c$PlSfaK%6={oY~Av4th<}tMeSrwE>4hIAaBi2|c zCTg&pd`JL>r={5L-@L8C$RX|)UMKSDjR_Yd=NA826inDXisnX@I4N-eo)onv4z=4z zS8SgajR|bX6NbuPwG{XvtxstBQvFg=V;Se@`7`p82sO1J-Y}Jy?Xj;?)!VTRw}c(U zA6)Z2*rHsDzE%C$r7SO3HZQ@$*RDL8OkTZ~gKl5n$icS* zrmGoV*Kf5+$sM0p%3@(!KA#dzpJ;q46z9y7yTnWH-gv+afj7V|mg8kW7FRZ5Oc>w8mKgbq=apH0*y+kgqVao<88Mvp{I zrt~oqmZU_KykkT8zxFT81?F{uL+IFJYf0xZ$`U)M4!(r_vYnPp!d>IkUSzJzbplf< zcjAuwwl|E{8(2uC${ni|qi??no+jlYl=d$?z!1C==|Q%{wUjqAXPPeQWqx4POX&sO zR5jPVFAEIG!Uxp+VgY~IqKf~!0AEjE>L=5>D)YBU_=<><#i!f%W(^C1P5Npn83{$! zEfz)al{82tdSL@Ig&LMqlH6970Wj2&oA=BiFaym2ON47Z2MdFXu3B$P4AOMILq& z8K6Z(EAcX@G&(J=&MJjM!Y)aCgh7bPML%aKx220QBw&6!t`Y{2IE0dPn7!|9t|pOG z#hJa>g{?lf4lrF_p698$z$=HAZz39z;B0QLXGq~55cB<2ddumv@bIetlot4UyO4I^ zLTBLv#rX&H_-l!4!Ksog%9&6>mST3lU#<}>g??f_E-_K2d;>$6P^b)7!$G2Au&IG z4ba2&h67AxHlSEUPv~yl6+~pWc*%G(zy!gsv>?hkM@4;4m02A6g$b=DaH@$^ToaQ~ zHPrrbtHo&(?fRw~e+$6bGZ5kXeL}tvC^T1|>Wm2!f}fb^4PAO`81DYI6&#To#FZIC z0|w}+4=A0^9NDDNjqi(6!vfN#8N)pP=h<6F*x=Pgv`o$Mt_9&rd|(7B3VREzgw^F#9|xN4e9z~3 zmbj?yec^wDN%%0D`>oBg!;4oji~&MDK8s35zkIU+KO>nt`-b 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