| Comment: | tentative beginnings of upload + media management system |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
f4c6e72a22221e80abe7e4088cf97cb9 |
| User & Date: | lexi on 2021-01-07 07:35:14 |
| Other Links: | manifest | tags |
|
2021-01-07
| ||
| 09:39 | further iteration on media check-in: af5ed65b68 user: lexi tags: trunk | |
| 07:35 | tentative beginnings of upload + media management system check-in: f4c6e72a22 user: lexi tags: trunk | |
|
2021-01-06
| ||
| 22:31 | unfuck last commit check-in: 8d8ab01573 user: lexi tags: trunk | |
Modified backend/pgsql.t from [80ca2205e4] to [5d4cebec04].
477 477 limit case when $4::bigint = 0 then null 478 478 else $4::bigint end 479 479 offset $5::bigint 480 480 ]]; 481 481 }; 482 482 483 483 artifact_instantiate = { 484 - params = {binblob, binblob, pstring}, sql = [[ 485 - insert into parsav_artifacts (content,hash,mime) values ( 486 - $1::bytea, $2::bytea, $3::text 484 + params = {binblob, binblob, pstring, int64}, sql = [[ 485 + insert into parsav_artifacts (content,hash,mime,birth) values ( 486 + $1::bytea, $2::bytea, $3::text,$4::bigint 487 487 ) on conflict do nothing returning id 488 488 ]]; 489 489 }; 490 490 artifact_expropriate = { 491 - params = {uint64, uint64, pstring}, cmd = true, sql = [[ 492 - insert into parsav_artifact_claims (uid,rid,description,folder) values ( 493 - $1::bigint, $2::bigint, $3::text, 'new' 491 + params = {uint64, uint64, pstring, pstring, int64}, cmd = true, sql = [[ 492 + insert into parsav_artifact_claims (uid,rid,description,folder,birth) values ( 493 + $1::bigint, $2::bigint, $3::text, $4::text, $5::bigint 494 494 ) on conflict do nothing 495 495 ]]; 496 496 }; 497 497 artifact_quicksearch = { 498 498 params = {binblob}, sql = [[ 499 499 select id, (content is null) from parsav_artifacts where hash = $1::bytea 500 500 limit 1 ................................................................................ 529 529 -- "ERROR: cannot insert multiple commands into a prepared 530 530 -- statement" are you fucking shitting me with this shit 531 531 params = {uint64}, sql = [[ 532 532 delete from parsav_artifact_claims where 533 533 rid = $1::bigint 534 534 returning uid, description, birth, folder; 535 535 ]]; 536 + }; 537 + artifact_enum_uid = { 538 + params = {uint64, pstring}, sql = [[ 539 + select (pg_temp.parsavpg_translate_artifact(a)).* 540 + from parsav_artifact_claims as a where uid = $1::bigint and 541 + ($2::text is null or 542 + ($2::text = '' and folder is null) or 543 + $2::text = folder) 544 + order by birth desc 545 + ]]; 546 + }; 547 + artifact_fetch = { 548 + params = {uint64, uint64}, sql = [[ 549 + select (pg_temp.parsavpg_translate_artifact(a)).* 550 + from parsav_artifact_claims as a where uid = $1::bigint and rid = $2::bigint 551 + ]]; 552 + }; 553 + artifact_load = { 554 + params = {uint64}, sql = [[ 555 + select content, mime from parsav_artifacts where id = $1::bigint 556 + ]]; 536 557 }; 537 558 post_attach_ctl_ins = { 538 559 params = {uint64, uint64}, cmd=true, sql = [[ 539 560 update parsav_posts set 540 561 artifacts = artifacts || $2::bigint 541 562 where id = $1::bigint and not 542 563 artifacts @> array[$2::bigint] -- prevent duplication ................................................................................ 1530 1551 if ban then 1531 1552 lib.report('user attempted to instantiate forsaken artifact') 1532 1553 return 0 1533 1554 end 1534 1555 var oldid = srec:int(uint64,0,0) 1535 1556 return oldid 1536 1557 else -- not in db, insert 1537 - var nrec = queries.artifact_instantiate.exec(src, artifact, hashb, mime) 1558 + var nrec = queries.artifact_instantiate.exec(src, artifact, hashb, mime, lib.osclock.time(nil)) 1538 1559 if nrec.sz == 0 then 1539 1560 lib.warn('failed to instantiate artifact -- are you running out of storage?') 1540 1561 return 0 1541 1562 else defer nrec:free() 1542 1563 var newid = nrec:int(uint64,0,0) 1543 1564 return newid 1544 1565 end 1545 1566 end 1546 1567 end]; 1568 + 1569 + artifact_expropriate = [terra( 1570 + src: &lib.store.source, 1571 + uid: uint64, 1572 + artifact: uint64, 1573 + desc: pstring, 1574 + folder: pstring 1575 + ): {} 1576 + queries.artifact_expropriate.exec(src,uid,artifact,desc,folder, lib.osclock.time(nil)) 1577 + end]; 1578 + 1579 + artifact_enum_uid = [terra( 1580 + src: &lib.store.source, 1581 + uid: uint64, 1582 + folder: pstring 1583 + ) 1584 + var res = queries.artifact_enum_uid.exec(src,uid,folder) 1585 + if res.sz > 0 then 1586 + var m = lib.mem.heapa([lib.mem.ptr(lib.store.artifact)], res.sz) 1587 + for i=0,res.sz do 1588 + var id = res:int(uint64,i,0) 1589 + var idbuf: int8[lib.math.shorthand.maxlen] 1590 + var idlen = lib.math.shorthand.gen(id, &idbuf[0]) 1591 + var desc = res:_string(i,2) 1592 + var folder = res:_string(i,3) 1593 + var mime = res:_string(i,4) 1594 + var url = lib.str.acc{}:init(48):lpush('/media/a/'):push(&idbuf[0],idlen):finalize() defer url:free() 1595 + m.ptr[i] = [ lib.str.encapsulate(lib.store.artifact, { 1596 + desc = {`desc.ptr, `desc.ct + 1}; 1597 + folder = {`folder.ptr, `folder.ct + 1}; 1598 + mime = {`mime.ptr, `mime.ct + 1}; 1599 + url = {`url.ptr, `url.ct + 1}; 1600 + }) ] 1601 + m(i).ptr.rid = id 1602 + m(i).ptr.owner = uid 1603 + end 1604 + return m 1605 + else return [lib.mem.lstptr(lib.store.artifact)].null() end 1606 + end]; 1547 1607 1548 1608 post_attach_ctl = [terra( 1549 1609 src: &lib.store.source, 1550 1610 post: uint64, 1551 1611 artifact: uint64, 1552 1612 detach: bool 1553 1613 ): {}
Modified backend/schema/pgsql-views.sql from [d567971842] to [ca832d14af].
15 15 select p.id as post, 16 16 coalesce((select counts.ct from counts where counts.subject = p.id 17 17 and counts.kind = 'like'),0)::integer as likes, 18 18 coalesce((select counts.ct from counts where counts.subject = p.id 19 19 and counts.kind = 'rt' ),0)::integer as rts 20 20 from parsav_posts as p 21 21 ); 22 + 23 +create type pg_temp.parsavpg_intern_artifact as ( 24 + rid bigint, 25 + owner bigint, 26 + "desc" text, 27 + folder text, 28 + mime text 29 +); 30 + 31 +create or replace function 32 +pg_temp.parsavpg_translate_artifact(parsav_artifact_claims) 33 +returns pg_temp.parsavpg_intern_artifact as $$ 34 + select ($1).rid, ($1).uid, ($1).description, ($1).folder, a.mime 35 + from parsav_artifacts a where 36 + a.id = ($1).rid limit 1 37 +$$ language sql; 22 38 23 39 create type pg_temp.parsavpg_intern_notice as ( 24 40 kind smallint, 25 41 "when" bigint, 26 42 who bigint, 27 43 what bigint, 28 44 reply bigint,
Modified config.lua from [eb323f3b45] to [6cff716428].
57 57 {'bell.svg', 'image/svg+xml'}; 58 58 {'heart.webp', 'image/webp'}; 59 59 {'retweet.webp', 'image/webp'}; 60 60 {'padlock.svg', 'image/svg+xml'}; 61 61 {'warn.svg', 'image/svg+xml'}; 62 62 {'query.webp', 'image/webp'}; 63 63 {'reply.webp', 'image/webp'}; 64 + {'file.webp', 'image/webp'}; 64 65 -- keep in mind before you add anything to this list: these are not 65 66 -- just files parsav can access, they are files that are *kept in 66 67 -- memory* for fast access the entire time parsav is running, and 67 68 -- which need to be loaded into memory before the program can even 68 69 -- start. it's imperative to keep these as small and few in number 69 70 -- as is realistically possible. 70 71 };
Modified http.t from [7092b409fb] to [4c9f723184].
1 1 -- vim: ft=terra 2 2 local m = {} 3 3 local util = lib.util 4 4 5 -m.method = lib.enum { 'get', 'post', 'head', 'options', 'put', 'delete' } 5 +m.method = lib.enum { 'get', 'post', 'post_file', 'head', 'options', 'put', 'delete' } 6 6 m.mime = lib.enum { 7 7 'html'; -- default 8 8 'json'; 9 9 'mkdown'; 10 10 'text'; 11 11 'ansi'; 12 12 'none'; ................................................................................ 18 18 key: rawstring 19 19 value: rawstring 20 20 } 21 21 struct m.page { 22 22 respcode: uint16 23 23 body: lib.mem.ptr(int8) 24 24 headers: lib.mem.ptr(m.header) 25 +} 26 +struct m.upload { 27 + ctype: lib.str.t; 28 + filename: lib.str.t; 29 + field: lib.str.t; 30 + body: lib.str.t; 25 31 } 26 32 27 33 local resps = { 28 34 [200] = 'OK'; 29 35 [201] = 'Created'; 30 36 [301] = 'Moved Permanently'; 31 37 [302] = 'Found';
Modified makefile from [0e9ec3efb8] to [8559ac7b2c].
1 1 dl = git 2 2 dbg-flags = $(if $(dbg),-g) 3 3 4 -images = static/default-avatar.webp static/query.webp static/heart.webp static/retweet.webp static/reply.webp 4 +images = static/default-avatar.webp static/query.webp static/heart.webp static/retweet.webp static/reply.webp static/file.webp 5 5 #$(addsuffix .webp, $(basename $(wildcard static/*.svg))) 6 6 styles = $(addsuffix .css, $(basename $(wildcard static/*.scss))) 7 7 8 8 parsav parsavd: parsav.t config.lua pkgdata.lua $(images) $(styles) 9 9 terra $(dbg-flags) $< 10 10 parsav.o parsavd.o: parsav.t config.lua pkgdata.lua $(images) $(styles) 11 11 env parsav_link=no terra $(dbg-flags) $<
Modified parsav.t from [8bdefbaeb6] to [7b59e1e979].
434 434 'render:profile'; 435 435 'render:compose'; 436 436 'render:tweet'; 437 437 'render:tweet-page'; 438 438 'render:user-page'; 439 439 'render:timeline'; 440 440 'render:notices'; 441 + 442 + 'render:media-gallery'; 441 443 442 444 'render:docpage'; 443 445 444 446 'render:conf:profile'; 445 447 'render:conf:sec'; 446 448 'render:conf:users'; 447 449 'render:conf';
Added render/media-gallery.t version [79b3557b2e].
1 +-- vim: ft=terra 2 +local pstr = lib.str.t 3 +local P = lib.str.plit 4 +local terra cs(s: rawstring) 5 + return pstr { ptr = s, ct = lib.str.sz(s) } 6 +end 7 + 8 +local show_all,show_new,show_files,show_vid,show_img=1,2,3,4,5 9 + 10 +local terra 11 +render_media_gallery(co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), uid: uint64, acc: &lib.str.acc) 12 + -- note that when calling this function, path must be adjusted so that path(0) 13 + -- eq "media" 14 + var owner = false 15 + if co.aid ~= 0 and co.who.id == uid then owner = true end 16 + var ou = co.srv:actor_fetch_uid(uid) 17 + if not ou then goto e404 end 18 + 19 + var view = data.view.media_gallery { 20 + menu = pstr{'',0}; 21 + folders = pstr{'',0}; 22 + directory = pstr{'',0}; 23 + images = pstr{'',0}; 24 + } 25 + 26 + if owner then 27 + view.menu = P'<a class="pos" href="/media/upload">upload</a><hr>' 28 + end 29 + var mode: uint8 = show_new 30 + var folder: pstr 31 + if mode == show_new then 32 + folder = lib.str.plit'' 33 + elseif mode == show_all then 34 + folder = pstr.null() 35 + -- else get folder from query str 36 + end 37 + 38 + var md = co.srv:artifact_enum_uid(uid, folder) 39 + var gallery: lib.str.acc gallery:init(256) 40 + var files: lib.str.acc files:init(256) 41 + for i=0,md.ct do 42 + if lib.str.ncmp(md(i)(0).mime, 'image/', 6) == 0 then 43 + gallery:lpush('<a class="thumb" href="') 44 + if not owner then 45 + gallery:lpush('/') 46 + if ou(0).origin ~= 0 then gallery:lpush('@') end 47 + gallery:push(ou(0).xid,0):lpush('/') 48 + end 49 + gallery:push(md(i)(0).url,0) 50 + :lpush('"><img src="') :push(md(i)(0).url,0) 51 + :lpush('/raw"><div class="caption">') :push(md(i)(0).desc,0) 52 + :lpush('</div></a>') 53 + else 54 + files:lpush('<a class="file" href="') 55 + if not owner then 56 + gallery:lpush('/') 57 + if ou(0).origin ~= 0 then gallery:lpush('@') end 58 + gallery:push(ou(0).xid,0):lpush('/') 59 + end 60 + files:push(md(i)(0).url,0) 61 + :lpush('"><span class="label">'):push(md(i)(0).desc,0) 62 + :lpush('</span> <span class="mime">'):push(md(i)(0).mime,0) 63 + :lpush('</span></a>') 64 + end 65 + md(i):free() 66 + end 67 + 68 + view.images = gallery:finalize() 69 + view.directory = files:finalize() 70 + 71 + if acc ~= nil then 72 + view:append(acc) 73 + else 74 + var pg = view:tostr() defer pg:free() 75 + co:stdpage([lib.srv.convo.page] { 76 + title = P'media'; 77 + class = P'media manager'; 78 + cache = false; 79 + body = pg; 80 + }) 81 + end 82 + 83 + view.images:free() 84 + view.directory:free() 85 + if md:ref() then md:free() end 86 + do return end 87 + 88 + ::e404:: co:complain(404,'media not found','no such media exists on this server') 89 +end 90 + 91 +return render_media_gallery
Modified render/nav.t from [62959bc993] to [5194b2263f].
3 3 render_nav(co: &lib.srv.convo) 4 4 var t: lib.str.acc t:init(64) 5 5 if co.who ~= nil or co.srv.cfg.pol_sec == lib.srv.secmode.public then 6 6 t:lpush(' <a accesskey="t" href="/">timeline</a>') 7 7 end 8 8 if co.who ~= nil then 9 9 t:lpush(' <a accesskey="c" href="/compose">compose</a> <a accesskey="p" href="/'):push(co.who.xid,0) 10 - t:lpush('">profile</a> <a accesskey="o" href="/conf">configure</a> <a accesskey="d" href="/doc">docs</a> <a accesskey="g" href="/logout">log out</a> <a class="bell" href="/notices">notices</a>') 10 + t:lpush('">profile</a> <a accesskey="m" href="/media">media</a> <a accesskey="o" href="/conf">configure</a> <a accesskey="d" href="/doc">docs</a> <a accesskey="g" href="/logout">log out</a> <a class="bell" href="/notices">notices</a>') 11 11 else 12 12 t:lpush(' <a accesskey="d" href="/doc">docs</a> <a accesskey="g" href="/login">log in</a>') 13 13 end 14 14 return t:finalize() 15 15 end 16 16 return render_nav
Modified route.t from [e8c2143a0c] to [af3d41e739].
1 1 -- vim: ft=terra 2 2 local r = lib.srv.route 3 3 local method = lib.http.method 4 4 local pstring = lib.mem.ptr(int8) 5 5 local rstring = lib.mem.ref(int8) 6 +local binblob = lib.mem.ptr(uint8) 6 7 local hpath = lib.mem.ptr(rstring) 7 8 local http = {} 8 9 9 10 terra meth_get(meth: method.t) return (meth == method.get) or (meth == method.head) end 10 11 11 12 terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t) 12 13 var rel: lib.store.relationship ................................................................................ 395 396 return 396 397 else goto badop end 397 398 end 398 399 399 400 lib.render.notices(co) 400 401 do return end 401 402 402 - ::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end 403 + ::badop:: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end 404 +end 405 + 406 +terra http.media_manager(co: &lib.srv.convo, path: hpath, meth: method.t) 407 + if meth == method.post then 408 + goto badop 409 + end 410 + 411 + if path.ct == 1 or (path.ct >= 3 and path(1):cmp(lib.str.lit'a')) then 412 + if meth == method.post then goto badop end 413 + lib.render.media_gallery(co,path,co.who.id,nil) 414 + elseif path.ct == 2 then 415 + if path(1):cmp(lib.str.lit'upload') and co.who.rights.powers.artifact() then 416 + if meth == method.get then 417 + var view = data.view.media_upload { 418 + folders = '' 419 + } 420 + var pg = view:tostr() defer pg:free() 421 + co:stdpage([lib.srv.convo.page] { 422 + title = lib.str.plit'media :: upload'; 423 + class = lib.str.plit'media upload'; 424 + cache = false; body = pg; 425 + }) 426 + elseif meth == method.post_file then 427 + var desc = pstring.null() 428 + var folder = pstring.null() 429 + var mime = pstring.null() 430 + var name = pstring.null() 431 + var body = binblob.null() 432 + for i=0, co.uploads.sz do var up = co.uploads.storage.ptr + i 433 + if up.body.ct > 0 then 434 + if up.field:cmp(lib.str.plit'desc') then 435 + desc = up.body 436 + elseif up.field:cmp(lib.str.plit'folder') then 437 + folder = up.body 438 + elseif up.field:cmp(lib.str.plit'file') then 439 + mime = up.ctype 440 + body = binblob {ptr = [&uint8](up.body.ptr), ct = up.body.ct} 441 + name = up.filename 442 + end 443 + end 444 + end 445 + if not body then goto badop end 446 + if body.ct > co.srv.cfg.maxupsz then 447 + co:complain(403, 'file too long', "the file you have attempted to upload exceeds the maximum length permitted by this server's upload policy. if it is an image or video, try compressing it at a lower quality setting or resolution") 448 + return 449 + end 450 + var id = co.srv:artifact_instantiate(body,mime) 451 + if id == 0 then 452 + co:complain(500,'upload failed','artifact rejected. either the server is running out of space or this file is banned from the server') 453 + return 454 + end 455 + co.srv:artifact_expropriate(co.who.id,id,desc,folder) 456 + 457 + var idbuf: int8[lib.math.shorthand.maxlen] 458 + var idlen = lib.math.shorthand.gen(id,&idbuf[0]) 459 + 460 + var url = lib.str.acc{}:compose('/media/a/',pstring{&idbuf[0],idlen}):finalize() 461 + co:reroute(url.ptr) 462 + url:free() 463 + else goto badop end 464 + end 465 + else goto e404 end 466 + do return end 467 + 468 + ::badop:: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end 469 + ::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded by this user') return end 403 470 end 404 471 405 472 do local branches = quote end 406 473 local filename, flen = symbol(&int8), symbol(intptr) 407 474 local page = symbol(lib.http.page) 408 475 local send = label() 409 476 local storage = data.stmap ................................................................................ 483 550 var path = lib.http.hier(uri) defer path:free() 484 551 if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then 485 552 http.actor_profile_uid(co, path, meth) 486 553 elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then 487 554 http.tweet_page(co, path, meth) 488 555 elseif path(0):cmp(lib.str.lit('tl')) then 489 556 http.timeline(co, path) 557 + elseif path(0):cmp(lib.str.lit('media')) then 558 + http.media_manager(co, path, meth) 490 559 elseif path(0):cmp(lib.str.lit('doc')) then 491 560 if not meth_get(meth) then goto wrongmeth end 492 561 http.documentation(co, path) 493 562 elseif path(0):cmp(lib.str.lit('conf')) then 494 563 if co.aid == 0 then goto unauth end 495 564 http.configure(co,path,meth) 496 565 else goto notfound end
Modified srv.t from [d4dcecb4e5] to [a1d0408148].
134 134 aid: uint64 -- 0 if logged out 135 135 aid_issue: lib.store.timepoint 136 136 who: &lib.store.actor -- who we're logged in as, if aid ~= 0 137 137 peer: lib.store.inet 138 138 reqtype: lib.http.mime.t -- negotiated content type 139 139 method: lib.http.method.t 140 140 live_last: lib.store.timepoint 141 + uploads: lib.mem.vec(lib.http.upload) 142 + body: lib.str.t 141 143 -- cache 142 144 ui_hue: uint16 143 145 navbar: lib.mem.ptr(int8) 144 146 actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries 145 147 -- private 146 148 varbuf: lib.mem.ptr(int8) 147 149 vbofs: &int8 ................................................................................ 328 330 else return nil, 0 end 329 331 end 330 332 terra convo:pgetv(name: rawstring) 331 333 var s,l = self:getv(name) 332 334 return pstring { ptr = s, ct = l } 333 335 end 334 336 335 -local urimatch = macro(function(uri, ptn) 336 - return `lib.net.mg_globmatch(ptn, [#ptn], uri.ptr, uri.ct+1) 337 -end) 338 - 339 337 local route = {} -- these are defined in route.t, as they need access to renderers 340 338 terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {} 341 339 342 340 local mimetypes = { 343 341 {'html', 'text/html'}; 344 342 {'json', 'application/json'}; 345 343 {'mkdown', 'text/markdown'}; ................................................................................ 391 389 reqtype = lib.http.mime.none; 392 390 peer = peer, live_last = 0; 393 391 } co.varbuf.ptr = nil 394 392 co.navbar.ptr = nil 395 393 co.actorcache.top = 0 396 394 co.actorcache.cur = 0 397 395 co.ui_hue = server.cfg.ui_hue 396 + co.body.ptr = msg.body.ptr co.body.ct = msg.body.len 398 397 399 398 -- first, check for an accept header. if it's there, we need to 400 399 -- iterate over the values and pick the highest-priority one 401 400 do var acc = lib.http.findheader(msg, 'Accept') 402 401 -- TODO handle q-value 403 402 if acc ~= nil and acc.ptr ~= nil then 404 403 var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr } ................................................................................ 511 510 end 512 511 uri.ct = msg.uri.len 513 512 else uri.ct = urideclen end 514 513 lib.dbg('routing URI ', {uri.ptr, uri.ct}) 515 514 516 515 if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then 517 516 co.method = [lib.http.method.get] 518 - route.dispatch_http(&co, uri, [lib.http.method.get]) 519 517 elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then 520 - co.method = [lib.http.method.get] 521 - route.dispatch_http(&co, uri, [lib.http.method.post]) 518 + co.method = [lib.http.method.post] 522 519 elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then 523 520 co.method = [lib.http.method.head] 524 - route.dispatch_http(&co, uri, [lib.http.method.head]) 525 521 elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then 526 522 co.method = [lib.http.method.options] 527 - route.dispatch_http(&co, uri, [lib.http.method.options]) 528 523 else 529 524 co:complain(400,'unknown method','you have submitted an invalid http request') 525 + goto fail 526 + end 527 + -- check for a content-type header, and see if it's a multipart/ 528 + -- form-data encoded POST request so we can handle file uploads 529 + co.uploads.sz = 0 co.uploads.run = 0 530 + if co.method == [lib.http.method.post] then 531 + var ctt = lib.http.findheader(msg, 'Content-Type') 532 + if ctt ~= nil then 533 + lib.dbg('found content type', {ctt.ptr,ctt.ct}) 534 + if lib.str.ncmp(ctt.ptr,'multipart/form-data;',20) == 0 then 535 + var p = lib.str.ffw(ctt.ptr + 20,ctt.ct-20) 536 + if lib.str.ncmp(p,'boundary=',9) ~= 0 then 537 + co:complain(400,'bad request','unrecognized content-type') 538 + goto fail 539 + end 540 + var boundary = pstring {ptr=p+9,ct=ctt.ct - ((p - ctt.ptr) + 9)} 541 + lib.dbg('got boundary ',{boundary.ptr,boundary.ct}) 542 + co.method = lib.http.method.post_file 543 + co.uploads:init(8) 544 + 545 + var bsr = (lib.str.acc{}):compose('\r\n--',boundary,'\r\n'):finalize() 546 + 547 + var upmap = lib.str.splitmap(co.body,bsr,8) 548 + -- first entry may not be preceded by header-break 549 + if lib.str.find(upmap(0), pstring { 550 + ptr = bsr.ptr + 2, ct = bsr.ct - 2 551 + }):ref() then 552 + upmap(0).ptr = upmap(0).ptr + (bsr.ct - 2) 553 + upmap(0).ct = upmap(0).ct - (bsr.ct - 2) 554 + end 555 + 556 + -- last entry is weird 557 + do var lsr = (lib.str.acc{}):compose('\r\n--',boundary,'--\r\n'):finalize() 558 + var lsent = upmap.ptr + (upmap.ct - 1) 559 + var halt = lib.str.find(@lsent, lsr) 560 + if halt:ref() then 561 + lsent.ct = halt.ptr - lsent.ptr 562 + end 563 + lsr:free() end 564 + 565 + for i=0,upmap.ct do 566 + var hdrbrk = lib.str.find(upmap(i), lib.str.plit'\r\n\r\n') 567 + if hdrbrk:ref() then 568 + lib.dbg('got new entry') 569 + var hdrtxt = pstring {upmap(i).ptr,upmap(i).ct - hdrbrk.ct} 570 + var hdrs = lib.str.splitmap(hdrtxt, '\r\n',6) 571 + var ctt = pstring.null() 572 + var ctd = pstring.null() 573 + for j=0, hdrs.ct do 574 + var brk = lib.str.find(hdrs(j),lib.str.plit':') 575 + if brk:ref() then 576 + var hdr = pstring{hdrs(j).ptr,hdrs(j).ct - brk.ct} 577 + var val = pstring{brk.ptr+1, brk.ct-1}:ffw() 578 + if hdr:cmp(lib.str.plit'Content-Type') then 579 + ctt = val 580 + elseif hdr:cmp(lib.str.plit'Content-Disposition') then 581 + ctd = val 582 + end 583 + end 584 + end 585 + if ctd:ref() then 586 + var ctdvals = lib.str.splitmap(ctd, ';', 4) defer ctdvals:free() 587 + if ctdvals(0):cmp(lib.str.plit'form-data') and ctdvals.ct > 1 then 588 + lib.dbg('found form data') 589 + var fld = pstring.null() 590 + var file = pstring.null() 591 + for j=1, ctdvals.ct do var v = ctdvals(j):ffw() 592 + var x = lib.str.find(v,lib.str.plit'=') 593 + if x:ref() then 594 + var key = pstring{v.ptr, v.ct - x.ct} 595 + var val = pstring{x.ptr + 1, x.ct - 1} 596 + var decval, ofs, sp = lib.str.toknext(val,@';',true) 597 + if key:cmp(lib.str.plit'name') then 598 + fld = decval 599 + elseif key:cmp(lib.str.plit'filename') then 600 + file = decval 601 + else decval:free() end 602 + end 603 + end 604 + if fld:ref() then 605 + var nextup = co.uploads:new() 606 + if ctt:ref() then 607 + nextup.ctype = ctt 608 + else 609 + nextup.ctype = pstring.null() 610 + end 611 + nextup.body = pstring { 612 + ptr = hdrbrk.ptr + 4; 613 + ct = hdrbrk.ct - 4; 614 + } 615 + nextup.ctype = ctt 616 + nextup.field = fld 617 + nextup.filename = file 618 + end 619 + end 620 + end 621 + end 622 + end 623 + bsr:free() 624 + upmap:free() 625 + end 626 + end 627 + end 628 + 629 + route.dispatch_http(&co, uri, co.method) 630 + if co.uploads.run > 0 then 631 + for i=0,co.uploads.sz do 632 + co.uploads(i).filename:free() 633 + co.uploads(i).field:free() 634 + end 635 + co.uploads:free() 530 636 end 531 637 638 + ::fail:: 532 639 if co.aid ~= 0 then lib.mem.heapf(co.who) end 533 640 if co.varbuf.ptr ~= nil then co.varbuf:free() end 534 641 if co.navbar.ptr ~= nil then co.navbar:free() end 535 642 co.actorcache:free() 536 643 end 537 644 end 538 645 end;
Added static/file.svg version [c89d070bea].
1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 +<!-- Created with Inkscape (http://www.inkscape.org/) --> 3 + 4 +<svg 5 + xmlns:dc="http://purl.org/dc/elements/1.1/" 6 + xmlns:cc="http://creativecommons.org/ns#" 7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 8 + xmlns:svg="http://www.w3.org/2000/svg" 9 + xmlns="http://www.w3.org/2000/svg" 10 + xmlns:xlink="http://www.w3.org/1999/xlink" 11 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 12 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 13 + width="20" 14 + height="20" 15 + viewBox="0 0 5.2916664 5.2916665" 16 + version="1.1" 17 + id="svg8" 18 + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" 19 + sodipodi:docname="file.svg"> 20 + <defs 21 + id="defs2"> 22 + <linearGradient 23 + inkscape:collect="always" 24 + id="linearGradient935"> 25 + <stop 26 + style="stop-color:#39104b;stop-opacity:1;" 27 + offset="0" 28 + id="stop931" /> 29 + <stop 30 + style="stop-color:#39104b;stop-opacity:0;" 31 + offset="1" 32 + id="stop933" /> 33 + </linearGradient> 34 + <linearGradient 35 + id="linearGradient923" 36 + inkscape:collect="always"> 37 + <stop 38 + id="stop919" 39 + offset="0" 40 + style="stop-color:#f0cfff;stop-opacity:1" /> 41 + <stop 42 + id="stop921" 43 + offset="1" 44 + style="stop-color:#eabcff;stop-opacity:0;" /> 45 + </linearGradient> 46 + <linearGradient 47 + inkscape:collect="always" 48 + id="linearGradient904"> 49 + <stop 50 + style="stop-color:#df9aff;stop-opacity:1;" 51 + offset="0" 52 + id="stop900" /> 53 + <stop 54 + style="stop-color:#df9aff;stop-opacity:0;" 55 + offset="1" 56 + id="stop902" /> 57 + </linearGradient> 58 + <linearGradient 59 + inkscape:collect="always" 60 + id="linearGradient896"> 61 + <stop 62 + style="stop-color:#eabcff;stop-opacity:1;" 63 + offset="0" 64 + id="stop892" /> 65 + <stop 66 + style="stop-color:#eabcff;stop-opacity:0;" 67 + offset="1" 68 + id="stop894" /> 69 + </linearGradient> 70 + <linearGradient 71 + inkscape:collect="always" 72 + id="linearGradient954"> 73 + <stop 74 + style="stop-color:#ffffff;stop-opacity:1;" 75 + offset="0" 76 + id="stop950" /> 77 + <stop 78 + style="stop-color:#ffffff;stop-opacity:0;" 79 + offset="1" 80 + id="stop952" /> 81 + </linearGradient> 82 + <linearGradient 83 + inkscape:collect="always" 84 + id="linearGradient938"> 85 + <stop 86 + style="stop-color:#d9fff6;stop-opacity:1;" 87 + offset="0" 88 + id="stop934" /> 89 + <stop 90 + style="stop-color:#d9fff6;stop-opacity:0;" 91 + offset="1" 92 + id="stop936" /> 93 + </linearGradient> 94 + <linearGradient 95 + inkscape:collect="always" 96 + id="linearGradient1403"> 97 + <stop 98 + style="stop-color:#ccaaff;stop-opacity:1;" 99 + offset="0" 100 + id="stop1399" /> 101 + <stop 102 + style="stop-color:#ccaaff;stop-opacity:0;" 103 + offset="1" 104 + id="stop1401" /> 105 + </linearGradient> 106 + <linearGradient 107 + id="linearGradient1395" 108 + inkscape:collect="always"> 109 + <stop 110 + id="stop1391" 111 + offset="0" 112 + style="stop-color:#ff1616;stop-opacity:1" /> 113 + <stop 114 + id="stop1393" 115 + offset="1" 116 + style="stop-color:#ff1d1d;stop-opacity:0" /> 117 + </linearGradient> 118 + <linearGradient 119 + inkscape:collect="always" 120 + id="linearGradient1383"> 121 + <stop 122 + style="stop-color:#980000;stop-opacity:1;" 123 + offset="0" 124 + id="stop1379" /> 125 + <stop 126 + style="stop-color:#980000;stop-opacity:0;" 127 + offset="1" 128 + id="stop1381" /> 129 + </linearGradient> 130 + <linearGradient 131 + inkscape:collect="always" 132 + id="linearGradient832"> 133 + <stop 134 + style="stop-color:#ffcfcf;stop-opacity:1;" 135 + offset="0" 136 + id="stop828" /> 137 + <stop 138 + style="stop-color:#ffcfcf;stop-opacity:0;" 139 + offset="1" 140 + id="stop830" /> 141 + </linearGradient> 142 + <radialGradient 143 + inkscape:collect="always" 144 + xlink:href="#linearGradient832" 145 + id="radialGradient834" 146 + cx="3.2286437" 147 + cy="286.62921" 148 + fx="3.2286437" 149 + fy="286.62921" 150 + r="1.0866126" 151 + gradientTransform="matrix(1.8608797,0.8147617,-0.38242057,0.87343168,106.71446,33.692223)" 152 + gradientUnits="userSpaceOnUse" /> 153 + <radialGradient 154 + inkscape:collect="always" 155 + xlink:href="#linearGradient1383" 156 + id="radialGradient1385" 157 + cx="4.1787109" 158 + cy="286.89261" 159 + fx="4.1787109" 160 + fy="286.89261" 161 + r="1.2260786" 162 + gradientTransform="matrix(1.7016464,0,0,1.6348586,-2.9319775,-182.10895)" 163 + gradientUnits="userSpaceOnUse" /> 164 + <radialGradient 165 + inkscape:collect="always" 166 + xlink:href="#linearGradient1395" 167 + id="radialGradient1389" 168 + gradientUnits="userSpaceOnUse" 169 + gradientTransform="matrix(0.66230313,-1.6430738,1.0154487,0.40931507,-290.06307,177.39489)" 170 + cx="4.02069" 171 + cy="287.79269" 172 + fx="4.02069" 173 + fy="287.79269" 174 + r="1.0866126" /> 175 + <linearGradient 176 + inkscape:collect="always" 177 + xlink:href="#linearGradient1403" 178 + id="linearGradient1405" 179 + x1="8.3939333" 180 + y1="288.1091" 181 + x2="7.0158253" 182 + y2="287.32819" 183 + gradientUnits="userSpaceOnUse" /> 184 + <linearGradient 185 + inkscape:collect="always" 186 + xlink:href="#linearGradient938" 187 + id="linearGradient940" 188 + x1="7.609839" 189 + y1="288.73215" 190 + x2="7.609839" 191 + y2="283.78305" 192 + gradientUnits="userSpaceOnUse" /> 193 + <linearGradient 194 + inkscape:collect="always" 195 + xlink:href="#linearGradient954" 196 + id="linearGradient956" 197 + x1="3.0150654" 198 + y1="285.94464" 199 + x2="3.0150654" 200 + y2="282.40109" 201 + gradientUnits="userSpaceOnUse" /> 202 + <linearGradient 203 + inkscape:collect="always" 204 + xlink:href="#linearGradient954" 205 + id="linearGradient1138" 206 + gradientUnits="userSpaceOnUse" 207 + x1="3.0150654" 208 + y1="285.94464" 209 + x2="3.0150654" 210 + y2="284.62277" /> 211 + <linearGradient 212 + inkscape:collect="always" 213 + xlink:href="#linearGradient896" 214 + id="linearGradient898" 215 + x1="2.6224887" 216 + y1="20" 217 + x2="2.6224887" 218 + y2="-0.44642866" 219 + gradientUnits="userSpaceOnUse" 220 + gradientTransform="matrix(0.26458333,0,0,0.26458333,2.6134662,283.36966)" /> 221 + <linearGradient 222 + inkscape:collect="always" 223 + xlink:href="#linearGradient904" 224 + id="linearGradient906" 225 + x1="5.1028705" 226 + y1="285.45639" 227 + x2="6.1422977" 228 + y2="284.41696" 229 + gradientUnits="userSpaceOnUse" 230 + gradientTransform="translate(1.4605056e-7,1.403324e-5)" /> 231 + <linearGradient 232 + inkscape:collect="always" 233 + xlink:href="#linearGradient923" 234 + id="linearGradient915" 235 + gradientUnits="userSpaceOnUse" 236 + x1="2.6224887" 237 + y1="-7.0215807" 238 + x2="2.6224887" 239 + y2="19.346249" /> 240 + <linearGradient 241 + inkscape:collect="always" 242 + xlink:href="#linearGradient904" 243 + id="linearGradient927" 244 + gradientUnits="userSpaceOnUse" 245 + gradientTransform="matrix(1.1002873,0,0,1.1002873,-0.68825328,-28.478577)" 246 + x1="5.2755661" 247 + y1="285.28369" 248 + x2="5.7849226" 249 + y2="284.77432" /> 250 + <radialGradient 251 + inkscape:collect="always" 252 + xlink:href="#linearGradient935" 253 + id="radialGradient939" 254 + cx="6.3029079" 255 + cy="284.65445" 256 + fx="6.3029079" 257 + fy="284.65445" 258 + r="1.6035197" 259 + gradientTransform="matrix(1.3125186,0,0,1.1401099,-1.643629,-40.275795)" 260 + gradientUnits="userSpaceOnUse" /> 261 + </defs> 262 + <sodipodi:namedview 263 + id="base" 264 + pagecolor="#181818" 265 + bordercolor="#666666" 266 + borderopacity="1.0" 267 + inkscape:pageopacity="0" 268 + inkscape:pageshadow="2" 269 + inkscape:zoom="2.8" 270 + inkscape:cx="-104.22073" 271 + inkscape:cy="-48.222179" 272 + inkscape:document-units="mm" 273 + inkscape:current-layer="layer1" 274 + showgrid="false" 275 + units="px" 276 + inkscape:window-width="1920" 277 + inkscape:window-height="1042" 278 + inkscape:window-x="0" 279 + inkscape:window-y="38" 280 + inkscape:window-maximized="0" 281 + showguides="false" 282 + fit-margin-top="0" 283 + fit-margin-left="0" 284 + fit-margin-right="0" 285 + fit-margin-bottom="0" /> 286 + <metadata 287 + id="metadata5"> 288 + <rdf:RDF> 289 + <cc:Work 290 + rdf:about=""> 291 + <dc:format>image/svg+xml</dc:format> 292 + <dc:type 293 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> 294 + <dc:title></dc:title> 295 + </cc:Work> 296 + </rdf:RDF> 297 + </metadata> 298 + <g 299 + inkscape:label="Layer 1" 300 + inkscape:groupmode="layer" 301 + id="layer1" 302 + transform="translate(-2.6134661,-283.36966)"> 303 + <path 304 + sodipodi:type="inkscape:offset" 305 + inkscape:radius="1.2412" 306 + inkscape:original="M 5.9453125 2.2695312 C 4.8334737 2.2695312 3.9394531 3.1635518 3.9394531 4.2753906 L 3.9394531 15.724609 C 3.9394531 16.836448 4.8334737 17.730469 5.9453125 17.730469 L 14.054688 17.730469 C 15.166526 17.730469 16.060547 16.836448 16.060547 15.724609 L 16.060547 7.6113281 L 10.71875 2.2695312 L 5.9453125 2.2695312 z " 307 + style="opacity:0.223;vector-effect:none;fill:url(#linearGradient915);fill-opacity:1;stroke:none;stroke-width:0.62362206;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" 308 + id="path913" 309 + d="m 5.9453125,1.0292969 c -1.777198,0 -3.2460937,1.4688957 -3.2460937,3.2460937 V 15.724609 c -1e-7,1.777199 1.4688954,3.246094 3.2460937,3.246094 h 8.1093755 c 1.777198,0 3.246093,-1.468895 3.246093,-3.246094 V 7.6113281 A 1.2413241,1.2413241 0 0 0 16.9375,6.734375 L 11.595703,1.3925781 A 1.2413241,1.2413241 0 0 0 10.71875,1.0292969 Z" 310 + transform="matrix(0.26458333,0,0,0.26458333,2.6134662,283.36966)" /> 311 + <path 312 + style="opacity:1;vector-effect:none;fill:url(#linearGradient898);fill-opacity:1;stroke:none;stroke-width:0.16500001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" 313 + d="m 4.1864967,283.97014 c -0.294174,0 -0.5307169,0.23654 -0.5307169,0.53072 v 3.02927 c 0,0.29417 0.2365429,0.53072 0.5307169,0.53072 h 2.1456056 c 0.2941738,0 0.5307169,-0.23655 0.5307169,-0.53072 v -2.14664 l -1.4133505,-1.41335 z" 314 + id="rect882" 315 + inkscape:connector-curvature="0" /> 316 + <path 317 + inkscape:connector-curvature="0" 318 + id="path929" 319 + d="m 4.1864967,283.97014 c -0.294174,0 -0.5307169,0.23654 -0.5307169,0.53072 v 3.02927 c 0,0.29417 0.2365429,0.53072 0.5307169,0.53072 h 2.1456056 c 0.2941738,0 0.5307169,-0.23655 0.5307169,-0.53072 v -2.14664 l -1.4133505,-1.41335 z" 320 + style="opacity:0.71;vector-effect:none;fill:url(#radialGradient939);fill-opacity:1;stroke:none;stroke-width:0.16500001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> 321 + <path 322 + inkscape:connector-curvature="0" 323 + id="path925" 324 + d="m 5.3077278,283.97015 v 0.97114 c 0,0.32367 0.2602653,0.58395 0.583941,0.58395 h 0.9711506 z" 325 + style="fill:url(#linearGradient927);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.449" /> 326 + <path 327 + style="fill:url(#linearGradient906);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 328 + d="m 5.4494687,283.97014 v 0.88263 c 0,0.29417 0.2365431,0.53072 0.5307169,0.53072 h 0.8826336 z" 329 + id="path890" 330 + inkscape:connector-curvature="0" /> 331 + </g> 332 +</svg>
Modified static/style.scss from [37c2876b01] to [7f55f7d5d1].
1002 1002 } 1003 1003 > article.post { 1004 1004 margin: 0.1in 0.2in; 1005 1005 margin-left: 0.4in; 1006 1006 } 1007 1007 } 1008 1008 } 1009 + 1010 +.media.manager main, .media.gallery { 1011 + display: grid; 1012 + grid-template-columns: 2in 1fr; 1013 + grid-template-rows: max-content 1fr; 1014 + menu { 1015 + @extend %navmenu; 1016 + } 1017 + .gallery, .dir { 1018 + background: tone(-55%,-0.5); 1019 + border: 1px solid tone(-60%); 1020 + padding: 0.2in; 1021 + display: flex; 1022 + flex-wrap: wrap; 1023 + } 1024 + .gallery { 1025 + grid-row: 1/2; grid-column: 2/3; 1026 + margin-left: 0.1in; 1027 + flex-flow: row; 1028 + > a[href].thumb { 1029 + display: block; 1030 + width: 1.5in; 1031 + padding: 0.1in; 1032 + height: max-content; 1033 + > img { 1034 + width: 1.5in; height: 1.5in; 1035 + } 1036 + > .caption { 1037 + text-align: center; 1038 + font-size: 80%; 1039 + } 1040 + } 1041 + } 1042 + .dir { 1043 + grid-row: 2/3; grid-column: 1/3; 1044 + margin-top: 0.1in; 1045 + flex-flow: column; 1046 + flex-grow: 1; 1047 + > a[href].file { 1048 + padding: 0.1in 0.15in; 1049 + text-decoration: none; 1050 + height: max-content; 1051 + background-image: url(/s/file.webp); //TODO different icons for different mime types 1052 + background-repeat: no-repeat; 1053 + background-position: left; 1054 + padding-left: 0.4in; 1055 + > .label { 1056 + text-decoration: underline; 1057 + } 1058 + > .mime { 1059 + font-style: italic; 1060 + opacity: 60%; 1061 + margin-left: 0.5ex; 1062 + } 1063 + } 1064 + } 1065 +} 1066 + 1067 +.media.upload form { 1068 + padding: 0.1in 0.2in; 1069 + @extend %box; 1070 +}
Modified store.t from [54fed43947] to [fdc1c1d9e2].
226 226 rtdby: uint64 -- 0 if not rt 227 227 rtact: uint64 -- 0 if not rt, id of rt action otherwise 228 228 isreply: bool 229 229 source: &m.source 230 230 231 231 -- save :: bool -> {} (defined in acl.t due to dep. hell) 232 232 } 233 + 234 +struct m.artifact { 235 + rid: uint64 236 + owner: uint64 237 + desc: str 238 + folder: str 239 + mime: str 240 + url: str 241 +} 233 242 234 243 m.user_conf_funcs = function(be,n,ty,rty,rty2) 235 244 rty = rty or ty 236 245 local gt 237 246 if not rty2 -- what the fuck? 238 247 then gt = {&m.source, uint64, rawstring} -> rty; 239 248 else gt = {&m.source, uint64, rawstring} -> {rty, rty2}; ................................................................................ 447 456 -- artifact: bytea 448 457 -- mime: pstring 449 458 artifact_quicksearch: {&m.source, lib.mem.ptr(uint8)} -> {uint64,bool} 450 459 -- checks whether a hash is already in the database without uploading 451 460 -- the entire file to the database server 452 461 -- hash: bytea 453 462 --> artifact id (0 if null), suppressed? 454 - artifact_expropriate: {&m.source, uint64, uint64, lib.mem.ptr(int8)} -> {} 463 + artifact_expropriate: {&m.source, uint64, uint64, lib.str.t, lib.str.t} -> {} 455 464 -- claims an existing artifact for the user's own collection 456 465 -- uid: uint64 457 466 -- artifact id: uint64 458 467 -- description: pstring 468 + -- folder: pstring 469 + artifact_claim_alter: {&m.source, uint64, uint64, lib.str.t, lib.str.t} -> {} 470 + -- edits an existing claim to an artifact 471 + -- ibid 459 472 artifact_disclaim: {&m.source, uint64, uint64} -> {} 460 473 -- a user disclaims their ownership stake in an artifact, removing it from 461 474 -- the database entirely if they were the only owner, and removing their 462 475 -- description of it either way 463 476 -- uid: uint64 464 477 -- artifact id: uint64 465 478 artifact_excise: {&m.source, uint64, bool} -> {} ................................................................................ 466 479 -- (admin action) forcibly excise an artifact from the database, deleting 467 480 -- all links to it and removing it from users' collections. if "blacklist," 468 481 -- the artifact will be banned and attempts to upload it in the future 469 482 -- will fail, triggering a report. mainly intended for dealing with spam, 470 483 -- IP violations, That Which Shall Not Be Named, and various other infohazards. 471 484 -- artifact id: uint64 472 485 -- blacklist: bool 486 + artifact_enum_uid: {&m.source, uint64, lib.str.t} -> lib.mem.lstptr(m.artifact) 487 + -- produces a list of artifacts claimed by a user, optionally 488 + -- restricted by folder (empty string = new only) 489 + artifact_fetch: {&m.source, uint64, uint64} -> lib.mem.ptr(m.artifact) 490 + -- fetch a user's view of an artifact 491 + -- uid: uint64 492 + -- rid: uint64 493 + artifact_load: {&m.source, uint64} -> {lib.mem.ptr(uint8),lib.str.t} 494 + -- load the body of an artifact into memory (also returns mime) 473 495 474 496 nkvd_report_issue: {&m.source, &m.kompromat} -> {} 475 497 -- an incidence of Badthink has been detected. report it immediately 476 498 -- to the Supreme Soviet 477 499 nkvd_reports_enum: {&m.source, &m.kompromat} -> lib.mem.ptr(m.kompromat) 478 500 -- search through the Archives 479 501 -- proto: kompromat (null for all records, or a prototype describing the records to return)
Modified str.t from [004fceba8a] to [d98a573fe7].
17 17 ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring); 18 18 fmt = terralib.externfunction('asprintf', 19 19 terralib.types.funcpointer({&rawstring,rawstring},{int},true)); 20 20 bfmt = terralib.externfunction('sprintf', 21 21 terralib.types.funcpointer({rawstring,rawstring},{int},true)); 22 22 span = terralib.externfunction('strspn',{rawstring, rawstring} -> rawstring); 23 23 } 24 + 25 +terra m.ffw(str: &int8, maxlen: intptr) 26 + if maxlen == 0 then maxlen = m.sz(str) end 27 + while maxlen > 0 and @str ~= 0 and 28 + (@str == @' ' or @str == @'\t' or @str == @'\n') do 29 + str = str + 1 30 + maxlen = maxlen - 1 31 + end 32 + return str 33 +end 34 + 24 35 25 36 do local strptr = (lib.mem.ptr(int8)) 26 37 local strref = (lib.mem.ref(int8)) 27 38 local byteptr = (lib.mem.ptr(uint8)) 28 39 strptr.metamethods.__cast = function(from,to,e) 29 40 if from == &int8 then 30 41 return `strptr {ptr = e, ct = m.sz(e)} ................................................................................ 51 62 var sz = lib.math.biggest(self.ct, other.ct) 52 63 for i = 0, sz do 53 64 if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end 54 65 if self.ptr[i] ~= other.ptr[i] then return false end 55 66 end 56 67 return true 57 68 end 58 - 69 + terra strptr:ffw() 70 + var newp = m.ffw(self.ptr,self.ct) 71 + var newct = self.ct - (newp - self.ptr) 72 + return strptr { ptr = newp, ct = newct } 73 + end 59 74 strptr.methods.cmpl = macro(function(self,other) 60 75 return `self:cmp(strptr { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) 61 76 end) 62 77 strref.methods.cmpl = macro(function(self,other) 63 78 return `self:cmp(strref { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) 64 79 end) 65 80 ................................................................................ 357 372 for j=0, reject.ct do 358 373 if str.ptr[i] == reject.ptr[j] then return i end 359 374 end 360 375 end 361 376 return maxlen 362 377 end 363 378 364 -terra m.ffw(str: &int8, maxlen: intptr) 365 - while maxlen > 0 and @str ~= 0 and 366 - (@str == @' ' or @str == @'\t' or @str == @'\n') do 367 - str = str + 1 368 - maxlen = maxlen - 1 369 - end 370 - return str 371 -end 372 - 373 379 terra m.ffw_unsafe(str: &int8) 374 380 while @str ~= 0 and 375 381 (@str == @' ' or @str == @'\t' or @str == @'\n') do 376 382 str = str + 1 377 383 end 378 384 return str 379 385 end 386 + 387 +terra m.find(haystack: pstr, needle: pstr): pstr 388 + for i=0,haystack.ct do 389 + for j=0, needle.ct do 390 + if haystack(i + j) ~= needle(j) then goto nomatch end 391 + end 392 + do return pstr { 393 + ptr = haystack.ptr + i; 394 + ct = haystack.ct - i; 395 + } end 396 + ::nomatch::end 397 + return pstr.null() 398 +end 399 + 400 +terra m.splitmap(str: pstr, delim: pstr, expect: uint16) 401 + var vec: lib.mem.vec(pstr) vec:init(expect) 402 + var start = pstr{str.ptr, str.ct} 403 + while true do 404 + var n = m.find(start, delim) 405 + if not n then break end 406 + vec:push(pstr {ptr = start.ptr, ct = start.ct - n.ct}) 407 + n.ptr = n.ptr + delim.ct 408 + n.ct = n.ct - delim.ct 409 + start = n 410 + end 411 + vec:push(start) 412 + return vec:crush() 413 +end 414 + 415 +terra m.toknext(str: m.t, delim: int8, brkspace: bool): {pstr,intptr,bool} 416 + var b: m.acc b:init(48) 417 + var mode: int8 = 0 418 + var esc = false 419 + var spacebroke = false 420 + var max = 0 421 + for i=0, str.ct do 422 + max = i 423 + if str(i) == 0 then break 424 + elseif esc == true then b:push(str.ptr + i,1) esc = false 425 + elseif str(i) == @'\\' then esc = true 426 + 427 + elseif mode == 0 and str(i) == delim then break 428 + elseif mode ~= 2 and str(i) == @'"' then 429 + if mode == 1 430 + then mode = 0 431 + else mode = 1 432 + end 433 + elseif mode ~= 1 and str(i) == @"'" then 434 + if mode == 2 435 + then mode = 0 436 + else mode = 2 437 + end 438 + 439 + elseif brkspace and mode == 0 and ( 440 + str(i) == @' ' or str(i) == @'\t' or 441 + str(i) == @'\r' or str(i) == @'\n') then 442 + spacebroke = true 443 + break 444 + 445 + else b:push(str.ptr + i,1) end 446 + end 447 + if mode ~= 0 then return m.t.null(), 0, false end 448 + 449 + return b:finalize(), max, spacebroke 450 +end 380 451 381 452 return m
Modified view/load.lua from [1dbf5e584d] to [fbf23a2927].
6 6 local sources = { 7 7 'docskel'; 8 8 'confirm'; 9 9 'tweet'; 10 10 'profile'; 11 11 'compose'; 12 12 'notice'; 13 + 14 + 'media-gallery'; 15 + 'media-upload'; 13 16 14 17 'login-username'; 15 18 'login-challenge'; 16 19 17 20 'conf'; 18 21 'conf-profile'; 19 22 'conf-sec';
Added view/media-gallery.tpl version [d752c55f41].
1 +<menu>@menu 2 + <a href="/media">new uploads</a> 3 + <a href="/media/unfiled">unfiled</a> 4 + <hr> 5 + @folders 6 + <a href="/media/all">all uploads</a> 7 + <a href="/media/kind/img">all images</a> 8 + <a href="/media/kind/vid">all videos</a> 9 + <a href="/media/kind/txt">all text files</a> 10 + <a href="/media/king/misc">all others</a> 11 +</menu> 12 + 13 +<div class="dir"> 14 + @directory 15 +</div> 16 + 17 +<div class="gallery"> 18 + @images 19 +</div>
Added view/media-upload.tpl version [687485d89c].
1 +<form method="post" enctype="multipart/form-data"> 2 + <div class="elem"> 3 + <label for="file">file</label> 4 + <input type="file" name="file" id="file" required> 5 + </div> 6 + <div class="elem"> 7 + <label for="desc">description</label> 8 + <textarea name="desc" id="desc" placeholder="soviet troops planting the red flag on olympus mons after the battle of tharsis (1969)"></textarea> 9 + </div> 10 + <div class="elem"> 11 + <label for="folder">folder</label> 12 + <input type="text" name="folder" id="folder" list="folders"> 13 + </div> 14 + <menu class="choice horizontal"> 15 + <button>upload</button> 16 + <a class="button" href="/media">cancel</a> 17 + </menu> 18 +</form> 19 + 20 +<datalist id="folders"> 21 + @folders 22 +</datalist>
Deleted view/media.tpl version [5a68c18a8e].
1 -<menu> 2 - <a href="/user/@:xid/media">new uploads</a> 3 - @folders 4 -</menu> 5 - 6 -<div name="gallery"> 7 - @images 8 -</div> 9 - 10 -<div name="files"> 11 - @files 12 -</div>