\c,{unzfqI6n1ВJP>5K
_Ni-@x~f<#ZonEfDĲ6Cy~gUYcN3ga 2 @ˀ}X1,099]NxrB
V䨮 !AI,2HN	3- G{爬H;PǻzuO4	hl#24;7uDAVe.Yu+J5^}`^LM^m)Yϡq,}"JjsU)qfMNHPfk\	YUI{tne.pݩ$-j	i#3 
כim>sB]9[\N0vҕ[Gr!6j_R)h$d)oN_޴=J̃GӜ8̈́$sP]m`^U>VځÁlGGv{ewFePH=]*wwGjtQw X?_WiH#V¨]R|(\ZdHfDUan{=kE&˕, @0+tG@͸ĀsJ&UbOӀUA+?ۢA:[ܝg%!hb%OvR	0<z#;&
q94Ol7Lr$lb9m:T:퐺}|
7jj2uE=c0Hr6I* i?Q6\dB#s8;(TbIYHbT0Mg6ͳ(~c
y<`{S7^<1:K-bdF܇T28(- 	6fc)=0oڊOn+u>Q=yz#<l~fau)MS~s~ABFymj"a61g&{`$e2q#;3z+萌0lϟ@?J{志ѡ`ƀG0KRk	w
h^=[xҐ^5K$lmLǺIZ
Qrߝr55E]jÏfc`-{Oח4؇HtvKΉfSPֆ4[jiXqH|>[Ua98Xz&;	T݂$!|?i=c"Mޏ<ثEZB?d!#B|7ݜ2Rs]#V)u#tSU-2A@ۑwp
r\?t`XFS۔%;5sy|Ow eu!~[aU?2r!vV3fSnUAϭ҂.$}|?Y;QO`͖*R$Rmp==|c&謍hC	ܼ.DN3jJ{*
ᄸ4a7WW@b'Gj
F\'%
z-ُ@U`]9y|9z1>/Axp8>>=Mɭv˛	mPpmTHG[V==2ɚڰPu,ll,i8<A̯*NHs>@듗}bbcu,Vi;?WmX*[/HӚ)\*	
6QvZ؇Vk."7V Q񘖊	$,4ғ |Ǩ|zuv8xRzF~f불e1p;sc}++E"}¾.cqsLL?`(~Ѳ@,a'k|WTM(N4C{LCE}v-CJ<wm`4Hs(λ參B|Jh<~eMhz7eR=co<'=coПQ.D=ξ@Ԙȣt@]ɕ<qk7E87LQ@4] v/fM}zi$$|7cKif0t9o-uvHuYJ 3a($\zʦR
G^}պxV:
`Ok>n$UUٽr5+`>|:f+f24w%8&'uȧlNp]zɺ+z?EqŦB0l`>ukSMJ*P%z1&-|;SUL\ި>i"FHt<ƺ|>)4Ul3<ޓN{*fnX09+AS^:N}fW䈽k3S*wsj2RV蓗^.oy8 ~fӓjѤ͜,|-(h4#l9YOg.urJO M70YW>;q>mf.]vիO|R\;y	+N#D@`)7m0&u0zlGx!>n<9`:(I'Vl6I2Q6*lYqUPU]n#إn+SC߫yyi.㦚O`{~=#s<oLVfX}@j{tF)֪M63뵢B3v{R$S2>*l0d-Rc)ЬX5hx@-[	~_!ƕh~N6Sa"ϭoyžҾQW?aXy=á7{jK{%\"o-uJ@2NSLWaOJ"-])fKF"Go&Rp:K x`\3d>B!/_}CA>9jO+W16zԳT@m(~IVxǱն)]ut%9}݋w)3bp˄r]pIlZ@C'}Pbjڻ}jwq6[Tj^Z8_\<5}'(_ݞt;a*	6IЌG_˰nE	61)GNY9pz[NR<to`z_rxy|+F}~g>k3)j?t[)r<f#(f+P
"-cFCĶsMm,
_67-A']yHPn>bgWť72yJ\18_G9 U|$"?s'%<!%/_w6j\ ңB{eh%*6%oVx=ނ-)M4rQE݁$V皲.mWP8ڏ8gY|iy=K"78nn&,`\fFIϥO8"-cFCĶꕗ,16XKk/o'աP!y>X`=ElO4L5tY3nK2dj@Qn24)5.&!,B;80n\3撉2)W0Z3bp˄{S¥62J4&YB|p~Ro ٢M&1
*DJkB$<%Dh3`Jr ZLT\߹AJڈe2];\qtH8ȡ"/k0{-Sxtoqo_c*QBrZc_BlQ5W7k6w:.5}A{}
            result['name'] = urllib.unquote(fieldStorage.filename)
            result['type'] = fieldStorage.type
            result['size'] = self.get_file_size(fieldStorage.file)
            if self.validate(result):
                key, thumbnail_key = self.write_blob(
                    fieldStorage.value,
                    result
                )
                if key is not None:
                    result['url'] = self.request.host_url + '/' + key
                    result['deleteUrl'] = result['url']
                    result['deleteType'] = 'DELETE'
                    if thumbnail_key is not None:
                        result['thumbnailUrl'] = self.request.host_url +\
                             '/' + thumbnail_key
                else:
                    result['error'] = 'Failed to store uploaded file.'
            results.append(result)
        return results

    def head(self):
        pass

    def get(self):
        self.redirect(WEBSITE)

    def post(self):
        if (self.request.get('_method') == 'DELETE'):
            return self.delete()
        result = {'files': self.handle_upload()}
        s = self.json_stringify(result)
        redirect = self.request.get('redirect')
        if self.validate_redirect(redirect):
            return self.redirect(str(
                redirect.replace('%s', urllib.quote(s, ''), 1)
            ))
        if 'application/json' in self.request.headers.get('Accept'):
            self.response.headers['Content-Type'] = 'application/json'
        self.response.write(s)

class FileHandler(CORSHandler):
    def normalize(self, str):
        return urllib.quote(urllib.unquote(str), '')

    def get(self, content_type, data_hash, file_name):
        content_type = self.normalize(content_type)
        file_name = self.normalize(file_name)
        key = content_type + '/' + data_hash + '/' + file_name
        data = memcache.get(key)
        if data is None:
            return self.error(404)
        # Prevent browsers from MIME-sniffing the content-type:
        self.response.headers['X-Content-Type-Options'] = 'nosniff'
        content_type = urllib.unquote(content_type)
        if not IMAGE_TYPES.match(content_type):
            # Force a download dialog for non-image types:
            content_type = 'application/octet-stream'
        elif file_name.endswith(THUMB_SUFFIX):
            content_type = 'image/png'
        self.response.headers['Content-Type'] = content_type
        # Cache for the expiration time:
        self.response.headers['Cache-Control'] = 'public,max-age=%d' \
            % EXPIRATION_TIME
        self.response.write(data)

    def delete(self, content_type, data_hash, file_name):
        content_type = self.normalize(content_type)
        file_name = self.normalize(file_name)
        key = content_type + '/' + data_hash + '/' + file_name
        result = {key: memcache.delete(key)}
        content_type = urllib.unquote(content_type)
        if IMAGE_TYPES.match(content_type):
            thumbnail_key = key + THUMB_SUFFIX
            result[thumbnail_key] = memcache.delete(thumbnail_key)
        if 'application/json' in self.request.headers.get('Accept'):
            self.response.headers['Content-Type'] = 'application/json'
        s = self.json_stringify(result)
        self.response.write(s)

app = webapp2.WSGIApplication(
    [
        ('/', UploadHandler),
        ('/(.+)/([^/]+)/([^/]+)', FileHandler)
    ],
    debug=DEBUG
)
