Compare commits

..

113 Commits

Author SHA1 Message Date
af13bfc920 Let's release 0.0.11.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4478 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-27 22:26:19 +00:00
e24fd92f85 OpenSSL 1.1.1w for Android.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4477 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-27 22:23:52 +00:00
7e27cefe6a This made 32-bit happier.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4476 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-25 16:43:56 +00:00
450cf6424e Oops! Passwords go in password fields.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4475 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-23 21:29:01 +00:00
54898d3dbb More thorough checkpoint.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4474 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-23 18:49:24 +00:00
dd851a2b25 Let's try some wal truncating.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4473 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-23 18:41:58 +00:00
4c6b44eb30 Implemented password changing.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4472 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-22 22:59:26 +00:00
74a3efe78d Let's restrict valid usernames.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4471 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-22 22:41:47 +00:00
51301fc49e More room robustifying?
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4470 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-22 22:20:25 +00:00
02dd8c3dd0 Try to isolate my instability with go-ssb-room. I guess I did? Haven't found a resolution.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4469 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-21 23:38:55 +00:00
26a778c3b2 Might as well make the case consistent.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4468 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-21 00:40:47 +00:00
9fecbd97e8 Autocomplete blob refs. That's too cool.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4467 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-21 00:26:29 +00:00
e1383e3903 Move the HTTP timeout into C where we can manage it better as writes are active. Fixes an accidental 45 second GET timeout from httpd.js.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4466 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-20 23:30:29 +00:00
47532b8512 Snapping to grid.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4465 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-17 20:52:12 +00:00
3c4959433a Proof of concept of building emojis.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4464 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-17 13:22:29 +00:00
e921b4a86a Actually serialize doubles. Yikes.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4463 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-17 00:13:31 +00:00
b23b0ca239 Some include-what-you-use progress.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4462 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-13 23:39:52 +00:00
191b45f054 Better map link.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4461 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-13 23:01:15 +00:00
15d0383349 api docs work in progress.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4460 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-13 23:00:47 +00:00
d2485583fd sqlite-amalgamation-3430100.zip
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4459 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-13 21:19:37 +00:00
2b94704916 If a user visiting /login is already authenticated, bounce them away. This is me trying to avoid hassle for people who bookmarked the login page.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4458 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-11 16:52:17 +00:00
85ac6c215a Populate host and port for incoming SSB connections, too.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4457 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-09 14:45:55 +00:00
e83e665db9 Try harder to not reply to errors. Dunno.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4456 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-09 14:28:06 +00:00
645aafef16 zlib 1.3.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4455 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-07 22:44:49 +00:00
152c893a6f Fix wrong argument count on ssb.blobStore.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4454 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-07 22:44:18 +00:00
7c130dda56 Beef up the API docs.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4453 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-07 00:40:43 +00:00
2d82dad806 Restore the connections tab.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4452 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-06 22:48:06 +00:00
e8ac5b759d Fix emoji search case again, and render issues.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4451 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-06 22:35:20 +00:00
4833d18968 Fix android app version.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4450 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-05 16:21:28 +00:00
6eafded1f6 Added the memory sparkline and tried to CSS them differently.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4449 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 20:13:17 +00:00
7b440b720e Set issues app icon.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4448 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 19:47:07 +00:00
e20ba7384f Put the latlng popup on the contextmenu event.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4447 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 19:26:48 +00:00
45231c6ede Add some GPS game tabs.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4446 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 19:21:00 +00:00
35475defb5 Faster activity load through concurrency.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4445 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 18:01:06 +00:00
8741841f27 Add the missing leaflet .js.map file. Link to Google Maps from clicked map locations. Allow poup from the sandbox to support target="_blank" links, though that does not work here.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4444 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 17:44:05 +00:00
5282d19b55 Structure replies to issues as replies so that their posts don't appear so unusual.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4443 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-04 17:12:32 +00:00
d9782aa0fb Automatically reply encrypted to the same recipients.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4442 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-02 13:37:50 +00:00
9751facfb4 Add recps field to encrypted messages.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4441 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-02 13:25:31 +00:00
e0110203e7 More reliable addition of encrypted messages. There's something I'm not understanding with lit.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4440 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-02 02:06:29 +00:00
088b44cc2c Fix the admin app.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4439 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-01 16:26:24 +00:00
8f63bcbfbf Sending encrypted messages. Revealing some weird behavior, but it's working.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4438 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-09-01 01:34:01 +00:00
c8029388c9 Blockquote and connections tab tweaks.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4437 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-31 16:46:40 +00:00
d9c4d847a1 Fix Strava account linking.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4436 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-31 16:31:24 +00:00
df9d9425ec Add issues app.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4435 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-31 00:12:18 +00:00
90bb3c684e OK, no -fanalyzer on raspi, yet.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4434 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-30 23:58:01 +00:00
9c81b6de8a This should have gone away in a previous change.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4433 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-30 23:50:28 +00:00
6383498041 Oops. Makefile debugging.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4432 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-30 21:38:59 +00:00
daeb88785d Let's try -fanalyzer.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4431 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 20:57:55 +00:00
dcea08f73b Enforce a timeout on user SQL queries.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4430 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 20:23:40 +00:00
b252b921f8 Call out restricted DB access when we acquire the reader.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4429 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 19:41:54 +00:00
172826bf13 Use the right default port now that I'm not always running two different clients side-by-side.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4428 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 18:51:14 +00:00
060f1980f5 A little more paranoia around the file watcher.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4427 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 18:27:19 +00:00
e223d35252 Make the connections tab know more about tunnels and such.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4426 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 18:22:09 +00:00
99dba1a4c6 svn didn't notice that these files changed.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4425 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 17:19:59 +00:00
b52026c81f Give quoted text a bar on the side, like I'm used to.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4424 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 17:06:32 +00:00
47b8c86426 sqlite-amalgamation-3430000.zip
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4423 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-25 16:20:32 +00:00
2e55c68648 Get ready for 0.0.11.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4422 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 22:40:00 +00:00
b7362dd84d 0.0.10.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4421 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 22:37:15 +00:00
01637b31e1 Let's release 0.0.10.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4420 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 21:55:22 +00:00
0e9a39608a Exclude gg from the release.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4419 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 21:54:43 +00:00
79404e4d41 Set android min sdk version and api version all to 28. That's all we need. Should fix reported crashes.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4418 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 21:37:23 +00:00
35c21fbdaf No really, specify the API version for android.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4417 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-23 02:16:22 +00:00
8c7bd7dc11 Fix windows build.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4416 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-22 16:49:42 +00:00
09ad4f0320 More callstacks on android.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4415 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-22 16:48:12 +00:00
d96b836bef Better lifetimes still in the Java code, and turn on some strict vm policy messages.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4414 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-22 16:43:04 +00:00
59b2ffaf95 Ohh, Java does scoped resources.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4413 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-22 02:45:22 +00:00
f1b55ddd64 Attempt to track requests better. New requests need to be flagged as such. Still trying to chase tunnel instability.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4412 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-20 19:55:59 +00:00
85acac3a30 Save more context about closed connections, and include the timestamp.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4411 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-20 18:36:46 +00:00
befff5c1e5 Apps are not allowed to read directly from the blobs table.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4410 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-20 18:26:26 +00:00
d72ba81a67 Try to respond to tunnel errors I'm seeing instead of forwarding them over the tunnel, which obviously won't work. Allow creating multiple connections to the same ID if it's for the sake of a tunnel. I think this explains timeouts I'm seeing with tunnels. More error handling, too. C'mon, fix tunnels.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4409 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-20 18:25:15 +00:00
fef88e2032 Prevent the watcher's finalizer from being called before we're done with it.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4408 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-20 11:25:49 +00:00
20557e8ce4 Respond better when somebody disconnects from us with a tunnel. Trying to robustify tunnels. This is largely untested.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4407 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-17 16:54:33 +00:00
99c905e908 Fix build with the removal of split.js.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4406 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-17 00:50:22 +00:00
d7b58ee2c5 Support an edit-only mode, which brings up the editor without running the app. Helpful if the app hangs the client. Also on mobile, where both don't fit side-by-side.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4405 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-17 00:49:02 +00:00
faca2d387b Calculate thread busyness as the current concurrent running threads vs. the max number of threads ever seen running concurrently.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4404 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-17 00:01:59 +00:00
358d02d97f Another index I've wanted, and better error display for queries in the ssb app.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4403 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-16 22:57:16 +00:00
b66dac7465 Add an SQL query tab.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4402 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-16 22:48:59 +00:00
f7d201859a Use the sqlite authorizer for async requests, too.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4401 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-16 22:43:08 +00:00
61d2ef5469 Yeah, the ping comes in a later message. Whoops.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4400 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-15 23:56:58 +00:00
ac994b9c62 Were we not responding to ping?
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4399 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-14 16:26:06 +00:00
264dcbc331 codemirror 5.65.14.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4398 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-13 01:11:14 +00:00
e5425c0ffb Apparently the MUXRPC maximum segment size is 4096: bd350c6f9e/boxstream/box.go (L23). Reducing the send size seems to keep me connected to/through rooms longer.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4397 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-12 19:57:00 +00:00
e10803de68 Fix GPX upload.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4396 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-12 18:37:15 +00:00
07b1a0e403 Fiddling with login CSS.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4395 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-12 11:54:27 +00:00
6ed2c702d8 Hide too-new messages, and cycle between message, raw, and markdown views of messages.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4394 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-09 23:39:17 +00:00
5c1c33d33e Put the release process in the makefile, and including building the tarball contents to avoid another snafu.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4393 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-09 23:14:16 +00:00
70d37c88b5 Redo auth flow with lit. Beef up the test a bit, accordingly.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4392 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-09 22:38:41 +00:00
1ba37d95b5 More concise wait.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4391 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-07 16:13:02 +00:00
0d82198849 Remove old, broken drag+drop code.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4390 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-06 12:03:37 +00:00
39927e75f2 Attempt to support .gpx files.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4389 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-06 12:03:22 +00:00
e6fd33b969 Sure, let's add GPS game.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4388 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-06 00:26:56 +00:00
e8fe32d5af Fix a crash on android three different ways?
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4387 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-05 03:10:24 +00:00
bfc8bb864d I suspect this is necessary to prevent sending an error when we get the final response to blobs.get.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4386 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-05 02:30:45 +00:00
9179746763 Freaking CSS. Trying to make the admin page...work.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4385 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-05 01:22:27 +00:00
d0177d24cb Clean up some test cruft.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4384 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 23:44:48 +00:00
0573008c9c Set some blob auto-delete option defaults on android only.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4383 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 23:35:02 +00:00
9506f518c2 +x and shebang
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4382 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 23:33:57 +00:00
0f0ae9153b lit-html 2.8.0.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4381 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 23:10:53 +00:00
09c7c8ac64 GPS game.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4380 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 23:08:16 +00:00
5e2dfff148 Remembering permissions never worked???
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4379 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-04 00:50:13 +00:00
958b47548d There, I can wait for a thing in a shadow root like I want.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4378 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-03 00:44:35 +00:00
16155ef746 Automated enough with selenium to be able to create a Tilde Friends account, create an SSB identity, and post a first message. I'm still confused on some things, but this is progress, and I fixed a longstanding issue creating the first identity.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4377 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-03 00:30:48 +00:00
5755b61ea6 Oops, one more reference to smoothie.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4376 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-08-02 16:25:41 +00:00
353847a77f Remove the smoothie graphs. The sparklines are too good. I will rebuild whatever I'm missing with these.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4375 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-31 16:32:21 +00:00
bdf64edeb8 Expose the client's requesting URL to apps.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4374 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-31 00:26:09 +00:00
b5768dd927 Capture (almost) all worker thread time.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4373 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-29 22:29:09 +00:00
3e5abf3a4d Enable auto vacuum.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4372 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 12:22:37 +00:00
d3029639de We can't exclude libsodium's version.h!
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4371 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 12:22:16 +00:00
d21d7e4add Delete more aggressively.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4370 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 03:04:49 +00:00
afde69b5d9 Took a whack at cleaning up old blobs.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4369 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 02:51:42 +00:00
3319df3df0 Oh yeah, I was playing with the follow app.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4368 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 01:47:50 +00:00
1102feaac3 speedscope 1.16.0.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4367 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 00:42:51 +00:00
deede728be Now we're 0.0.10-wip.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4366 ed5197a5-7fde-0310-b194-c3ffbd925b24
2023-07-27 00:37:09 +00:00
16277 changed files with 22466 additions and 438245 deletions

View File

@ -3,9 +3,9 @@
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
VERSION_CODE := 9
VERSION_NUMBER := 0.0.9
VERSION_NAME := Failure is the only opportunity to begin again.
VERSION_CODE := 11
VERSION_NUMBER := 0.0.11
VERSION_NAME := Be nothing, and you will have everything to give to others.
PROJECT = tildefriends
BUILD_DIR ?= out
@ -28,8 +28,7 @@ ANDROID_SDK ?= ~/Android/Sdk
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/33.0.1
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-33
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/23.1.7779620
ANDROID_NDK_API_VERSION := 31
ANDROID_MIN_SDK_VERSION := 26
ANDROID_MIN_SDK_VERSION := 28
ANDROID_ARM64_TARGETS := \
out/androiddebug/tildefriends \
@ -62,7 +61,8 @@ $(ANDROID_TARGETS): CFLAGS += \
-fPIC \
-fdebug-compilation-dir . \
-fomit-frame-pointer \
-fno-asynchronous-unwind-tables
-fno-asynchronous-unwind-tables \
-funwind-tables
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
$(RELEASE_TARGETS): CFLAGS += -DNDEBUG
@ -81,10 +81,10 @@ windebug winrelease: LDFLAGS += \
-Ldeps/openssl/mingw64/lib
$(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android
$(ANDROID_ARM64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := aarch64-linux-android
$(ANDROID_TARGETS): CC = $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
$(ANDROID_TARGETS): CC = $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/$(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION)-clang
$(ANDROID_TARGETS): AS = $(CC)
$(ANDROID_TARGETS): CFLAGS += \
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_NDK_API_VERSION) \
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
-Wno-unknown-warning-option
$(ANDROID_ARM64_TARGETS): CFLAGS += -Ideps/openssl/android/arm64-v8a/usr/local/include
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Ldeps/openssl/android/arm64-v8a/usr/local/lib
@ -121,6 +121,10 @@ $(APP_OBJS): CFLAGS += \
-Ideps/xopt \
-Wdouble-promotion \
-Werror
ifeq ($(UNAME_M),x86_64)
$(filter-out $(BUILD_DIR)/android%,$(APP_OBJS)): CFLAGS += \
-fanalyzer
endif
BLOWFISH_SOURCES := \
deps/crypt_blowfish/crypt_blowfish.c \
@ -270,7 +274,7 @@ $(SQLITE_OBJS): CFLAGS += \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_JSON1 \
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
-DSQLITE_MAX_ATTACHED=0 \
-DSQLITE_MAX_ATTACHED=1 \
-DSQLITE_MAX_COLUMN=100 \
-DSQLITE_MAX_COMPOUND_SELECT=300 \
-DSQLITE_MAX_EXPR_DEPTH=40 \
@ -391,7 +395,7 @@ windebug winrelease: LDFLAGS += \
-lws2_32 \
-lwsock32
$(ANDROID_TARGETS): LDFLAGS += \
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_NDK_API_VERSION) \
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
-ldl \
-llog \
-lssl \
@ -481,9 +485,7 @@ PACKAGE_DIRS := \
apps/ \
core/ \
deps/codemirror/ \
deps/lit/ \
deps/split/ \
deps/smoothie/
deps/lit/
RAW_FILES := $(shell find $(PACKAGE_DIRS) -type f)
@ -524,3 +526,36 @@ apklog:
clean:
rm -rf $(BUILD_DIR)
.PHONY: clean
dist: apk
@echo "[export] $$(svn info --show-item url)"
@rm -rf tildefriends-$(VERSION_NUMBER)
@svn export -q . tildefriends-$(VERSION_NUMBER)
@echo "tildefriends-$(VERSION_NUMBER): $(VERSION_NAME)" > tildefriends-$(VERSION_NUMBER)/VERSION
@echo "[tar] tildefriends-$(VERSION_NUMBER).tar.xz"
@tar \
--exclude=apps/gg* \
--exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \
--exclude=deps/libsodium/builds/msvc/vs* \
--exclude=deps/libsodium/builds/msvc/build \
--exclude=deps/libsodium/builds/msvc/properties \
--exclude=deps/libsodium/configure \
--exclude=deps/libsodium/test \
--exclude=deps/libuv/docs \
--exclude=deps/libuv/test \
--exclude=deps/openssl \
--exclude=deps/speedscope/*.map \
--exclude=deps/sqlite/shell.c \
--exclude=deps/zlib/contrib/vstudio \
--exclude=deps/zlib/doc \
-caf tildefriends-$(VERSION_NUMBER).tar.xz tildefriends-$(VERSION_NUMBER)
@rm -rf tildefriends-$(VERSION_NUMBER)
@echo "[cp] TildeFriends-$(VERSION_NUMBER).apk"
@cp out/TildeFriends-release.apk TildeFriends-$(VERSION_NUMBER).apk
.PHONY: dist
dist-test: dist
@tar -xf tildefriends-$(VERSION_NUMBER).tar.xz
@$(MAKE) -C tildefriends-$(VERSION_NUMBER)/ debug release
@rm -rf tildefriends-$(VERSION_NUMBER)
.PHONY: dist-test

View File

@ -9,14 +9,18 @@ tfrpc.register(function global_settings_set(key, value) {
});
async function main() {
let data = {
users: {},
granted: await core.allPermissionsGranted(),
settings: await core.globalSettingsDescriptions(),
};
for (let user of await core.users()) {
data.users[user] = await core.permissionsForUser(user);
try {
let data = {
users: {},
granted: await core.allPermissionsGranted(),
settings: await core.globalSettingsDescriptions(),
};
for (let user of await core.users()) {
data.users[user] = await core.permissionsForUser(user);
}
await app.setDocument(utf8Decode(getFile('index.html')).replace('$data', JSON.stringify(data)));
} catch {
await app.setDocument('<span style="color: #f00">Only an administrator can modify these settings.</span>');
}
await app.setDocument(utf8Decode(getFile('index.html')).replace('$data', JSON.stringify(data)));
}
main();

View File

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html>
<html style="width: 100%">
<head>
<script>const g_data = $data;</script>
</head>
<body style="color: #fff">
<body style="color: #fff; width: 100%">
<h1>Tilde Friends Administration</h1>
</body>
<script type="module" src="script.js"></script>

View File

@ -25,29 +25,37 @@ window.addEventListener('load', function() {
function input_template(key, description) {
if (description.type === 'boolean') {
return html`
<label ?for=${'gs_' + key} style="grid-column: 1">${key}: </label>
<input type="checkbox" ?checked=${description.value} ?id=${'gs_' + key} style="grid-column: 2"></input>
<div style="grid-column: 3">
<button @click=${(e) => global_settings_set(key, e.srcElement.parentElement.previousElementSibling.checked)}>Set</button>
<span>${description.description}</span>
<div style="margin-top: 1em">
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
<div>
<input type="checkbox" ?checked=${description.value} id=${'gs_' + key}></input>
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.checked)}>Set</button>
<div>${description.description}</div>
</div>
</div>
`;
} else if (description.type === 'textarea') {
return html`
<label ?for=${'gs_' + key} style="grid-column: 1">${key}: </label>
<textarea style="vertical-align: top" rows=20 cols=80 ?id=${'gs_' + key}>${description.value}</textarea>
<div style="grid-column: 3">
<button @click=${(e) => global_settings_set(key, e.srcElement.parentElement.previousElementSibling.value)}>Set</button>
<span>${description.description}</span>
<div style="margin-top: 1em"">
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
<div style="width: 100%; padding: 0; margin: 0">
<div style="width: 90%; padding: 0 margin: 0">
<textarea style="vertical-align: top; width: 100%" rows=20 cols=80 id=${'gs_' + key}>${description.value}</textarea>
</div>
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.firstElementChild.value)}>Set</button>
<div>${description.description}</div>
</div>
</div>
`;
} else {
return html`
<label ?for=${'gs_' + key} style="grid-column: 1">${key}: </label>
<input type="text" value="${description.value}" ?id=${'gs_' + key}></input>
<div style="grid-column: 3">
<button @click=${(e) => global_settings_set(key, e.srcElement.parentElement.previousElementSibling.value)}>Set</button>
<span>${description.description}</span>
<div style="margin-top: 1em">
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
<div>
<input type="text" value="${description.value}" id=${'gs_' + key}></input>
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button>
<div>${description.description}</div>
</div>
</div>
`;
}
@ -67,12 +75,13 @@ window.addEventListener('load', function() {
${Object.entries(users).map(u => user_template(u[0], u[1]))}
</ul>`;
const page_template = (data) =>
html`<div>
<h2>Global Settings</h2>
<div style="display: grid">
${Object.keys(data.settings).sort().map(x => html`${input_template(x, data.settings[x])}`)}
html`<div style="padding: 0; margin: 0; width: 100%; max-width: 100%">
<h2>Global Settings</h2>
<div>
${Object.keys(data.settings).sort().map(x => html`${input_template(x, data.settings[x])}`)}
</div>
${users_template(data.users)}
</div>
${users_template(data.users)}
</div>`;
`;
render(page_template(g_data), document.body);
});

File diff suppressed because one or more lines are too long

313
apps/api/docs.js Normal file
View File

@ -0,0 +1,313 @@
export const docs = {};
docs.global = `# Tilde Friends API Documentation
Welcome to the Tilde Friends API documentation.
* [App Globals](#App_Globals)
* [Database Interface](#Database)
* [Remote Procedure Calls](#tfrpc)
<a id="App_Globals"></a>
## <span style="color: #aaf">App Globals</span>
The following are functions and values exposed to all apps in their \`app.js\` or \`handler.js\`. Most
of these are asynchronous, returning a \`Promise\` that will be resolved when the call completes, unless
noted otherwise.
This is all a work in progess. These are liable to change without warning. Feedback is welcome.
The exposed functions in this API balance multiple competing needs:
* The surface area of the exposed API ought to be fairly minimal. If something can be implemented entirely app-side, that is
generally preferred over building it into the core.
* Everything is built on this API. Ideally the admin app, the SSB app, and the editor all use standard API exposed to all
apps, with appropriate permission guards in place making it so that only trusted apps do potentially destructive operations.
There will be some things here that aren't necessarily general use to support what's required.
If you are looking at the [Tilde Friends source code](https://www.tildefriends.net/~cory/releases/),
the vast majority of these are implemented in \`src/*.js.c\` files, and exposed to apps via \`core/core.js\`.
`;
docs['core.user.credentials.session.name'] = `
*String* The name of the authenticated user.
`;
docs['app.setDocument()'] = `
Set the contents of the client &lt;iframe/&gt;.
### Parameters
* *String* **html** The HTML contents.
`;
docs['ssb.sqlAsync()'] = `
Run an SQL query against the sqlite database.
### Parameters
* *String* **query** The sqlite query.
* *Array* **args** The query arguments to bind.
* *Function* **callback** Callback called for each row result.
`;
docs['ssb.appendMessageWithIdentity()'] = `
Signs and stores a message in the SSB database.
### Parameters
* *String* **id** The public key of an SSB identity owned by the authenticated user.
* *Object* **message** The unsigned message.
`;
docs['ssb.storeMessage()'] = `
Verifies and stores a signed message in the SSB database.
### Parameters
* *Object* **message** The valid, signed message to store.
`;
docs['ssb.blobStore()'] = `
Store a blob in the SSB database.
### Parameters
* *String*/*Uint8Array* **blob** The blob contents to store
### Returns
*String* The stored blob ID.
`;
docs['ssb.blobGet()'] = `
Fetches a blob from the database.
### Parameters
* *String* **blob_id** The blob identifier to fetch (\`&....sha256\`).
### Returns
*ArrayBuffer* The blob data.
`;
docs['print()'] = `
Log debug information both to the server's console and to the visiting user's browser console when possible.
### Parameters
* **...** Whatever you want to log. Will be joined with spaces.
`;
docs['database()'] = `
Returns a database instance that is specific to the authenticated user and the given key.
### Parameters
* *String* **key** The database key.
### Returns
*Database* A database.
`;
docs['my_shared_database()'] = `
Returns a database instance that is specific to the authenticated user and the given key.
### Parameters
* *String* **package_name** The database package name.
* *String* **key** The database key.
### Returns
*Database* A database.
`;
docs['shared_database()'] = `
Returns a database instance that is shared between all users of the app, determined by its owner and app name.
### Parameters
* *String* **key** The database key.
### Returns
*Database* A database.
`;
docs['base64Decode()'] = `
Decode a base64 string to bytes.
Completes synchronously.
### Parameters
* *String* value The base64-encoded string.
### Returns
*Uint8Array* The decoded bytes.
`;
docs['base64Encode()'] = `
Encode bytes to a base64 string.
Completes synchronously.
### Parameters
* *Uint8Array* The bytes to encode.
### Returns
*String* The base64-encoded string.
`;
docs['utf8Decode()'] = `
Decode UTF-8 bytes to a string.
Completes synchronously.
### Parameters
* *Uint8Array* **value** The value to decode.
### Returns
*String* The value as a string.
`;
docs['utf8Encode()'] = `
Encodes a string to UTF-8 bytes.
Completes synchronously.
### Parameters
* *String* **value** The value to encode.
### Returns
*Uint8Array* The encoded \`value\`.
`;
docs['setTimeout()'] = `
Call a function after some delay.
### Parameters
* *Function* **callback** The function to call.
* *Number* **timeout** Number of milliseconds to wait before calling the callback function.
`;
docs['parseHttpRequest()'] = `
Parses an HTTP request.
### Parameters
* *Uint8Array* **request** The request data. Maybe be partial or contain extra data. The return value will
indicate when and where it is complete.
* *Number* **last_length** The length of the data passed on a previous attempt for the same request, or 0 initially.
### Returns
* *Integer* **-2** if the request is incomplete.
* *Integer* **-1** if the request could not be parsed.
* *Object* An object with **bytes_parsed**, **minor_version**, **path**, and **headers** fields on successful parse.
`;
docs['parseHttpResponse()'] = `
Parses an HTTP response.
### Parameters
* *Uint8Array* **response** The response data. Maybe be partial or contain extra data. The return value will
indicate when and where it is complete.
* *Number* **last_length** The length of the data passed on a previous attempt for the same response, or 0 initially.
### Returns
* *Integer* **-2** if the response is incomplete.
* *Integer* **-1** if the response could not be parsed.
* *Object* An object with **bytes_parsed**, **minor_version**, **status**, **message**, and **headers** fields on successful parse.
`;
docs['sha1Digest()'] =`
Calculates a SHA1 digest.
Completes synchronously.
### Parameters
* *String* **value** The value for which to calculate the digest.
### Returns
*String* The SHA1 digest of UTF-8 encoded \`value\`.
`;
docs['maskBytes()'] = `
Masks bytes for WebSocket communication.
Completes synchronously.
### Parameters
* *Uint8Array* **bytes** The byte array of data to mask.
* *Uint32* **mask** The mask to apply.
### Returns
*Uint32Array* The masked bytes.
`;
docs['exit()'] = `
Exits the app. But why would you want to do that?
Completes synchronously.
### Parameters
* *Integer* **exit_code** System exit code.
`;
docs['version()'] = `
Gets version information for the running server.
### Returns
*Object* Keys are things like \`name\` and \`number\` for the server itself and \`libuv\` and \`openssl\` for
dependencies. Values are *String* version numbers.
`;
docs['platform()'] = `
Gets the host operating system platform of the running server.
### Returns
*String* The platform, one of \`windows\`, \`android\`, \`linux\`, or \`other\`.
`;
docs['getFile()'] = `
Gets a file from the running app.
### Parameters
* *String* **name** Name of the file to retrieve.
### Returns
*Uint8Array* The contents of a file from the app with the given name, or *undefined*.
`;
docs.database = `
# <span style="color: #aaf">Database</span>
Local-only storage is provided by a \`Database\` type representing a key-value store.
`;
docs['database.get()'] = `
Gets a value from the database.
### Parameters
* *String* **key** The key.
### Returns
*String* The value from the database or undefined if not found.
`;
docs['database.getAll()'] = `
Gets all keys from the database.
### Returns
*Array* An array of *String* key names for all keys in the given database.
`;
docs['database.getLike()'] = `
Gets all keys and values from the database matching a pattern.
### Parameters
* *String* **pattern** An sqlite \`LIKE\` pattern to match keys against.
### Returns
*Object* An object whose keys are the database keys and values are the database values that match the given pattern.
`;
docs['database.set()'] = `
Sets a value in the database, creating a new entry or replacing an existing entry.
### Parameters
* *String* **key** The key.
* *String* **value** The value.
`;
docs['database.exchange()'] = `
Performs an atomic compare and exchange operation, setting a value in the database only if its current value matches what is expected.
### Parameters
* *String* **key** The key.
* *String* **expected** The expected value.
* *String* **value** The new value.
### Returns
*Boolean* true if the value is now the given value.
`;
docs['database.remove()'] = `
Removes an entry from the database if it exists.
### Parameters
* *String* **key** The key.
`;
docs.tfrpc = `
# <span style="color: #aaf" id="tfrpc">tfrpc</span>
\`tfrpc.js\` is a small helper script that is available to be used to facilitate communication between parts of an application.
\`tfrpc.js\` can be used to asynchronously make calls between the app code running in a sandboxed iframe in the browser
and the app process on the server.
From \`app.js\`:
\`\`\`
import * as tfrpc from '/tfrpc.js';
\`\`\`
From script running in the browser:
\`\`\`
import * as tfrpc from '/static/tfrpc.js';
\`\`\`
Either side can register or call functions, though they must be registered before they can be called. Arguments and return
values are ultimately serialized by means that attempt to preserve most JSON-serializable values as well as functions themselves.
`;
docs['tfrpc.register()'] = `
Register a function, allowing it to be called remotely.
### Parameters
* *Function* **function** The function to register. Its name will be how it will be called.
`;
docs['tfrpc.rpc.*()'] = `
Call a remote function.
### Parameters
* **...** Parameters to pass to the function.
### Returns
The return value of the called function.
`;

View File

@ -1,73 +1,163 @@
var g_following_cache = {};
var g_following_deep_cache = {};
var g_about_cache = {};
let g_about_cache = {};
async function following(db, id) {
if (g_following_cache[id]) {
return g_following_cache[id];
}
var o = await db.get(id + ":following");
const k_version = 5;
var f = o ? JSON.parse(o) : o;
if (!f || f.version != k_version) {
f = {users: [], sequence: 0, version: k_version};
}
f.users = new Set(f.users);
await ssb.sqlAsync(
"SELECT "+
" sequence, "+
" json_extract(content, '$.contact') AS contact, "+
" json_extract(content, '$.following') AS following "+
"FROM messages "+
"WHERE "+
" author = ?1 AND "+
" sequence > ?2 AND "+
" json_extract(content, '$.type') = 'contact' "+
"UNION SELECT MAX(sequence) AS sequence, NULL, NULL FROM messages WHERE author = ?1 "+
"ORDER BY sequence",
[id, f.sequence],
function(row) {
if (row.following) {
f.users.add(row.contact);
} else {
f.users.delete(row.contact);
}
f.sequence = row.sequence;
});
var as_set = f.users;
f.users = Array.from(f.users).sort();
var j = JSON.stringify(f);
if (o != j) {
await db.set(id + ":following", j);
}
f.users = as_set;
g_following_cache[id] = f.users;
return f.users;
async function query(sql, args) {
let result = [];
await ssb.sqlAsync(sql, args, function(row) {
result.push(row);
});
return result;
}
async function followingDeep(db, seed_ids, depth) {
if (depth <= 0) {
return seed_ids;
async function contacts_internal(id, last_row_id, following, max_row_id) {
let result = Object.assign({}, following[id] || {});
result.following = result.following || {};
result.blocking = result.blocking || {};
let contacts = await query(
`
SELECT content FROM messages
WHERE author = ? AND
rowid > ? AND
rowid <= ? AND
json_extract(content, '$.type') = 'contact'
ORDER BY sequence
`,
[id, last_row_id, max_row_id]);
for (let row of contacts) {
let contact = JSON.parse(row.content);
if (contact.following === true) {
result.following[contact.contact] = true;
} else if (contact.following === false) {
delete result.following[contact.contact];
} else if (contact.blocking === true) {
result.blocking[contact.contact] = true;
} else if (contact.blocking === false) {
delete result.blocking[contact.contact];
}
}
var key = JSON.stringify([seed_ids, depth]);
if (g_following_deep_cache[key]) {
return g_following_deep_cache[key];
following[id] = result;
return result;
}
async function contact(id, last_row_id, following, max_row_id) {
return await contacts_internal(id, last_row_id, following, max_row_id);
}
async function following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id) {
let contacts = await Promise.all([...new Set(ids)].map(x => contact(x, last_row_id, following, max_row_id)));
let result = {};
for (let i = 0; i < ids.length; i++) {
let id = ids[i];
let contact = contacts[i];
let all_blocking = Object.assign({}, contact.blocking, blocking);
let found = Object.keys(contact.following).filter(y => !all_blocking[y]);
let deeper = depth > 1 ? await following_deep_internal(found, depth - 1, all_blocking, last_row_id, following, max_row_id) : [];
result[id] = [id, ...found, ...deeper];
}
var f = await Promise.all(seed_ids.map(x => following(db, x).then(x => [...x])));
var ids = [].concat(...f);
var x = await followingDeep(db, [...new Set(ids)].sort(), depth - 1);
x = [...new Set([].concat(...x, ...seed_ids))].sort();
g_following_deep_cache[key] = x;
return x;
return [...new Set(Object.values(result).flat())];
}
async function following_deep(ids, depth, blocking) {
let db = await database('cache');
const k_cache_version = 5;
let cache = await db.get('following');
cache = cache ? JSON.parse(cache) : {};
if (cache.version !== k_cache_version) {
cache = {
version: k_cache_version,
following: {},
last_row_id: 0,
};
}
let max_row_id = (await query(`
SELECT MAX(rowid) AS max_row_id FROM messages
`, []))[0].max_row_id;
let result = await following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id);
cache.last_row_id = max_row_id;
let store = JSON.stringify(cache);
await db.set('following', store);
return result;
}
async function fetch_about(db, ids, users) {
const k_cache_version = 1;
let cache = await db.get('about');
cache = cache ? JSON.parse(cache) : {};
if (cache.version !== k_cache_version) {
cache = {
version: k_cache_version,
about: {},
last_row_id: 0,
};
}
let max_row_id = 0;
await ssb.sqlAsync(`
SELECT MAX(rowid) AS max_row_id FROM messages
`,
[],
function(row) {
max_row_id = row.max_row_id;
});
for (let id of Object.keys(cache.about)) {
if (ids.indexOf(id) == -1) {
delete cache.about[id];
}
}
let abouts = [];
await ssb.sqlAsync(
`
SELECT
messages.*
FROM
messages,
json_each(?1) AS following
WHERE
messages.author = following.value AND
messages.rowid > ?3 AND
messages.rowid <= ?4 AND
json_extract(messages.content, '$.type') = 'about'
UNION
SELECT
messages.*
FROM
messages,
json_each(?2) AS following
WHERE
messages.author = following.value AND
messages.rowid <= ?4 AND
json_extract(messages.content, '$.type') = 'about'
ORDER BY messages.author, messages.sequence
`,
[
JSON.stringify(ids.filter(id => cache.about[id])),
JSON.stringify(ids.filter(id => !cache.about[id])),
cache.last_row_id,
max_row_id,
]);
for (let about of abouts) {
let content = JSON.parse(about.content);
if (content.about === about.author) {
delete content.type;
delete content.about;
cache.about[about.author] = Object.assign(cache.about[about.author] || {}, content);
}
}
cache.last_row_id = max_row_id;
await db.set('about', JSON.stringify(cache));
users = users || {};
for (let id of Object.keys(cache.about)) {
users[id] = Object.assign(users[id] || {}, cache.about[id]);
}
return Object.assign({}, users);
}
async function getAbout(db, id) {
if (g_about_cache[id]) {
return g_about_cache[id];
}
var o = await db.get(id + ":about");
let o = await db.get(id + ":about");
const k_version = 4;
var f = o ? JSON.parse(o) : o;
let f = o ? JSON.parse(o) : o;
if (!f || f.version != k_version) {
f = {about: {}, sequence: 0, version: k_version};
}
@ -87,7 +177,7 @@ async function getAbout(db, id) {
function(row) {
f.sequence = row.sequence;
if (row.content) {
var about = {};
let about = {};
try {
about = JSON.parse(row.content);
} catch {
@ -97,7 +187,7 @@ async function getAbout(db, id) {
f.about = Object.assign(f.about, about);
}
});
var j = JSON.stringify(f);
let j = JSON.stringify(f);
if (o != j) {
await db.set(id + ":about", j);
}
@ -108,7 +198,7 @@ async function getAbout(db, id) {
async function getSize(db, id) {
let size = 0;
await ssb.sqlAsync(
"SELECT (SUM(LENGTH(content)) + SUM(LENGTH(author)) + SUM(LENGTH(id))) AS size FROM messages WHERE author = ?1",
"SELECT (LENGTH(author) * COUNT(*) + SUM(LENGTH(content)) + SUM(LENGTH(id))) AS size FROM messages WHERE author = ?1",
[id],
function (row) {
size += row.size;
@ -116,6 +206,25 @@ async function getSize(db, id) {
return size;
}
async function getSizes(ids) {
let sizes = {};
await ssb.sqlAsync(
`
SELECT
author,
(SUM(LENGTH(content)) + SUM(LENGTH(author)) + SUM(LENGTH(messages.id))) AS size
FROM messages
JOIN json_each(?) AS ids ON author = ids.value
GROUP BY author
`,
[JSON.stringify(ids)],
function (row) {
sizes[row.author] = row.size;
});
return sizes;
}
function niceSize(bytes) {
let value = bytes;
let unit = 'B';
@ -131,27 +240,28 @@ function niceSize(bytes) {
return Math.round(value * 10) / 10 + ' ' + unit;
}
async function buildTree(db, root, indent, depth) {
var f = await following(db, root);
var result = indent + '[' + f.size + '] ' + '<a target="_top" href="../index/#' + root + '">' + ((await getAbout(db, root)).name || root) + '</a> ' + niceSize(await getSize(db, root)) + '\n';
if (depth > 0) {
for (let next of f) {
result += await buildTree(db, next, indent + ' ', depth - 1);
}
}
return result;
function escape(value) {
return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
}
async function main() {
await app.setDocument('<pre style="color: #fff">building...</pre>');
var db = await database('ssb');
var whoami = await ssb.getIdentities();
var tree = '';
for (let id of whoami) {
await app.setDocument(`<pre style="color: #fff">building... ${id}</pre>`);
tree += await buildTree(db, id, '', 2);
let db = await database('ssb');
let whoami = await ssb.getIdentities();
let tree = '';
await app.setDocument(`<pre style="color: #fff">Enumerating followed users...</pre>`);
let following = await following_deep(whoami, 2, {});
await app.setDocument(`<pre style="color: #fff">Getting names and sizes...</pre>`);
let [about, sizes] = await Promise.all([
fetch_about(db, following, {}),
getSizes(following),
]);
await app.setDocument(`<pre style="color: #fff">Finishing...</pre>`);
following.sort((a, b) => ((sizes[b] ?? 0) - (sizes[a] ?? 0)));
for (let id of following) {
tree += `<li><a href="/~core/ssb/#${id}">${escape(about[id]?.name ?? id)}</a> ${niceSize(sizes[id] ?? 0)}</li>\n`;
}
await app.setDocument('<pre style="color: #fff">FOLLOWING:\n' + tree + '</pre>');
await app.setDocument('<!DOCTYPE html>\n<html>\n<body style="color: #fff"><h1>Following</h1>\n<ul>' + tree + '</ul>\n</body>\n</html>');
}
main();

4
apps/gg.json Normal file
View File

@ -0,0 +1,4 @@
{
"type": "tildefriends-app",
"emoji": "🗺"
}

80
apps/gg/app.js Normal file
View File

@ -0,0 +1,80 @@
import * as tfrpc from '/tfrpc.js';
import * as strava from './strava.js';
let g_database;
let g_shared_database;
tfrpc.register(async function createIdentity() {
return ssb.createIdentity();
});
tfrpc.register(async function appendMessage(id, message) {
print('APPEND', JSON.stringify(message));
return ssb.appendMessageWithIdentity(id, message);
});
tfrpc.register(function url() {
return core.url;
});
tfrpc.register(async function getUser() {
return core.user;
});
tfrpc.register(function getIdentities() {
return ssb.getIdentities();
});
tfrpc.register(async function databaseGet(key) {
return g_database ? g_database.get(key) : undefined;
});
tfrpc.register(async function databaseSet(key, value) {
return g_database ? g_database.set(key, value) : undefined;
});
tfrpc.register(async function databaseRemove(key, value) {
return g_database ? g_database.remove(key, value) : undefined;
});
tfrpc.register(async function sharedDatabaseGet(key) {
return g_shared_database ? g_shared_database.get(key) : undefined;
});
tfrpc.register(async function sharedDatabaseSet(key, value) {
return g_shared_database ? g_shared_database.set(key, value) : undefined;
});
tfrpc.register(async function sharedDatabaseRemove(key, value) {
return g_shared_database ? g_shared_database.remove(key, value) : undefined;
});
tfrpc.register(async function query(sql, args) {
let result = [];
await ssb.sqlAsync(sql, args, function callback(row) {
result.push(row);
});
return result;
});
tfrpc.register(async function store_blob(blob) {
if (typeof(blob) == 'string') {
blob = utf8Encode(blob);
}
if (Array.isArray(blob)) {
blob = Uint8Array.from(blob);
}
return await ssb.blobStore(blob);
});
tfrpc.register(async function get_blob(id) {
return utf8Decode(await ssb.blobGet(id));
});
tfrpc.register(strava.refresh_token);
async function main() {
g_shared_database = await shared_database('state');
if (core.user.credentials?.session?.name) {
g_database = await database('state');
}
let attempt;
if (core.user.credentials?.session?.name) {
let shared_db = await shared_database('state');
attempt = await shared_db.get(core.user.credentials.session.name);
}
app.setDocument(utf8Decode(getFile('index.html')).replace('${data}', JSON.stringify({
attempt: attempt,
state: core.user?.credentials?.session?.name,
})));
}
main();

81
apps/gg/gpx.js Normal file
View File

@ -0,0 +1,81 @@
function xml_parse(xml) {
let result;
let path = [];
let tag_begin;
let text_begin;
for (let i = 0; i < xml.length; i++) {
let c = xml.charAt(i);
if (!tag_begin && c == '<') {
if (i > text_begin && path.length) {
let value = xml.substring(text_begin, i);
if (!/^\s*$/.test(value)) {
path[path.length - 1].value = value;
}
}
tag_begin = i + 1;
} else if (tag_begin && c == '>') {
let tag = xml.substring(tag_begin, i).trim();
if (tag.startsWith('?') && tag.endsWith('?')) {
/* Ignore directives. */
} else if (tag.startsWith('/')) {
path.pop();
} else {
let parts = tag.split(' ');
let attributes = {};
for (let j = 1; j < parts.length; j++) {
let eq = parts[j].indexOf('=');
let value = parts[j].substring(eq + 1);
if (value.startsWith('"') && value.endsWith('"')) {
value = value.substring(1, value.length - 1);
}
attributes[parts[j].substring(0, eq)] = value;
}
let next = {name: parts[0], children: [], attributes: attributes};
if (path.length) {
path[path.length - 1].children.push(next);
} else {
result = next;
}
if (!tag.endsWith('/')) {
path.push(next);
}
}
tag_begin = undefined;
text_begin = i + 1;
}
}
return result;
}
function* xml_each(node, name) {
for (let child of node.children) {
if (child.name == name) {
yield child;
}
}
}
export function gpx_parse(xml) {
let result = {segments: []};
let tree = xml_parse(xml);
if (tree?.name == 'gpx') {
for (let trk of xml_each(tree, 'trk')) {
for (let trkseg of xml_each(trk, 'trkseg')) {
let segment = [];
for (let trkpt of xml_each(trkseg, 'trkpt')) {
segment.push({lat: parseFloat(trkpt.attributes.lat), lon: parseFloat(trkpt.attributes.lon)});
}
result.segments.push(segment);
}
}
}
for (let metadata of xml_each(tree, 'metadata')) {
for (let link of xml_each(metadata, 'link')) {
result.link = link.attributes.href;
}
for (let time of xml_each(metadata, 'time')) {
result.time = time.value;
}
}
return result;
}

21
apps/gg/handler.js Normal file
View File

@ -0,0 +1,21 @@
import * as strava from './strava.js';
async function main() {
print('handler running');
let r = await strava.authorization_code(request.query.code);
print('state =', request.query.state);
print('body = ', r.body);
if (request.query.state && r.body) {
let shared_db = await shared_database('state');
await shared_db.set(request.query.state, utf8Decode(r.body));
}
await respond({
data: r.body,
content_type: 'text/plain',
headers: {
Location: 'https://tildefriends.net/~cory/gg/',
},
status_code: 307,
});
}
main();

14
apps/gg/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html style="width: 100%; height: 100%; margin: 0; padding: 0">
<head>
<script>window.litDisableBundleWarning = true;</script>
<script>
let g_data = ${data};
</script>
<script src="script.js" type="module"></script>
<script src="leaflet.js"></script>
</head>
<body style="color: #fff; display: flex; flex-flow: column; height: 100%; width: 100%; margin: 0; padding: 0">
<gg-app style="width: 100%; height: 100%" id="ggapp"></gg-app>
</body>
</html>

661
apps/gg/leaflet.css Normal file
View File

@ -0,0 +1,661 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
max-width: none !important;
max-height: none !important;
}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
width: auto;
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
svg.leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline-offset: 1px;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.5;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
font-size: 13px;
font-size: 1.08333em;
min-height: 1px;
}
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-top: -1px;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
pointer-events: auto;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
border: none;
text-align: center;
width: 24px;
height: 24px;
font: 16px/24px Tahoma, Verdana, sans-serif;
color: #757575;
text-decoration: none;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
.leaflet-popup-scrolled {
overflow: auto;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}

6
apps/gg/leaflet.js Normal file

File diff suppressed because one or more lines are too long

1
apps/gg/leaflet.js.map Normal file

File diff suppressed because one or more lines are too long

126
apps/gg/lit-all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

158
apps/gg/polyline.js Normal file
View File

@ -0,0 +1,158 @@
/**
* Based off of [the offical Google document](https://developers.google.com/maps/documentation/utilities/polylinealgorithm)
*
* Some parts from [this implementation](http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/PolylineEncoder.js)
* by [Mark McClure](http://facstaff.unca.edu/mcmcclur/)
*
* @module polyline
*/
var polyline = {};
function py2_round(value) {
// Google's polyline algorithm uses the same rounding strategy as Python 2, which is different from JS for negative values
return Math.floor(Math.abs(value) + 0.5) * (value >= 0 ? 1 : -1);
}
function encode(current, previous, factor) {
current = py2_round(current * factor);
previous = py2_round(previous * factor);
var coordinate = (current - previous) * 2;
if (coordinate < 0) {
coordinate = -coordinate - 1
}
var output = '';
while (coordinate >= 0x20) {
output += String.fromCharCode((0x20 | (coordinate & 0x1f)) + 63);
coordinate /= 32;
}
output += String.fromCharCode((coordinate | 0) + 63);
return output;
}
/**
* Decodes to a [latitude, longitude] coordinates array.
*
* This is adapted from the implementation in Project-OSRM.
*
* @param {String} str
* @param {Number} precision
* @returns {Array}
*
* @see https://github.com/Project-OSRM/osrm-frontend/blob/master/WebContent/routing/OSRM.RoutingGeometry.js
*/
polyline.decode = function(str, precision) {
var index = 0,
lat = 0,
lng = 0,
coordinates = [],
shift = 0,
result = 0,
byte = null,
latitude_change,
longitude_change,
factor = Math.pow(10, Number.isInteger(precision) ? precision : 5);
// Coordinates have variable length when encoded, so just keep
// track of whether we've hit the end of the string. In each
// loop iteration, a single coordinate is decoded.
while (index < str.length) {
// Reset shift, result, and byte
byte = null;
shift = 1;
result = 0;
do {
byte = str.charCodeAt(index++) - 63;
result += (byte & 0x1f) * shift;
shift *= 32;
} while (byte >= 0x20);
latitude_change = (result & 1) ? ((-result - 1) / 2) : (result / 2);
shift = 1;
result = 0;
do {
byte = str.charCodeAt(index++) - 63;
result += (byte & 0x1f) * shift;
shift *= 32;
} while (byte >= 0x20);
longitude_change = (result & 1) ? ((-result - 1) / 2) : (result / 2);
lat += latitude_change;
lng += longitude_change;
coordinates.push([lat / factor, lng / factor]);
}
return coordinates;
};
/**
* Encodes the given [latitude, longitude] coordinates array.
*
* @param {Array.<Array.<Number>>} coordinates
* @param {Number} precision
* @returns {String}
*/
polyline.encode = function(coordinates, precision) {
if (!coordinates.length) { return ''; }
var factor = Math.pow(10, Number.isInteger(precision) ? precision : 5),
output = encode(coordinates[0][0], 0, factor) + encode(coordinates[0][1], 0, factor);
for (var i = 1; i < coordinates.length; i++) {
var a = coordinates[i], b = coordinates[i - 1];
output += encode(a[0], b[0], factor);
output += encode(a[1], b[1], factor);
}
return output;
};
function flipped(coords) {
var flipped = [];
for (var i = 0; i < coords.length; i++) {
var coord = coords[i].slice();
flipped.push([coord[1], coord[0]]);
}
return flipped;
}
/**
* Encodes a GeoJSON LineString feature/geometry.
*
* @param {Object} geojson
* @param {Number} precision
* @returns {String}
*/
polyline.fromGeoJSON = function(geojson, precision) {
if (geojson && geojson.type === 'Feature') {
geojson = geojson.geometry;
}
if (!geojson || geojson.type !== 'LineString') {
throw new Error('Input must be a GeoJSON LineString');
}
return polyline.encode(flipped(geojson.coordinates), precision);
};
/**
* Decodes to a GeoJSON LineString geometry.
*
* @param {String} str
* @param {Number} precision
* @returns {Object}
*/
polyline.toGeoJSON = function(str, precision) {
var coords = polyline.decode(str, precision);
return {
type: 'LineString',
coordinates: flipped(coords)
};
};
let polyline_decode = polyline.decode;
export { polyline_decode as decode };

789
apps/gg/script.js Normal file
View File

@ -0,0 +1,789 @@
import {LitElement, html, unsafeHTML, css, guard, until} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import * as polyline from './polyline.js';
import {gpx_parse} from './gpx.js';
const k_client_id = '28276';
const k_redirect_url = 'https://tildefriends.net/~cory/gg/login';
const k_color_snow = [128, 128, 255, 255];
const k_color_ice = [160, 160, 255, 255];
const k_color_water = [0, 0, 255, 255];
const k_color_dirt = [128, 129, 130, 255];
const k_color_pavement = [32, 32, 32, 255];
const k_color_grass = [0, 255, 0, 255];
const k_color_default = [128, 128, 128, 255];
const k_store = {
'🦞': 15,
'🛶': 10,
'🏠': 10,
'⛰': 10,
};
const k_marker_snap = {x: 5, y: 1};
class GgAppElement extends LitElement {
static get properties() {
return {
user: {type: Object},
strava: {type: Object},
activities: {type: Array},
activity: {type: Object},
world: {type: Object},
whoami: {type: String},
status: {type: Object},
tab: {type: String},
url: {type: String},
currency: {type: Number},
to_build: {type: String},
};
}
constructor() {
super();
this.activities = [];
this.activity = {};
this.loaded_activities = [];
this.placed_emojis = [];
this.strava = {};
this.min_lat = Number.MAX_VALUE;
this.min_lon = Number.MAX_VALUE;
this.max_lat = -Number.MAX_VALUE;
this.max_lon = -Number.MAX_VALUE;
this.focus = undefined;
this.status = undefined;
this.tab = 'map';
this.load().catch(function(e) {
console.log('load error', e);
});
this.to_build = '🏠';
}
async load() {
console.log('load');
this.user = await tfrpc.rpc.getUser();
this.url = (await tfrpc.rpc.url()).split('?')[0];
try {
await this.update_credentials();
} catch (e) {
console.log('update_credentials failed', e);
}
try {
await this.update_activities();
} catch (e) {
console.log('update_activities failed', e);
}
await this.acquire_ssb_identity();
if (this.whoami && this.activities?.length) {
await this.sync_activities();
}
await this.get_activities_from_ssb();
}
/* https://gist.github.com/jcouyang/632709f30e12a7879a73e9e132c0d56b?permalink_comment_id=3591045#gistcomment-3591045 */
async promise_all(promises, max_concurrent) {
let index = 0;
let results = [];
async function exec_thread() {
while (index < promises.length) {
const current = index++;
results[current] = await promises[current];
}
}
const threads = [];
for (let thread = 0; thread < max_concurrent; thread++) {
threads.push(exec_thread());
}
await Promise.all(threads);
return results;
}
async get_activities_from_ssb() {
this.status = {text: 'loading activities'};
this.loaded_activities = [];
let rows = await tfrpc.rpc.query(`
SELECT messages.author, json_extract(mention.value, '$.link') AS blob_id
FROM messages_fts('"gg-activity"')
JOIN messages ON messages.rowid = messages_fts.rowid,
json_each(messages.content, '$.mentions') as mention
WHERE json_extract(messages.content, '$.type') = 'gg-activity' AND
json_extract(mention.value, '$.name') = 'activity_data'
ORDER BY messages.timestamp DESC
`, []);
this.status = {text: 'loading activity data'};
let authors = rows.map(x => x.author);
let blobs = await this.promise_all(rows.map(x => tfrpc.rpc.get_blob(x.blob_id)), 8);
this.status = {text: 'processing activity data'};
for (let [index, blob] of blobs.entries()) {
let activity;
try {
activity = JSON.parse(blob);
} catch {
activity = gpx_parse(blob);
}
if (activity) {
activity.author = authors[index];
this.loaded_activities.push(activity);
}
}
this.status = {text: 'calculating balance'};
rows = await tfrpc.rpc.query(`
SELECT count(*) AS currency FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-activity'
`, [this.whoami]);
let currency = rows[0].currency;
rows = await tfrpc.rpc.query(`
SELECT SUM(json_extract(content, '$.cost')) AS cost FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-place'
`, [this.whoami]);
let spent = rows[0].cost;
this.currency = currency - spent;
this.status = {text: 'getting placed emojis'};
rows = await tfrpc.rpc.query(`
SELECT messages.content
FROM messages_fts('"gg-place"')
JOIN messages ON messages.rowid = messages_fts.rowid
WHERE json_extract(messages.content, '$.type') = 'gg-place'
ORDER BY messages.timestamp
`);
for (let row of rows) {
console.log(row.content);
let content = JSON.parse(row.content);
this.placed_emojis.push({
position: content.position,
emoji: content.emoji,
});
}
console.log(this.placed_emojis);
this.status = undefined;
this.update_map();
}
async sync_activities() {
let ids = this.activities.map(x => `https://www.strava.com/activities/${x.id}`);
let missing = await tfrpc.rpc.query(`
WITH my_activities AS (
SELECT json_extract(mention.value, '$.link') AS url
FROM messages, json_each(messages.content, '$.mentions') AS mention
WHERE
author = ? AND
json_extract(messages.content, '$.type') = 'gg-activity' AND
json_extract(mention.value, '$.name') = 'activity_url')
SELECT from_strava.value FROM json_each(?) AS from_strava
LEFT OUTER JOIN my_activities ON from_strava.value = my_activities.url
WHERE my_activities.url IS NULL
`, [this.whoami, JSON.stringify(ids)]);
console.log('missing = ', missing);
for (let [index, row] of missing.entries()) {
this.status = {text: 'syncing from strava', value: index, max: missing.length};
let url = row.value;
let id = url.match(/.*\/(\d+)/)[1];
let response = await fetch(`https://www.strava.com/api/v3/activities/${id}`, {
headers: {
'Authorization': `Bearer ${this.strava.access_token}`,
},
});
let activity = await response.json();
let blob_id = await tfrpc.rpc.store_blob(JSON.stringify(activity));
let message = {
type: 'gg-activity',
mentions: [
{
link: url,
name: 'activity_url',
},
{
link: blob_id,
name: 'activity_data',
}
],
};
await tfrpc.rpc.appendMessage(this.whoami, message);
}
this.status = undefined;
}
async acquire_ssb_identity() {
let user = await tfrpc.rpc.getUser();
if (!user?.credentials?.session?.name) {
return;
}
let ids = await tfrpc.rpc.getIdentities();
let players = ids.length ? (await tfrpc.rpc.query(`
SELECT author FROM messages JOIN json_each(?) ON messages.author = json_each.value
WHERE
json_extract(messages.content, '$.type') = 'gg-player' AND
json_extract(messages.content, '$.active')
ORDER BY timestamp DESC limit 1
`, [JSON.stringify(ids)])).map(row => row.author) : [];
if (!players.length) {
this.whoami = await tfrpc.rpc.createIdentity();
if (this.whoami) {
await tfrpc.rpc.appendMessage(this.whoami, {
type: 'gg-player',
active: true,
});
}
} else {
players.sort();
this.whoami = players[0];
}
}
async update_credentials() {
let name = this.user?.credentials?.session?.name;
if (!name) {
return;
}
let shared = await tfrpc.rpc.sharedDatabaseGet(name);
if (shared) {
await tfrpc.rpc.databaseSet('strava', shared);
await tfrpc.rpc.sharedDatabaseRemove(name);
}
this.strava = JSON.parse(await tfrpc.rpc.databaseGet('strava') || '{}');
if (new Date().valueOf() / 1000 > this.strava.expires_at) {
console.log('this looks expired', new Date().valueOf() / 1000, '>', this.strava.expires_at);
let x = await tfrpc.rpc.refresh_token(this.strava);
if (x) {
this.strava = x;
await tfrpc.rpc.databaseSet('strava', JSON.stringify(x));
} else {
this.strava = null;
}
}
}
async update_activities() {
if (this?.strava?.access_token) {
let response = await fetch('https://www.strava.com/api/v3/athlete/activities', {
headers: {
'Authorization': `Bearer ${this.strava.access_token}`,
},
});
this.activities = await response.json();
this.activities.sort((a, b) => (a.id - b.id));
}
}
color_to_emoji(color) {
const k_map = [
[k_color_snow, '⬜'],
[k_color_ice, '🟦'],
[k_color_water, '🟦'],
[k_color_dirt, '🟫'],
[k_color_pavement, '⬛'],
[k_color_grass, '🟩'],
[k_color_default, '🟧'],
];
for (let m of k_map) {
if (m[0][0] == color[0] &&
m[0][1] == color[1] &&
m[0][2] == color[2] &&
m[0][3] == color[3]) {
return m[1];
}
}
}
activity_bounds(activity) {
let min_lat = Number.MAX_VALUE;
let min_lon = Number.MAX_VALUE;
let max_lat = -Number.MAX_VALUE;
let max_lon = -Number.MAX_VALUE;
if (activity?.map?.polyline) {
for (let pt of polyline.decode(activity.map.polyline)) {
min_lat = Math.min(min_lat, pt[0]);
min_lon = Math.min(min_lon, pt[1]);
max_lat = Math.max(max_lat, pt[0]);
max_lon = Math.max(max_lon, pt[1]);
}
}
if (activity?.segments) {
for (let segment of activity.segments) {
for (let pt of segment) {
min_lat = Math.min(min_lat, pt.lat);
min_lon = Math.min(min_lon, pt.lon);
max_lat = Math.max(max_lat, pt.lat);
max_lon = Math.max(max_lon, pt.lon);
}
}
}
return {
min: {
lat: min_lat,
lng: min_lon,
},
max: {
lat: max_lat,
lng: max_lon,
},
};
}
on_click(event) {
let popup = L.popup()
.setLatLng(event.latlng)
.setContent(`
<div><a target="_top" href="https://www.google.com/maps/search/?api=1&query=${event.latlng.lat},${event.latlng.lng}">${event.latlng.lat}, ${event.latlng.lng}</a></div>
`)
.openOn(this.leaflet);
}
async build() {
if (this.popup) {
this.popup.remove();
}
if (!this.marker) {
return;
}
let latlng = this.marker.getLatLng();
let cost = k_store[this.to_build];
if (cost > this.currency) {
alert('Insufficient funds.');
return;
}
let message = {
type: 'gg-place',
position: {lat: latlng.lat, lng: latlng.lng},
emoji: this.to_build,
cost: cost,
};
let id = await tfrpc.rpc.appendMessage(this.whoami, message);
this.marker.remove();
this.placed_emojis.push({
position: {lat: latlng.lat, lng: latlng.lng},
emoji: this.to_build,
});
this.currency -= cost;
return this.update_map();
}
on_marker_click(event) {
this.popup = L.popup()
.setLatLng(event.latlng)
.setContent(`
${this.to_build} (-${k_store[this.to_build]}) <input type="button" value="Build" onclick="document.getElementById('ggapp').build()"></input>
`)
.openOn(this.leaflet);
}
snap_to_grid(latlng, fudge) {
let position = this.leaflet.options.crs.latLngToPoint(latlng, this.leaflet.getZoom());
position.x = Math.round(position.x / 16) * 16 + (fudge?.x ?? 0);
position.y = Math.round(position.y / 16) * 16 + (fudge?.y ?? 0);
position = this.leaflet.options.crs.pointToLatLng(position, this.leaflet.getZoom());
return position;
}
on_marker_move(event) {
if (!this.no_snap && this.marker) {
this.no_snap = true;
this.marker.setLatLng(this.snap_to_grid(this.marker.getLatLng(), k_marker_snap));
this.no_snap = false;
}
}
on_mouse_down(event) {
if (this.marker) {
this.marker.remove();
this.marker = undefined;
}
if (this.to_build) {
this.marker = L.marker(this.snap_to_grid(event.latlng, k_marker_snap), {icon: L.divIcon({className: 'build-icon'}), draggable: true}).addTo(this.leaflet);
this.marker.on({click: this.on_marker_click.bind(this)});
this.marker.on({drag: this.on_marker_move.bind(this)});
}
}
async update_map() {
let map = this.shadowRoot.getElementById('map');
if (!map || !this.loaded_activities.length) {
this.leaflet = undefined;
this.grid_layer = undefined;
return;
}
if (!this.leaflet) {
this.leaflet = L.map(map, {attributionControl: false, maxZoom: 16, bounceAtZoomLimits: false});
this.leaflet.on({contextmenu: this.on_click.bind(this)});
this.leaflet.on({click: this.on_mouse_down.bind(this)});
}
let self = this;
let grid_layer = L.GridLayer.extend({
createTile: function(coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var size = this.getTileSize();
tile.width = size.x;
tile.height = size.y;
var context = tile.getContext('2d');
context.font = '10pt sans';
let bounds = this._tileCoordsToBounds(coords);
let degrees = 360.0 / (2 ** coords.z);
let ul = bounds.getNorthWest();
let lr = bounds.getSouthEast();
let mini = document.createElement('canvas');
mini.width = Math.floor(size.x / 16.0);
mini.height = Math.floor(size.y / 16.0);
let mini_context = mini.getContext('2d');
let image_data = context.getImageData(0, 0, mini.width, mini.height);
for (let activity of self.loaded_activities) {
self.draw_activity_to_tile(image_data, mini.width, mini.height, ul, lr, activity);
}
context.textAlign = 'left';
context.textBaseline = 'top';
for (let x = 0; x < mini.width; x++) {
for (let y = 0; y < mini.height; y++) {
let start = (y * mini.width + x) * 4;
let pixel = self.color_to_emoji(image_data.data.slice(start, start + 4));
if (pixel) {
context.fillText(pixel, x * size.x / mini.width, y * size.y / mini.height);
}
}
}
for (let placed of self.placed_emojis) {
let position = self.leaflet.options.crs.latLngToPoint(self.snap_to_grid(placed.position), coords.z);
let tile_x = Math.floor(position.x / size.x);
let tile_y = Math.floor(position.y / size.y);
position.x = position.x - tile_x * size.x;
position.y = position.y - tile_y * size.y;
if (tile_x == coords.x && tile_y == coords.y) {
context.fillText(placed.emoji, position.x, position.y);
}
}
return tile;
}
});
if (this.grid_layer) {
this.grid_layer.redraw();
} else {
this.grid_layer = new grid_layer();
this.grid_layer.addTo(this.leaflet);
}
for (let activity of this.loaded_activities) {
let bounds = this.activity_bounds(activity);
this.min_lat = Math.min(this.min_lat, bounds.min.lat);
this.min_lon = Math.min(this.min_lon, bounds.min.lng);
this.max_lat = Math.max(this.max_lat, bounds.max.lat);
this.max_lon = Math.max(this.max_lon, bounds.max.lng);
}
if (this.focus) {
this.leaflet.fitBounds([
this.focus.min,
this.focus.max,
]);
this.focus = undefined;
} else {
this.leaflet.fitBounds([
[this.min_lat, this.min_lon],
[this.max_lat, this.max_lon],
]);
}
}
activity_to_color(activity) {
let color = [0, 0, 0, 255];
switch (activity.sport_type) {
/* Implies snow. */
case 'AlpineSki':
case 'BackcountrySki':
case 'NordicSki':
case 'Snowshoe':
case 'Snowboard':
color = k_color_snow;
break;
/* Implies ice. */
case 'IceSkate':
case 'InlineSkate':
color = k_color_ice;
break;
/* Implies water. */
case 'Canoeing':
case 'Kayaking':
case 'Kitesurf':
case 'Rowing':
case 'Sail':
case 'StandUpPaddling':
case 'Surfing':
case 'Swim':
case 'Windsurf':
color = k_color_water;
break;
/* Implies dirt. */
case 'EMountainBikeRide':
case 'Hike':
case 'MountainBikeRide':
case 'RockClimbing':
case 'TrailRun':
color = k_color_dirt;
break;
/* Implies pavement. */
case 'EBikeRide':
case 'GravelRide':
case 'Handcycle':
case 'Ride':
case 'RollerSki':
case 'Run':
case 'Skateboard':
case 'Badminton':
case 'Tennis':
case 'Velomobile':
case 'Walk':
case 'Wheelchair':
color = k_color_pavement;
break;
/* Grass, maybe? */
case 'Golf':
case 'Soccer':
case 'Squash':
color = k_color_grass;
break;
// Crossfit,
// Elliptical
// HighIntensityIntervalTraining
// Pickleball
// Pilates
// Racquetball
// StairStepper
// TableTennis,
// VirtualRide
// VirtualRow
// VirtualRun
// WeightTraining
// Workout
// Yoga
default:
color = k_color_default;
}
return color;
}
line(image_data, x0, y0, x1, y1, value) {
/* <3 https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
let dx = Math.abs(x1 - x0);
let sx = x0 < x1 ? 1 : -1;
let dy = -Math.abs(y1 - y0);
let sy = y0 < y1 ? 1 : -1;
let error = dx + dy;
while (true) {
if (x0 >= 0 && y0 >= 0 && x0 < image_data.width && y0 < image_data.height) {
let base = (y0 * image_data.width + x0) * 4;
image_data.data[base + 0] = value[0];
image_data.data[base + 1] = value[1];
image_data.data[base + 2] = value[2];
image_data.data[base + 3] = value[3];
}
if (x0 == x1 && y0 == y1) {
break;
}
let e2 = 2 * error;
if (e2 >= dy) {
if (x0 == x1) {
break;
}
error += dy;
x0 = Math.round(x0 + sx);
}
if (e2 <= dx) {
if (y0 == y1) {
break;
}
error += dx;
y0 = Math.round(y0 + sy);
}
}
}
draw_activity_to_tile(image_data, width, height, ul, lr, activity) {
let color = this.activity_to_color(activity);
if (activity?.map?.polyline) {
let last;
for (let pt of polyline.decode(activity.map.polyline)) {
let px = [
Math.floor(width * (pt[1] - ul.lng) / (lr.lng - ul.lng)),
Math.floor(height * (pt[0] - ul.lat) / (lr.lat - ul.lat)),
];
if (last) {
this.line(image_data, last[0], last[1], px[0], px[1], color);
}
last = px;
}
}
if (activity?.segments) {
for (let segment of activity.segments) {
let last;
for (let pt of segment) {
let px = [
Math.floor(width * (pt.lon - ul.lng) / (lr.lng - ul.lng)),
Math.floor(height * (pt.lat - ul.lat) / (lr.lat - ul.lat)),
];
if (last) {
this.line(image_data, last[0], last[1], px[0], px[1], color);
}
last = px;
}
}
}
}
async on_upload(event) {
try {
let file = event.srcElement.files[0];
let xml = await file.text();
let gpx = gpx_parse(xml);
let blob_id = await tfrpc.rpc.store_blob(xml);
console.log('blob_id = ', blob_id);
console.log(gpx);
let message = {
type: 'gg-activity',
mentions: [
{
link: `https://${gpx.link}/activity/${gpx.time}`,
name: 'activity_url',
},
{
link: blob_id,
name: 'activity_data',
}
],
};
console.log('id =', this.whoami, 'message = ', message);
let id = await tfrpc.rpc.appendMessage(this.whoami, message);
console.log('appended message', id);
alert('Activity uploaded.');
await this.get_activities_from_ssb();
} catch (e) {
alert(`Error: ${JSON.stringify(e, null, 2)}`);
}
}
upload() {
let input = document.createElement('input');
input.type = 'file';
input.onchange = (event) => this.on_upload(event);
input.click();
}
updated() {
this.update_map();
}
focus_map(activity) {
let bounds = this.activity_bounds(activity);
if (bounds.min.lat < bounds.max.lat &&
bounds.min.lng < bounds.max.lng) {
this.tab = 'map';
this.focus = bounds;
}
}
render_news() {
return html`
<ul>
${this.loaded_activities.map(x => html`
<li style="cursor: pointer" @click=${() => this.focus_map(x)}>${x.author} ${x.name ?? x.time}</li>
`)}
</ul>
`;
}
render_store_item(item) {
let [emoji, cost] = item;
return html`
<div>
<input type="button" value="${emoji}" @click=${() => this.to_build = emoji}></input> ${cost} ${emoji == this.to_build ? '<-- Will be built next' : undefined}
</div>
`;
}
render_store() {
return html`
<h2>Store</h2>
<div><b>Your balance:</b> ${this.currency}</div>
${Object.entries(k_store).map(this.render_store_item.bind(this))}
`;
}
render() {
let header;
if (!this.user?.credentials?.session?.name) {
header = html`<div style="flex: 1 0">Please <a target="_top" href="/login?return=${this.url}">login</a> to Tilde Friends, first.</div>`;
} else if (!this.strava?.access_token) {
let strava_url = `https://www.strava.com/oauth/authorize?client_id=${k_client_id}&redirect_uri=${k_redirect_url}&response_type=code&approval_prompt=auto&scope=activity%3Aread&state=${g_data.state}`;
header = html`
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
<div style="flex: 1 1">Please <a target="_top" href=${strava_url}>login</a> to Strava.</div>
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.whoami}</span>
<input type="button" value="📁" @click=${this.upload}></input>
</div>
`;
} else {
header = html`
<div>
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
<h1>Welcome, ${this.user.credentials.session.name}</h1>
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.whoami}</span>
<input type="button" value="📁" @click=${this.upload}></input>
</div>
<h3 ?hidden=${!this.status?.text}>${this.status?.text} <progress ?hidden=${!this.status?.max} value=${this.status?.value} max=${this.status?.max}>${this.status?.value}</progress></h3>
</div>
`;
}
let navigation = html`
<style>
#navigation input[type="button"] {
min-width: 3em;
min-height: 3em;
flex: 1 0;
font-size: large;
}
</style>
<div id="navigation" style="display: flex; flex-direction: row">
<input type="button" id="button_map" @click=${() => this.tab = 'map'} value="🗺Map"></input>
<input type="button" id="button_news" @click=${() => this.tab = 'news'} value="🏃News"></input>
<input type="button" id="button_friends" @click=${() => this.tab = 'friends'} value="👫Friends"></input>
<input type="button" id="button_store" @click=${() => this.tab = 'store'} value="🏗Store"></input>
</div>
`;
let content;
switch (this.tab) {
case 'map':
content = html`<div id="map" style="width: 100%; height: 100%"></div>`;
break;
case 'news':
content = this.render_news();
break;
case 'friends':
content = html`<div>Friends</div>`;
break;
case 'store':
content = this.render_store();
break;
}
return html`
<style>
.build-icon::before {
content: '📍';
border: 2px solid red;
}
</style>
<link rel="stylesheet" href="leaflet.css"/>
<div style="width: 100%; height: 100%; display: flex; flex-direction: column">
${header}
<div style="flex: 1 0; overflow: scroll">${content}</div>
${navigation}
</div>
`;
}
}
customElements.define('gg-app', GgAppElement);

20
apps/gg/strava.js Normal file
View File

@ -0,0 +1,20 @@
const k_client_id = '28276';
const k_client_secret = '3123f1f5afe132d9731111066d1d17bdb22ef27e';
const k_access_token = 'f753e77764c26252bd2d80e7c5cc17ace51a8864';
const k_refresh_token = 'f58d8e1b5a3ec3bf96e681589d5014f9a294f5a4';
const k_redirect_url = 'https://tildefriends.net/~cory/gg/login';
export async function refresh_token(token) {
let r = await fetch('https://www.strava.com/api/v3/oauth/token', {
method: 'POST',
body: `client_id=${k_client_id}&client_secret=${k_client_secret}&refresh_token=${token.refresh_token}&grant_type=refresh_token`,
});
return r?.body ? JSON.parse(utf8Decode(r.body)) : undefined;
}
export async function authorization_code(code) {
return await fetch('https://www.strava.com/api/v3/oauth/token', {
method: 'POST',
body: `client_id=${k_client_id}&client_secret=${k_client_secret}&code=${code}&grant_type=authorization_code`,
});
}

4
apps/issues.json Normal file
View File

@ -0,0 +1,4 @@
{
"type": "tildefriends-app",
"emoji": "🦟"
}

105
apps/issues/app.js Normal file
View File

@ -0,0 +1,105 @@
import * as tfrpc from '/tfrpc.js';
let g_database;
let g_hash;
tfrpc.register(async function localStorageGet(key) {
return app.localStorageGet(key);
});
tfrpc.register(async function localStorageSet(key, value) {
return app.localStorageSet(key, value);
});
tfrpc.register(async function databaseGet(key) {
return g_database ? g_database.get(key) : undefined;
});
tfrpc.register(async function databaseSet(key, value) {
return g_database ? g_database.set(key, value) : undefined;
});
tfrpc.register(async function createIdentity() {
return ssb.createIdentity();
});
tfrpc.register(async function getIdentities() {
return ssb.getIdentities();
});
tfrpc.register(async function getAllIdentities() {
return ssb.getAllIdentities();
});
tfrpc.register(async function getBroadcasts() {
return ssb.getBroadcasts();
});
tfrpc.register(async function getConnections() {
return ssb.connections();
});
tfrpc.register(async function getStoredConnections() {
return ssb.storedConnections();
});
tfrpc.register(async function forgetStoredConnection(connection) {
return ssb.forgetStoredConnection(connection);
});
tfrpc.register(async function createTunnel(portal, target) {
return ssb.createTunnel(portal, target);
});
tfrpc.register(async function connect(token) {
await ssb.connect(token);
});
tfrpc.register(async function closeConnection(id) {
await ssb.closeConnection(id);
});
tfrpc.register(async function query(sql, args) {
let result = [];
await ssb.sqlAsync(sql, args, function callback(row) {
result.push(row);
});
return result;
});
tfrpc.register(async function appendMessage(id, message) {
return ssb.appendMessageWithIdentity(id, message);
});
core.register('message', async function message_handler(message) {
if (message.event == 'hashChange') {
g_hash = message.hash;
await tfrpc.rpc.hashChanged(message.hash);
}
});
tfrpc.register(function getHash(id, message) {
return g_hash;
});
tfrpc.register(function setHash(hash) {
return app.setHash(hash);
});
ssb.addEventListener('message', async function(id) {
await tfrpc.rpc.notifyNewMessage(id);
});
tfrpc.register(async function store_blob(blob) {
if (Array.isArray(blob)) {
blob = Uint8Array.from(blob);
}
return await ssb.blobStore(blob);
});
tfrpc.register(async function get_blob(id) {
return utf8Decode(await ssb.blobGet(id));
});
tfrpc.register(async function store_message(message) {
return await ssb.storeMessage(message);
});
tfrpc.register(function apps() {
return core.apps();
});
tfrpc.register(async function try_decrypt(id, content) {
return await ssb.privateMessageDecrypt(id, content);
});
ssb.addEventListener('broadcasts', async function() {
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
});
core.register('onConnectionsChanged', async function() {
await tfrpc.rpc.set('connections', await ssb.connections());
});
async function main() {
if (typeof(database) !== 'undefined') {
g_database = await database('ssb');
}
await app.setDocument(utf8Decode(await getFile('index.html')));
}
main();

View File

@ -0,0 +1,91 @@
function textNode(text) {
const node = new commonmark.Node("text", undefined);
node.literal = text;
return node;
}
function linkNode(text, url) {
const urlNode = new commonmark.Node("link", undefined);
urlNode.destination = url;
urlNode.appendChild(textNode(text));
return urlNode;
}
function splitMatches(text, regexp) {
// Regexp must be sticky.
regexp = new RegExp(regexp, "gm");
let i = 0;
const result = [];
let match = regexp.exec(text);
while (match) {
const matchText = match[0];
if (match.index > i) {
result.push([text.substring(i, match.index), false]);
}
result.push([matchText, true]);
i = match.index + matchText.length;
match = regexp.exec(text);
}
if (i < text.length) {
result.push([text.substring(i, text.length), false]);
}
return result;
}
const urlRegexp = new RegExp("https?://[^ ]+[^ .,]");
function splitURLs(textNodes) {
const text = textNodes.map(n => n.literal).join("");
const parts = splitMatches(text, urlRegexp);
return parts.map(part => {
if (part[1]) {
return linkNode(part[0], part[0]);
} else {
return textNode(part[0]);
}
});
}
export function transform(parsed) {
const walker = parsed.walker();
let event;
let nodes = [];
while ((event = walker.next())) {
const node = event.node;
if (event.entering && node.type === "text") {
nodes.push(node);
} else {
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
nodes = [];
}
}
}
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
}
return parsed;
}

1
apps/issues/commonmark.min.js vendored Normal file

File diff suppressed because one or more lines are too long

14
apps/issues/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html style="color: #fff">
<head>
<title>Tilde Friends</title>
<base target="_top">
</head>
<body>
<tf-issues-app/>
<script>window.litDisableBundleWarning = true;</script>
<script src="commonmark.min.js"></script>
<script src="commonmark-linkify.js" type="module"></script>
<script src="script.js" type="module"></script>
</body>
</html>

120
apps/issues/lit-all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

260
apps/issues/script.js Normal file
View File

@ -0,0 +1,260 @@
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import * as tfutils from './tf-utils.js';
const k_project = '%Hr+4xEVtjplidSKBlRWi4Aw/0Tfw7B+1OR9BzlDKmOI=.sha256';
class TfIdPickerElement extends LitElement {
static get properties() {
return {
ids: {type: Array},
selected: {type: String},
};
}
constructor() {
super();
this.load();
}
async load() {
this.selected = await tfrpc.rpc.localStorageGet('whoami');
this.ids = (await tfrpc.rpc.getIdentities()) || [];
}
changed(event) {
this.selected = event.srcElement.value;
tfrpc.rpc.localStorageSet('whoami', this.selected);
}
render() {
if (this.ids) {
return html`
<select @change=${this.changed} style="max-width: 100%">
${(this.ids).map(id => html`<option ?selected=${id == this.selected} value=${id}>${id}</option>`)}
</select>
`;
} else {
return html`<div>Loading...</div>`;
}
}
}
customElements.define('tf-id-picker', TfIdPickerElement);
class TfComposeElement extends LitElement {
static get properties() {
return {
value: {type: String},
};
}
input() {
let input = this.renderRoot.getElementById('input');
let preview = this.renderRoot.getElementById('preview');
if (input && preview) {
preview.innerHTML = tfutils.markdown(input.value);
}
}
submit() {
this.dispatchEvent(new CustomEvent('tf-submit', {
bubbles: true,
composed: true,
detail: {
value: this.renderRoot.getElementById('input').value,
},
}));
this.renderRoot.getElementById('input').value = '';
this.input();
}
render() {
return html`
<div style="display: flex; flex-direction: row">
<textarea id="input" @input=${this.input} style="flex: 1 1">${this.value}</textarea>
<div id="preview" style="flex: 1 1"></div>
</div>
<input type="submit" value="Submit" @click=${this.submit}></input>
`;
}
}
customElements.define('tf-compose', TfComposeElement);
class TfIssuesAppElement extends LitElement {
static get properties() {
return {
issues: {type: Array},
selected: {type: Object},
};
}
constructor() {
super();
this.issues = [];
this.load();
}
async load() {
let issues = {};
let messages = await tfrpc.rpc.query(`
WITH issues AS (SELECT messages.* FROM messages_refs JOIN messages ON
messages.id = messages_refs.message
WHERE messages_refs.ref = ? AND json_extract(messages.content, '$.type') = 'issue'),
edits AS (SELECT messages.* FROM issues JOIN messages_refs ON
issues.id = messages_refs.ref JOIN messages ON
messages.id = messages_refs.message
WHERE json_extract(messages.content, '$.type') IN ('issue-edit', 'post'))
SELECT * FROM issues
UNION
SELECT * FROM edits ORDER BY timestamp
`, [k_project]);
for (let message of messages) {
let content = JSON.parse(message.content);
switch (content.type) {
case 'issue':
issues[message.id] = {
id: message.id,
author: message.author,
text: content.text,
updates: [],
created: message.timestamp,
open: true,
};
break;
case 'issue-edit':
case 'post':
for (let issue of (content.issues || [])) {
if (issues[issue.link]) {
if (issue.open !== undefined) {
issues[issue.link].open = issue.open;
message.open = issue.open;
}
issues[issue.link].updates.push(message);
issues[issue.link].updated = message.timestamp;
}
}
break;
}
}
this.issues = Object.values(issues).sort((x, y) => y.created - x.created);
if (this.selected) {
for (let issue of this.issues) {
if (issue.id == this.selected.id) {
this.selected = issue;
}
}
}
}
render_issue_table_row(issue) {
return html`
<tr>
<td>${issue.open ? 'open' : 'closed'}</td>
<td style="max-width: 8em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">${issue.author}</td>
<td style="max-width: 40em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer" @click=${() => this.selected = issue}>
${issue.text.split('\n')?.[0]}
</td>
<td>${new Date(issue.updated ?? issue.created).toLocaleDateString()}</td>
</tr>
`;
}
render_update(update) {
let content = JSON.parse(update.content);
let message;
if (content.text) {
message = unsafeHTML(tfutils.markdown(content.text));
}
return html`
<div style="border-left: 2px solid #fff; padding-left: 8px">
<div>${new Date(update.timestamp).toLocaleString()}</div>
<div>${update.author}</div>
<div>${message}</div>
<div>${update.open !== undefined ? (update.open ? 'issue opened' : 'issue closed') : undefined}</div>
</div>
`;
}
async set_open(id, open) {
if (confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`)) {
let whoami = this.shadowRoot.getElementById('picker').selected;
await tfrpc.rpc.appendMessage(whoami, {
type: 'issue-edit',
issues: [
{
link: id,
open: open,
},
],
});
await this.load();
}
}
async create_issue(event) {
let whoami = this.shadowRoot.getElementById('picker').selected;
await tfrpc.rpc.appendMessage(whoami, {
type: 'issue',
project: k_project,
text: event.detail.value,
});
await this.load();
}
async reply_to_issue(event) {
let whoami = this.shadowRoot.getElementById('picker').selected;
await tfrpc.rpc.appendMessage(whoami, {
type: 'post',
text: event.detail.value,
root: this.selected.id,
branch: this.selected.updates.length ? this.selected.updates[this.selected.updates.length - 1].id : this.selected.id,
issues: [
{
link: this.selected.id,
},
],
});
await this.load();
}
render() {
let header = html`
<h1>Tilde Friends Issues</h1>
<tf-id-picker id="picker"></tf-id-picker>
`;
if (this.selected) {
return html`
${header}
<div>
<input type="button" value="Back" @click=${() => this.selected = undefined}></input>
${this.selected.open ?
html`<input type="button" value="Close Issue" @click=${() => this.set_open(this.selected.id, false)}></input>` :
html`<input type="button" value="Reopen Issue" @click=${() => this.set_open(this.selected.id, true)}></input>`}
</div>
<div>${new Date(this.selected.created).toLocaleString()}</div>
<div>${this.selected.author}</div>
<div>${this.selected.id}</div>
<div>${unsafeHTML(tfutils.markdown(this.selected.text))}</div>
${this.selected.updates.map(x => this.render_update(x))}
<tf-compose @tf-submit=${this.reply_to_issue}></tf-compose>
`;
} else {
return html`
${header}
<h2>New Issue</h2>
<tf-compose @tf-submit=${this.create_issue}></tf-compose>
<table>
<tr>
<th>Status</th>
<th>Author</th>
<th>Title</th>
<th>Date</th>
</tr>
${this.issues.map(x => this.render_issue_table_row(x))}
</table>
`;
}
}
}
customElements.define('tf-issues-app', TfIssuesAppElement);

91
apps/issues/tf-utils.js Normal file
View File

@ -0,0 +1,91 @@
import * as linkify from './commonmark-linkify.js';
function image(node, entering) {
if (node.firstChild?.type === 'text' &&
node.firstChild.literal.startsWith('video:')) {
if (entering) {
this.lit('<video style="max-width: 100%; max-height: 480px" title="' + this.esc(node.firstChild?.literal) + '" controls>');
this.lit('<source src="' + this.esc(node.destination) + '"></source>');
this.disableTags += 1;
} else {
this.disableTags -= 1;
this.lit('</video>');
}
} else if (node.firstChild?.type === 'text' &&
node.firstChild.literal.startsWith('audio:')) {
if (entering) {
this.lit('<audio style="height: 32px; max-width: 100%" title="' + this.esc(node.firstChild?.literal) + '" controls>');
this.lit('<source src="' + this.esc(node.destination) + '"></source>');
this.disableTags += 1;
} else {
this.disableTags -= 1;
this.lit('</audio>');
}
} else {
if (entering) {
if (this.disableTags === 0) {
this.lit('<div class="img_caption">' + this.esc(node.firstChild?.literal || node.destination) + '</div>');
if (this.options.safe && potentiallyUnsafe(node.destination)) {
this.lit('<img src="" alt="');
} else {
this.lit('<img src="' + this.esc(node.destination) + '" alt="');
}
}
this.disableTags += 1;
} else {
this.disableTags -= 1;
if (this.disableTags === 0) {
if (node.title) {
this.lit('" title="' + this.esc(node.title));
}
this.lit('" />');
}
}
}
}
export function markdown(md) {
var reader = new commonmark.Parser({safe: true});
var writer = new commonmark.HtmlRenderer();
writer.image = image;
var parsed = reader.parse(md || '');
parsed = linkify.transform(parsed);
var walker = parsed.walker();
var event, node;
while ((event = walker.next())) {
node = event.node;
if (event.entering) {
if (node.type == 'link') {
if (node.destination.startsWith('@') &&
node.destination.endsWith('.ed25519')) {
node.destination = '#' + node.destination;
} else if (node.destination.startsWith('%') &&
node.destination.endsWith('.sha256')) {
node.destination = '#' + node.destination;
} else if (node.destination.startsWith('&') &&
node.destination.endsWith('.sha256')) {
node.destination = '/' + node.destination + '/view';
}
} else if (node.type == 'image') {
if (node.destination.startsWith('&')) {
node.destination = '/' + node.destination + '/view';
}
}
}
}
return writer.render(parsed);
}
export function human_readable_size(bytes) {
let v = bytes;
let u = 'B';
for (let unit of ['kB', 'MB', 'GB']) {
if (v > 1024) {
v /= 1024;
u = unit;
} else {
break;
}
}
return `${Math.round(v * 10) / 10} ${u}`;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -118,17 +118,23 @@ class TfSneakerAppElement extends LitElement {
zip.file(`message/classic/${this.sanitize(id)}.ndjson`, all_messages);
let blobs = await tfrpc.rpc.query(
`SELECT blobs.id
`SELECT messages_refs.ref AS id
FROM messages
JOIN messages_refs ON messages.id = messages_refs.message
JOIN blobs ON messages_refs.ref = blobs.id
WHERE messages.author = ?`,
WHERE messages.author = ? AND messages_refs.ref LIKE '&%.sha256'`,
[id]);
let blobs_done = 0;
for (let row of blobs) {
this.progress = {name: 'blobs', value: blobs_done, max: blobs.length};
let blob = await tfrpc.rpc.get_blob(row.id);
zip.file(`blob/classic/${this.sanitize(row.id)}${this.guess_ext(blob)}`, new Uint8Array(blob));
let blob;
try {
blob = await tfrpc.rpc.get_blob(row.id);
} catch (e) {
console.log(`Failed to get ${row.id}: ${e.message}`);
}
if (blob) {
zip.file(`blob/classic/${this.sanitize(row.id)}${this.guess_ext(blob)}`, new Uint8Array(blob));
}
blobs_done++;
}

View File

@ -88,6 +88,9 @@ tfrpc.register(function apps() {
tfrpc.register(async function try_decrypt(id, content) {
return await ssb.privateMessageDecrypt(id, content);
});
tfrpc.register(async function encrypt(id, recipients, content) {
return await ssb.privateMessageEncrypt(id, recipients, content);
});
ssb.addEventListener('broadcasts', async function() {
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
});

View File

@ -64,7 +64,7 @@ export function picker(callback, anchor) {
while (list.firstChild) {
list.removeChild(list.firstChild);
}
let search = input.value;
let search = input.value.toLowerCase();
let any_at_all = false;
for (let row of Object.entries(json)) {
let header = document.createElement('div');
@ -74,7 +74,7 @@ export function picker(callback, anchor) {
for (let entry of Object.entries(row[1])) {
if (search &&
search.length &&
entry[0].indexOf(search) == -1) {
entry[0].toLowerCase().indexOf(search) == -1) {
continue;
}
let emoji = document.createElement('span');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,4 +13,5 @@ import * as tf_tab_news from './tf-tab-news.js';
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
import * as tf_tab_search from './tf-tab-search.js';
import * as tf_tab_connections from './tf-tab-connections.js';
import * as tf_tab_query from './tf-tab-query.js';
import * as tf_tag from './tf-tag.js';

View File

@ -68,6 +68,8 @@ class TfElement extends LitElement {
this.tab = 'connections';
} else if (this.hash === '#mentions') {
this.tab = 'mentions';
} else if (this.hash.startsWith('#sql=')) {
this.tab = 'query';
} else {
this.tab = 'news';
}
@ -245,13 +247,16 @@ class TfElement extends LitElement {
if (confirm("Are you sure you want to create a new identity?")) {
await tfrpc.rpc.createIdentity();
this.ids = (await tfrpc.rpc.getIdentities()) || [];
if (this.ids && !this.whoami) {
this.whoami = this.ids[0];
}
}
}
render_id_picker() {
return html`
<tf-id-picker id="picker" selected=${this.whoami} .ids=${this.ids} @change=${this._handle_whoami_changed}></tf-id-picker>
<button @click=${this.create_identity}>Create Identity</button>
<button @click=${this.create_identity} id="create_identity">Create Identity</button>
`;
}
@ -293,7 +298,7 @@ class TfElement extends LitElement {
let users = this.users;
if (this.tab === 'news') {
return html`
<tf-tab-news .following=${this.following} whoami=${this.whoami} .users=${this.users} hash=${this.hash} .unread=${this.unread} @refresh=${() => this.unread = []}></tf-tab-news>
<tf-tab-news id="tf-tab-news" .following=${this.following} whoami=${this.whoami} .users=${this.users} hash=${this.hash} .unread=${this.unread} @refresh=${() => this.unread = []}></tf-tab-news>
`;
} else if (this.tab === 'connections') {
return html`
@ -307,6 +312,10 @@ class TfElement extends LitElement {
return html`
<tf-tab-search .following=${this.following} whoami=${this.whoami} .users=${this.users} query=${this.hash?.startsWith('#q=') ? decodeURIComponent(this.hash.substring(3)) : null}></tf-tab-search>
`;
} else if (this.tab === 'query') {
return html`
<tf-tab-query .following=${this.following} whoami=${this.whoami} .users=${this.users} query=${this.hash?.startsWith('#sql=') ? decodeURIComponent(this.hash.substring(5)) : null}></tf-tab-query>
`;
}
}
@ -318,6 +327,8 @@ class TfElement extends LitElement {
await tfrpc.rpc.setHash('#connections');
} else if (tab === 'mentions') {
await tfrpc.rpc.setHash('#mentions');
} else if (tab === 'query') {
await tfrpc.rpc.setHash('#sql=');
}
}
@ -325,7 +336,6 @@ class TfElement extends LitElement {
let self = this;
if (!this.loading && this.whoami && this.loaded !== this.whoami) {
console.log(`starting loading ${this.whoami} ${this.loaded}`);
this.loading = true;
this.load().finally(function() {
self.loading = false;
@ -338,6 +348,7 @@ class TfElement extends LitElement {
<input type="button" class="tab" value="Connections" ?disabled=${self.tab == 'connections'} @click=${() => self.set_tab('connections')}></input>
<input type="button" class="tab" value="Mentions" ?disabled=${self.tab == 'mentions'} @click=${() => self.set_tab('mentions')}></input>
<input type="button" class="tab" value="Search" ?disabled=${self.tab == 'search'} @click=${() => self.set_tab('search')}></input>
<input type="button" class="tab" value="Query" ?disabled=${self.tab == 'query'} @click=${() => self.set_tab('query')}></input>
</div>
`;
let contents =
@ -355,4 +366,4 @@ class TfElement extends LitElement {
}
}
customElements.define('tf-app', TfElement);
customElements.define('tf-app', TfElement);

View File

@ -176,7 +176,7 @@ class TfComposeElement extends LitElement {
}
}
submit() {
async submit() {
let self = this;
let draft = this.get_draft();
let edit = this.renderRoot.getElementById('edit');
@ -195,14 +195,25 @@ class TfComposeElement extends LitElement {
message.contentWarning = draft.content_warning;
}
console.log('Would post:', message);
tfrpc.rpc.appendMessage(this.whoami, message).then(function() {
edit.value = '';
self.change();
self.notify(undefined);
self.requestUpdate();
}).catch(function(error) {
if (draft.encrypt_to) {
let to = new Set(draft.encrypt_to);
to.add(this.whoami);
to = [...to];
message.recps = to;
console.log('message is now', message);
message = await tfrpc.rpc.encrypt(this.whoami, to, JSON.stringify(message));
console.log('encrypted as', message);
}
try {
await tfrpc.rpc.appendMessage(this.whoami, message).then(function() {
edit.value = '';
self.change();
self.notify(undefined);
self.requestUpdate();
});
} catch (error) {
alert(error.message);
});
}
}
discard() {
@ -226,12 +237,47 @@ class TfComposeElement extends LitElement {
input.click();
}
async autocomplete(text, callback) {
this.last_autocomplete = text;
let results = [];
try {
let rows = await tfrpc.rpc.query(`
SELECT messages.content FROM messages_fts(?)
JOIN messages ON messages.rowid = messages_fts.rowid
WHERE messages.content LIKE ?
ORDER BY timestamp DESC LIMIT 10
`, ['"' + text.replace('"', '""') + '"', `%![%${text}%](%)%`]);
for (let row of rows) {
for (let match of row.content.matchAll(/!\[([^\]]*)\]\((&.*?)\)/g)) {
if (match[1].toLowerCase().indexOf(text.toLowerCase()) != -1) {
results.push({key: match[1], value: match[2]});
}
}
}
} finally {
if (this.last_autocomplete === text) {
callback(results);
}
}
}
firstUpdated() {
let tribute = new Tribute({
values: Object.entries(this.users).map(x => ({key: x[1].name, value: x[0]})),
selectTemplate: function(item) {
return `[@${item.original.key}](${item.original.value})`;
},
collection: [
{
values: Object.entries(this.users).map(x => ({key: x[1].name, value: x[0]})),
selectTemplate: function(item) {
return `[@${item.original.key}](${item.original.value})`;
},
},
{
trigger: '&',
values: this.autocomplete,
selectTemplate: function(item) {
return `![${item.original.key}](${item.original.value})`;
},
},
],
});
tribute.attach(this.renderRoot.getElementById('edit'));
}
@ -244,6 +290,16 @@ class TfComposeElement extends LitElement {
preview.innerHTML = this.process_text(edit.value);
this.last_updated_text = edit.value;
}
let encrypt = this.renderRoot.getElementById('encrypt_to');
if (encrypt) {
let tribute = new Tribute({
values: Object.entries(this.users).map(x => ({key: x[1].name, value: x[0]})),
selectTemplate: function(item) {
return item.original.value;
},
});
tribute.attach(encrypt);
}
}
remove_mention(id) {
@ -354,6 +410,45 @@ class TfComposeElement extends LitElement {
return this.drafts[this.branch || ''] || {};
}
update_encrypt(event) {
let input = event.srcElement;
let matches = input.value.match(/@.*?\.ed25519/g);
if (matches) {
let draft = this.get_draft();
let to = [...new Set(matches.concat(draft.encrypt_to))];
this.set_encrypt(to);
input.value = '';
}
}
render_encrypt() {
let draft = this.get_draft();
if (draft.encrypt_to === undefined) {
return;
}
return html`
<div style="display: flex; flex-direction: row; width: 100%">
<label for="encrypt_to">🔐 To:</label>
<input type="text" id="encrypt_to" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
<input type="button" value="🚮" @click=${() => this.set_encrypt(undefined)}></input>
</div>
<ul>
${draft.encrypt_to.map(x => html`
<li>
<tf-user id=${x} .users=${this.users}></tf-user>
<input type="button" value="🚮" @click=${() => this.set_encrypt(draft.encrypt_to.filter(id => id != x))}></input>
</li>`)}
</ul>
`;
}
set_encrypt(encrypt) {
let draft = this.get_draft();
draft.encrypt_to = encrypt;
this.notify(draft);
this.requestUpdate();
}
render() {
let self = this;
let draft = self.get_draft();
@ -361,7 +456,11 @@ class TfComposeElement extends LitElement {
draft.content_warning !== undefined ?
html`<div id="content_warning_preview" class="content_warning">${draft.content_warning}</div>` :
undefined;
let encrypt = draft.encrypt_to !== undefined ?
undefined :
html`<input type="button" value="🔐" @click=${() => this.set_encrypt([])}></input>`;
let result = html`
${this.render_encrypt()}
<div style="display: flex; flex-direction: row; width: 100%">
<textarea id="edit" @input=${this.input} @change=${this.change} @paste=${this.paste} style="flex: 1 0 50%">${draft.text}</textarea>
<div style="flex: 1 0 50%">
@ -372,13 +471,14 @@ class TfComposeElement extends LitElement {
${Object.values(draft.mentions || {}).map(x => self.render_mention(x))}
${this.render_content_warning()}
${this.render_attach_app()}
<input type="button" value="Submit" @click=${this.submit}></input>
<input type="button" id="submit" value="Submit" @click=${this.submit}></input>
<input type="button" value="Attach" @click=${this.attach}></input>
${this.render_attach_app_button()}
${encrypt}
<input type="button" value="Discard" @click=${this.discard}></input>
`;
return result;
}
}
customElements.define('tf-compose', TfComposeElement);
customElements.define('tf-compose', TfComposeElement);

View File

@ -14,7 +14,6 @@ class TfIdentityPickerElement extends LitElement {
constructor() {
super();
let self = this;
this.ids = [];
}
@ -34,4 +33,4 @@ class TfIdentityPickerElement extends LitElement {
}
}
customElements.define('tf-id-picker', TfIdentityPickerElement);
customElements.define('tf-id-picker', TfIdentityPickerElement);

View File

@ -11,10 +11,9 @@ class TfMessageElement extends LitElement {
message: {type: Object},
users: {type: Object},
drafts: {type: Object},
raw: {type: Boolean},
format: {type: String},
blog_data: {type: String},
expanded: {type: Object},
decrypted: {type: Object},
};
}
@ -27,13 +26,14 @@ class TfMessageElement extends LitElement {
this.message = {};
this.users = {};
this.drafts = {};
this.raw = false;
this.format = 'message';
this.expanded = {};
this.decrypted = undefined;
}
show_reply() {
let event = new CustomEvent('tf-draft', {bubbles: true, composed: true, detail: {id: this.message?.id, draft: ''}});
let event = new CustomEvent('tf-draft', {bubbles: true, composed: true, detail: {id: this.message?.id, draft: {
encrypt_to: this.message?.decrypted?.recps,
}}});
this.dispatchEvent(event);
}
@ -222,8 +222,8 @@ class TfMessageElement extends LitElement {
render_channels() {
let content = this.message?.content;
if (this.decrypted?.type == 'post') {
content = this.decrypted;
if (this?.messsage?.decrypted?.type == 'post') {
content = this.message.decrypted;
}
let channels = [];
if (typeof content.channel === 'string') {
@ -240,31 +240,36 @@ class TfMessageElement extends LitElement {
return channels.map(x => html`<tf-tag tag=${x}></tf-tag>`);
}
async try_decrypt(content) {
let result = await tfrpc.rpc.try_decrypt(this.whoami, content);
if (result) {
this.decrypted = JSON.parse(result);
} else {
this.decrypted = false;
}
}
render() {
let content = this.message?.content;
if (this.decrypted?.type == 'post') {
content = this.decrypted;
if (this.message?.decrypted?.type == 'post') {
content = this.message.decrypted;
}
let self = this;
let raw_button = this.raw ?
html`<input type="button" value="Message" @click=${() => self.raw = false}></input>` :
html`<input type="button" value="Raw" @click=${() => self.raw = true}></input>`;
let raw_button;
switch (this.format) {
case 'raw':
if (content?.type == 'post' || content?.type == 'blog') {
raw_button = html`<input type="button" value="Markdown" @click=${() => self.format = 'md'}></input>`;
} else {
raw_button = html`<input type="button" value="Message" @click=${() => self.format = 'message'}></input>`;
}
break;
case 'md':
raw_button = html`<input type="button" value="Message" @click=${() => self.format = 'message'}></input>`;
break;
default:
raw_button = html`<input type="button" value="Raw" @click=${() => self.format = 'raw'}></input>`;
break;
}
function small_frame(inner) {
let body;
return html`
<div style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px; display: inline-block; overflow-wrap: anywhere">
<tf-user id=${self.message.author} .users=${self.users}></tf-user>
<span style="padding-right: 8px"><a tfarget="_top" href=${'#' + self.message.id}>%</a> ${new Date(self.message.timestamp).toLocaleString()}</span>
${raw_button}
${self.raw ? self.render_raw() : inner}
${self.format == 'raw' ? self.render_raw() : inner}
${self.render_votes()}
</div>
`;
@ -342,9 +347,18 @@ class TfMessageElement extends LitElement {
<input type="button" value="Reply" @click=${this.show_reply}></input>
`;
let self = this;
let body = this.raw ?
this.render_raw() :
unsafeHTML(tfutils.markdown(content.text));
let body;
switch (this.format) {
case 'raw':
body = this.render_raw();
break;
case 'md':
body = html`<code style="white-space: pre-wrap; overflow-wrap: anywhere">${content.text}</code>`;
break;
case 'message':
body = unsafeHTML(tfutils.markdown(content.text));
break;
}
let content_warning = html`
<div class="content_warning" @click=${x => this.toggle_expanded(':cw')}>${content.contentWarning}</div>
`;
@ -363,8 +377,8 @@ class TfMessageElement extends LitElement {
` :
content_warning :
content_html;
let is_encrypted = this.decrypted ? html`<span style="align-self: center">🔓</span>` : undefined;
let style_background = this.decrypted ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.1)';
let is_encrypted = this.message?.decrypted ? html`<span style="align-self: center">🔓</span>` : undefined;
let style_background = this.message?.decrypted ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.1)';
return html`
<style>
code {
@ -397,6 +411,40 @@ class TfMessageElement extends LitElement {
${this.render_children()}
</div>
`;
} else if (content.type === 'issue') {
let is_encrypted = this.message?.decrypted ? html`<span style="align-self: center">🔓</span>` : undefined;
let style_background = this.message?.decrypted ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.1)';
return html`
<style>
code {
white-space: pre-wrap;
overflow-wrap: break-word;
}
div {
overflow-wrap: anywhere;
}
img {
max-width: 100%;
height: auto;
display: block;
}
</style>
<div style="border: 1px solid black; background-color: ${style_background}; margin-top: 8px; padding: 16px">
<div style="display: flex; flex-direction: row">
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
${is_encrypted}
<span style="flex: 1"></span>
<span style="padding-right: 8px"><a target="_top" href=${'#' + self.message.id}>%</a> ${new Date(this.message.timestamp).toLocaleString()}</span>
<span>${raw_button}</span>
</div>
${content.text}
${this.render_votes()}
<div>
<input type="button" value="React" @click=${this.react}></input>
</div>
${this.render_children()}
</div>
`;
} else if (content.type === 'blog') {
let self = this;
tfrpc.rpc.get_blob(content.blog).then(function(data) {
@ -406,9 +454,16 @@ class TfMessageElement extends LitElement {
this.expanded[(this.message.id || '') + ':blog'] ?
html`<div>${this.blog_data ? unsafeHTML(tfutils.markdown(this.blog_data)) : 'Loading...'}</div>` :
undefined;
let body = this.raw ?
this.render_raw() :
html`
let body;
switch (this.format) {
case 'raw':
body = this.render_raw();
break;
case 'md':
body = content.summary;
break;
case 'message':
body = html`
<div
style="border: 1px solid #fff; border-radius: 1em; padding: 8px; margin: 4px; cursor: pointer"
@click=${x => self.toggle_expanded(':blog')}>
@ -420,6 +475,8 @@ class TfMessageElement extends LitElement {
</div>
${payload}
`;
break;
}
return html`
<style>
code {
@ -468,13 +525,10 @@ class TfMessageElement extends LitElement {
</div>
`);
} else if (typeof(this.message.content) == 'string') {
if (this.decrypted) {
if (this.message?.decrypted) {
return small_frame(html`<span>🔓</span><pre>${JSON.stringify(this.decrypted, null, 2)}</pre>`);
} else if (this.decrypted === undefined) {
this.try_decrypt(content);
return small_frame(html`<span>🔐</span>`);
} else {
return small_frame(html`<span>🔒</span>`);
return small_frame(html`<span>🔒</span>`);
}
} else {
return small_frame(html`<div><b>type</b>: ${content.type}</div>`);

View File

@ -45,4 +45,10 @@ div.img_caption {
div.img_caption::after {
content: ' ±';
}
blockquote {
border-left: 4px solid #fff;
margin-left: 0px;
padding-left: 8px;
}
`;

View File

@ -42,11 +42,8 @@ class TfTabConnectionsElement extends LitElement {
let self = this;
let peers = this.broadcasts.filter(x => x.tunnel?.id == connection);
if (peers.length) {
return html`
<ul>
${peers.map(x => html`${self.render_room_peer(x)}`)}
</ul>
`;
let connections = this.connections.map(x => x.id);
return html`${peers.filter(x => connections.indexOf(x.pubkey) == -1).map(x => html`${self.render_room_peer(x)}`)}`;
}
}
@ -59,7 +56,7 @@ class TfTabConnectionsElement extends LitElement {
return html`
<li>
<input type="button" @click=${() => self._tunnel(connection.tunnel.id, connection.pubkey)} value="Connect"></input>
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user> 📡
</li>
`;
}
@ -79,6 +76,18 @@ class TfTabConnectionsElement extends LitElement {
this.stored_connections = (await tfrpc.rpc.getStoredConnections()) || [];
}
render_connection(connection) {
return html`
<input type="button" @click=${() => tfrpc.rpc.closeConnection(connection.id)} value="Close"></input>
<tf-user id=${connection.id} .users=${this.users}></tf-user>
${connection.tunnel !== undefined ? '🚇' : html`(${connection.host}:${connection.port})`}
<ul>
${this.connections.filter(x => x.tunnel === this.connections.indexOf(connection)).map(x => html`<li>${this.render_connection(x)}</li>`)}
${this.render_room_peers(connection.id)}
</ul>
`;
}
render() {
let self = this;
return html`
@ -93,12 +102,8 @@ class TfTabConnectionsElement extends LitElement {
</ul>
<h2>Connections</h2>
<ul>
${this.connections.map(x => html`
<li>
<input type="button" @click=${() => tfrpc.rpc.closeConnection(x)} value="Close"></input>
<tf-user id=${x} .users=${this.users}></tf-user>
${self.render_room_peers(x)}
</li>
${this.connections.filter(x => x.tunnel === undefined).map(x => html`
<li>${this.render_connection(x)}</li>
`)}
</ul>
<h2>Stored Connections (WIP)</h2>

View File

@ -70,7 +70,7 @@ class TfTabNewsFeedElement extends LitElement {
WITH news AS (SELECT messages.*
FROM messages
JOIN json_each(?) AS following ON messages.author = following.value
WHERE messages.timestamp > ?
WHERE messages.timestamp > ? AND messages.timestamp < ?
ORDER BY messages.timestamp DESC)
SELECT messages.*
FROM news
@ -87,6 +87,11 @@ class TfTabNewsFeedElement extends LitElement {
[
JSON.stringify(this.following),
this.start_time,
/*
** Don't show messages more than a day into the future to prevent
** messages with far-future timestamps from staying at the top forever.
*/
new Date().valueOf() + 24 * 60 * 60 * 1000,
]);
}
}
@ -119,7 +124,38 @@ class TfTabNewsFeedElement extends LitElement {
this.start_time,
last_start_time,
]);
this.messages = [...more, ...this.messages];
this.messages = await this.decrypt([...more, ...this.messages]);
}
async decrypt(messages) {
let result = [];
for (let message of messages) {
let content;
try {
content = JSON.parse(message?.content);
} catch {
}
if (typeof(content) === 'string') {
let decrypted;
try {
decrypted = await tfrpc.rpc.try_decrypt(this.whoami, content);
} catch {
}
if (decrypted) {
try {
message.decrypted = JSON.parse(decrypted);
} catch {
message.decrypted = decrypted;
}
}
}
result.push(message);
}
return result;
}
async add_messages(messages) {
this.messages = await this.decrypt([...messages, ...this.messages]);
}
render() {
@ -131,7 +167,7 @@ class TfTabNewsFeedElement extends LitElement {
this.messages = [];
this._messages_hash = this.hash;
this._messages_following = this.following;
this.fetch_messages().then(function(messages) {
this.fetch_messages().then(this.decrypt.bind(this)).then(function(messages) {
self.messages = messages;
console.log(`loading mesages done for ${self.whoami}`);
}).catch(function(error) {

View File

@ -48,7 +48,7 @@ class TfTabNewsElement extends LitElement {
let news = this.shadowRoot?.getElementById('news');
if (news) {
console.log('injecting messages', news.messages);
news.messages = Object.values(Object.fromEntries([...this.unread, ...news.messages].map(x => [x.id, x])));
news.add_messages(Object.values(Object.fromEntries(this.unread.map(x => [x.id, x]))));
this.dispatchEvent(new CustomEvent('refresh'));
}
}
@ -109,11 +109,11 @@ class TfTabNewsElement extends LitElement {
<div><input type="button" value=${this.new_messages_text()} @click=${this.show_more}></input></div>
<a target="_top" href="#" ?hidden=${this.hash.length <= 1}>🏠Home</a>
<div>Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!</div>
<div><tf-compose whoami=${this.whoami} .users=${this.users} .drafts=${this.drafts} @tf-draft=${this.draft}></tf-compose></div>
<div><tf-compose id="tf-compose" whoami=${this.whoami} .users=${this.users} .drafts=${this.drafts} @tf-draft=${this.draft}></tf-compose></div>
${profile}
<tf-tab-news-feed id="news" whoami=${this.whoami} .users=${this.users} .following=${this.following} hash=${this.hash} .drafts=${this.drafts} .expanded=${this.expanded} @tf-draft=${this.draft} @tf-expand=${this.on_expand}></tf-tab-news-feed>
`;
}
}
customElements.define('tf-tab-news', TfTabNewsElement);
customElements.define('tf-tab-news', TfTabNewsElement);

114
apps/ssb/tf-tab-query.js Normal file
View File

@ -0,0 +1,114 @@
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import {styles} from './tf-styles.js';
class TfTabQueryElement extends LitElement {
static get properties() {
return {
whoami: {type: String},
users: {type: Object},
following: {type: Array},
query: {type: String},
expanded: {type: Object},
results: {type: Array},
error: {type: Object},
duration: {type: Number},
};
}
static styles = styles;
constructor() {
super();
let self = this;
this.whoami = null;
this.users = {};
this.following = [];
this.expanded = {};
this.duration = undefined;
}
async search(query) {
console.log('Searching...', this.whoami, query);
this.results = [];
this.error = undefined;
this.duration = undefined;
let search = this.renderRoot.getElementById('search');
if (search) {
search.value = query;
search.focus();
}
await tfrpc.rpc.setHash('#sql=' + encodeURIComponent(query));
let start_time = new Date();
try {
this.results = await tfrpc.rpc.query(query, [])
} catch (error) {
this.error = error;
}
let end_time = new Date();
this.duration = (end_time - start_time).valueOf();
console.log('Done.');
search = this.renderRoot.getElementById('search');
if (search) {
search.value = query;
search.focus();
}
}
search_keydown(event) {
if (event.keyCode == 13 && event.ctrlKey) {
this.query = this.renderRoot.getElementById('search').value;
event.preventDefault();
}
}
on_expand(event) {
if (event.detail.expanded) {
let expand = {};
expand[event.detail.id] = true;
this.expanded = Object.assign({}, this.expanded, expand);
} else {
delete this.expanded[event.detail.id];
this.expanded = Object.assign({}, this.expanded);
}
}
render_results() {
if (!this.results?.length) {
return html`<div>No results.</div>`;
} else {
let keys = Object.keys(this.results[0]).sort();
return html`<table style="width: 100%; max-width: 100%">
<tr>${keys.map(key => html`<th>${key}</th>`)}</tr>
${this.results.map(row => html`<tr>${keys.map(key => html`<td>${row[key]}</td>`)}</tr>`)}
</table>`;
}
}
render_error() {
if (this.error) {
return html`<h2 style="color: red">${this.error.message}</h2>
<pre style="color: red">${this.error.stack}</pre>`;
}
}
render() {
if (this.query !== this.last_query) {
this.last_query = this.query;
this.search(this.query);
}
let self = this;
return html`
<div style="display: flex; flex-direction: row">
<textarea id="search" rows=8 style="flex: 1" @keydown=${this.search_keydown}>${this.query}</textarea>
<input type="button" value="Execute" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}></input>
</div>
<div ?hidden=${this.duration === undefined}>Took ${this.duration / 1000.0} seconds.</div>
<div ?hidden=${this.duration !== undefined}>Executing...</div>
${this.render_error()}
${this.render_results()}
`;
}
}
customElements.define('tf-tab-query', TfTabQueryElement);

View File

@ -124,9 +124,14 @@ function socket(request, response, client) {
options.credentials = credentials;
options.packageOwner = packageOwner;
options.packageName = packageName;
options.url = message.url;
let sessionId = makeSessionId();
if (blobId) {
process = await core.getSessionProcessBlob(blobId, sessionId, options);
if (message.edit_only) {
response.send(JSON.stringify({action: 'ready', edit_only: true}), 0x1);
} else {
process = await core.getSessionProcessBlob(blobId, sessionId, options);
}
}
if (process) {
process.app.readOutput(function(message) {

View File

@ -2,18 +2,137 @@
<html>
<head>
<title>Tilde Friends Sign-in</title>
<script>
function showHideConfirm() {
document.getElementById("confirmPassword").style.display = document.getElementById("register").checked ? "block" : "none";
}
</script>
<link type="text/css" rel="stylesheet" href="/static/style.css">
<link type="image/png" rel="shortcut icon" href="/static/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--HEAD-->
</head>
<body>
<h1 style="text-align: center">Tilde Friends Sign-in</h1>
<div id="content"><!--SESSION--></div>
<tf-auth id="auth"></tf-auth>
<script>window.litDisableBundleWarning = true;</script>
<script type="module">
import {LitElement, html} from '/static/lit/lit-all.min.js';
let g_data = $AUTH_DATA;
let app = document.getElementById('auth');
Object.assign(app, g_data);
class TfAuthElement extends LitElement {
static get_properties() {
return {
name: {type: String},
tab: {type: String},
};
}
constructor() {
super();
this.tab = 'login';
}
tab_changed(name) {
this.tab = name;
this.requestUpdate();
}
render() {
let self = this;
return html`
<style>
[name="tab"] {
display: none;
}
[name="tab"]+label {
background-color: #586e75;
padding: 1em;
display: inline-block;
flex: 1 0;
text-align: center;
cursor: pointer;
}
[name="tab"]+label:hover {
background-color: #dc322f;
}
[name="tab"]:checked+label {
background-color: #93a1a1;
border: 2px solid #eee8d5;
}
.error {
color: #f00;
border: 1px solid #f00;
margin: 4px;
padding: 8px;
}
form label {
padding-top: 1em;
font-weight: bold;
}
form input {
font-size: x-large;
padding: 4px;
}
input[type="submit"] {
margin-top: 1em;
margin-bottom: 1em;
padding: 1em;
}
</style>
<div style="display: flex; flex-direction: column; max-width: 1280px; margin: auto">
<h1 ?hidden=${this.name}>Welcome.</h1>
<h1 ?hidden=${this.name === undefined}>Welcome, ${this.name}.</h1>
<div style="display: flex; flex-direction: row; width: 100%">
<input type="radio" name="tab" id="login" value="Login" ?checked=${this.tab == 'login'} @change=${() => self.tab_changed('login')}></input>
<label for="login" id="login_label">Login</label>
<input type="radio" name="tab" id="register" value="Register" ?checked=${this.tab == 'register'} @change=${() => self.tab_changed('register')}></input>
<label for="register" id="register_label">Register</label>
<input type="radio" name="tab" id="guest" value="Guest" ?checked=${this.tab == 'guest'} @change=${() => self.tab_changed('guest')}></input>
<label for="guest" id="guest_label">Guest</label>
<input type="radio" name="tab" id="change" value="Change Password" ?checked=${this.tab == 'change'} @change=${() => self.tab_changed('change')}></input>
<label for="change" id="change_label">Change Password</label>
</div>
<div ?hidden=${this.tab != 'login' && this.tab != 'register' && this.tab != 'change'}>
<div id="error" ?hidden=${this.error === undefined} class="error">
${this.error}
</div>
<form method="POST" style="display: flex; flex-direction: column">
<label for="name">Name:</label>
<input type="text" id="name" name="name"></input>
<label for="password">${this.tab == 'change' ? 'Old ' : ''}Password:</label>
<input type="password" id="password" name="password"></input>
<label ?hidden=${this.tab != 'change'} for="new_password">New Password:</label>
<input ?hidden=${this.tab != 'change'} type="password" id="new_password" name="new_password"></input>
<label ?hidden=${this.tab != 'register' && this.tab != 'change'} for="confirm">Confirm ${this.tab == 'change' ? 'New ' : ''}Password:</label>
<input ?hidden=${this.tab != 'register' && this.tab != 'change'} type="password" id="confirm" name="confirm"></input>
<input id="loginButton" type="submit" name="submit" value="Login"></input>
<input type="hidden" name="register" value="${this.tab == 'register' ? 1 : 0}"></input>
<input type="hidden" name="change" value="${this.tab == 'change' ? 1 : 0}"></input>
</form>
</div>
<div ?hidden=${this.tab != 'guest'}>
<form method="POST" style="display: flex; flex-direction: column">
<input type="submit" id="guestButton" name="guestButton" value="Proceed as Guest"></input>
</form>
</div>
<div ?hidden=${this.have_administrator} class="notice">
There is currently no administrator. You will be made administrator.
</div>
<h2>Code of Conduct</h2>
<textarea readonly rows="20" cols="80" style="resize: none">${this.code_of_conduct}</textarea>
</div>
`;
}
}
customElements.define('tf-auth', TfAuthElement);
</script>
</body>
</html>

View File

@ -113,22 +113,37 @@ function getCookies(headers) {
return cookies;
}
function isNameValid(name) {
let c = name.charAt(0);
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && name.split().map(x => x >= ('a' && x <= 'z') || x >= ('A' && x <= 'Z') || x >= ('0' && x <= '9'));
}
function handler(request, response) {
let session = getCookies(request.headers).session;
if (request.uri == "/login") {
let formData = form.decodeForm(request.query);
if (query(request.headers)?.permissions?.authenticated) {
if (formData.return) {
response.writeHead(303, {"Location": formData.return});
} else {
response.writeHead(303, {"Location": (request.client.tls ? 'https://' : 'http://') + request.headers.host + '/', "Content-Length": "0"});
}
response.end();
return;
}
let sessionIsNew = false;
let loginError;
let formData = form.decodeForm(request.query);
if (request.method == "POST" || formData.submit) {
sessionIsNew = true;
formData = form.decodeForm(utf8Decode(request.body), formData);
if (formData.submit == "Login") {
let account = gDatabase.get("user:" + formData.name);
account = account ? JSON.parse(account) : account;
if (formData.register == "1") {
if (formData.register == '1') {
if (!account &&
isNameValid(formData.name) &&
formData.password == formData.confirm) {
let users = new Set();
let users_original = gDatabase.get('users');
@ -145,12 +160,23 @@ function handler(request, response) {
}
session = makeJwt({name: formData.name});
account = {password: hashPassword(formData.password)};
gDatabase.set("user:" + formData.name, JSON.stringify(account));
gDatabase.set('user:' + formData.name, JSON.stringify(account));
if (noAdministrator()) {
makeAdministrator(formData.name);
}
} else {
loginError = "Error registering account.";
loginError = 'Error registering account.';
}
} else if (formData.change == '1') {
if (account &&
isNameValid(formData.name) &&
formData.new_password == formData.confirm &&
verifyPassword(formData.password, account.password)) {
session = makeJwt({name: formData.name});
account = {password: hashPassword(formData.new_password)};
gDatabase.set('user:' + formData.name, JSON.stringify(account));
} else {
loginError = 'Error changing password.';
}
} else {
if (account &&
@ -161,7 +187,7 @@ function handler(request, response) {
makeAdministrator(formData.name);
}
} else {
loginError = "Invalid username or password.";
loginError = 'Invalid username or password.';
}
}
} else {
@ -178,46 +204,16 @@ function handler(request, response) {
} else {
File.readFile("core/auth.html").then(function(data) {
let html = utf8Decode(data);
let contents = "";
if (entry) {
if (sessionIsNew) {
contents += '<div>Welcome back, ' + entry.name + '.</div>\n';
} else {
contents += '<div>You are already logged in, ' + entry.name + '.</div>\n';
}
contents += '<div><a href="/login/logout">Logout</a></div>\n';
} else {
contents += '<form method="POST">\n';
if (loginError) {
contents += "<p>" + loginError + "</p>\n";
}
contents += '<div id="auth_greeting"><b>Halt. Who goes there?</b></div>\n'
contents += '<div id="auth">\n';
contents += '<div id="auth_login">\n'
if (noAdministrator()) {
contents += '<div class="notice">There is currently no administrator. You will be made administrator.</div>\n';
}
contents += '<div><label for="name">Name:</label> <input type="text" id="name" name="name" value=""></div>\n';
contents += '<div><label for="password">Password:</label> <input type="password" id="password" name="password" value=""></div>\n';
contents += '<div id="confirmPassword" style="display: none"><label for="confirm">Confirm:</label> <input type="password" id="confirm" name="confirm" value=""></div>\n';
contents += '<div><input type="checkbox" id="register" name="register" value="1" onchange="showHideConfirm()"> <label for="register">Register a new account</label></div>\n';
contents += '<div><input id="loginButton" type="submit" name="submit" value="Login"></div>\n';
contents += '</div>';
contents += '<div class="auth_or"> - or - </div>';
contents += '<div id="auth_guest">\n';
contents += '<input id="guestButton" type="submit" name="submit" value="Proceeed as Guest">\n';
contents += '</div>\n';
contents += '</div>\n';
contents += '<div style="text-align: center">\n';
contents += '<h2>Code of Conduct</h2>\n';
contents += `<div><textarea readonly rows=20 cols=80>${core.globalSettings.code_of_conduct}</textarea></div>\n`;
contents += '</div>\n';
contents += '</form>';
}
let text = html.replace("<!--SESSION-->", contents);
response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": text.length});
response.end(text);
let auth_data = {
session_is_new: sessionIsNew,
name: entry?.name,
error: loginError,
code_of_conduct: core.globalSettings.code_of_conduct,
have_administrator: !noAdministrator(),
};
html = utf8Encode(html.replace('$AUTH_DATA', JSON.stringify(auth_data)));
response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": html.length});
response.end(html);
}).catch(function(error) {
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
response.end("404 File not found");

View File

@ -6,9 +6,6 @@ let gCurrentFile;
let gFiles = {};
let gApp = {files: {}, emoji: '📦'};
let gEditor;
let gSplit;
let gGraphs = {};
let gTimeSeries = {};
let gOriginalInput;
let kErrorColor = "#dc322f";
@ -83,7 +80,7 @@ class TfNavigationElement extends LitElement {
let spark_line = document.createElement('tf-sparkline');
spark_line.style.display = 'flex';
spark_line.style.flexDirection = 'row';
spark_line.style.flex = '0 100 10em';
spark_line.style.flex = '0 50 5em';
spark_line.title = key;
if (options) {
if (options.max) {
@ -98,9 +95,9 @@ class TfNavigationElement extends LitElement {
render_login() {
if (this?.credentials?.session?.name) {
return html`<a href="/login/logout?return=${url() + hash()}">logout ${this.credentials.session.name}</a>`;
return html`<a id="login" href="/login/logout?return=${url() + hash()}">logout ${this.credentials.session.name}</a>`;
} else {
return html`<a href="/login?return=${url() + hash()}">login</a>`;
return html`<a id="login" href="/login?return=${url() + hash()}">login</a>`;
}
}
@ -129,7 +126,7 @@ class TfNavigationElement extends LitElement {
<style>
${k_global_style}
</style>
<div style="margin: 4px; display: flex; flex-direction: row; flex-wrap: nowrap; gap: 3px">
<div style="margin: 4px; display: flex; flex-direction: row; flex-wrap: nowrap; gap: 3px; align-items: center">
<span style="cursor: pointer" @click=${() => this.show_version = !this.show_version}>😎</span>
<span ?hidden=${!this.show_version} style="flex: 0 0; white-space: nowrap" title=${this.version?.name + ' ' + Object.entries(this.version || {}).filter(x => ['name', 'number'].indexOf(x[0]) == -1).map(x => `\n* ${x[0]}: ${x[1]}`)}>${this.version?.number}</span>
<a accesskey="h" data-tip="Open home app." href="/" style="color: #fff; white-space: nowrap">TF</a>
@ -139,7 +136,7 @@ class TfNavigationElement extends LitElement {
<span style="display: inline-block; vertical-align: top; white-space: pre; color: ${this.status.color ?? kErrorColor}">${this.status.message}</span>
<span id="requests"></span>
${this.render_permissions()}
<span style="flex: 1 1; display: flex; flex-direction: row; white-space: nowrap; margin: 0; padding: 0">${Object.keys(this.spark_lines).sort().map(x => this.spark_lines[x]).map(x => [x.dataset.emoji, x])}</span>
<span style="flex: 1 1; display: flex; flex-direction: row; white-space: nowrap; margin: 0; padding: 0">${Object.keys(this.spark_lines).sort().map(x => this.spark_lines[x]).map(x => [html`<span style="font-size: xx-small">${x.dataset.emoji}</span>`, x])}</span>
<span style="flex: 0 0; white-space: nowrap">${this.render_login()}</span>
</div>
`;
@ -313,7 +310,7 @@ class TfSparkLineElement extends LitElement {
render_line(line) {
if (line?.values?.length >= 2) {
let max = Math.max(this.max, ...line.values);
let points = [].concat(...line.values.map((x, i) => [100.0 * i / (line.values.length - 1), 10.0 - 10.0 * (x - this.min) / (max - this.min)]));
let points = [].concat(...line.values.map((x, i) => [50.0 * i / (line.values.length - 1), 10.0 - 10.0 * (x - this.min) / (max - this.min)]));
return svg`<polyline points=${points.join(' ')} stroke=${line.style} fill="none"/>`;
}
}
@ -321,7 +318,7 @@ class TfSparkLineElement extends LitElement {
render() {
let max = Math.round(10.0 * Math.max(...this.lines.map(line => line.values[line.values.length - 1]))) / 10.0;
return html`
<svg style="width-auto: object-fit: cover; margin: 0; padding: 0; background: #000" viewBox="0 0 100 10" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<svg style="max-width: 7.5em; max-height: 1.5em; margin: 0; padding: 0; background: #000" viewBox="0 0 50 10" xmlns="http://www.w3.org/2000/svg">
${this.lines.map(x => this.render_line(x))}
<text x="0" y="1em" style="font: 8px sans-serif; fill: #fff">${max}</text>
</svg>
@ -385,18 +382,18 @@ function editing() {
return document.getElementById("editPane").style.display != 'none';
}
function is_edit_only() {
return window.location.search == '?editonly=1' || window.innerWidth < 1024;
}
function edit() {
if (editing()) {
return;
}
window.localStorage.setItem('editing', '1');
if (gSplit) {
gSplit.destroy();
gSplit = undefined;
}
gSplit = Split(['#editPane', '#viewPane'], {minSize: 0});
document.getElementById("editPane").style.display = 'flex';
document.getElementById('viewPane').style.display = is_edit_only() ? 'none' : 'flex';
ensureLoaded([
{tagName: "script", attributes: {src: "/codemirror/codemirror.min.js"}},
@ -431,24 +428,6 @@ function trace() {
window.open(`/speedscope/#profileURL=${encodeURIComponent('/trace')}`);
}
function stats() {
window.localStorage.setItem('stats', '1');
document.getElementById("statsPane").style.display = 'flex';
}
function closeStats() {
window.localStorage.setItem('stats', '0');
document.getElementById("statsPane").style.display = 'none';
}
function toggleStats() {
if (document.getElementById("statsPane").style.display == 'none') {
stats();
} else {
closeStats();
}
}
function guessMode(name) {
return name.endsWith(".js") ? "javascript" :
name.endsWith(".html") ? "htmlmixed" :
@ -533,10 +512,7 @@ function load(path) {
function closeEditor() {
window.localStorage.setItem('editing', '0');
document.getElementById("editPane").style.display = 'none';
if (gSplit) {
gSplit.destroy();
gSplit = undefined;
}
document.getElementById('viewPane').style.display = 'flex';
}
function explodePath() {
@ -724,11 +700,13 @@ function api_requestPermission(permission, id) {
const k_options = [
{
id: 'allow',
text: '✅ Allow',
grant: ['allow once', 'allow'],
},
{
id: 'deny',
text: '❌ Deny',
grant: ['deny once', 'deny'],
},
@ -739,6 +717,7 @@ function api_requestPermission(permission, id) {
for (let option of k_options) {
let button = document.createElement('button');
button.innerText = option.text;
button.id = option.id;
button.onclick = function() {
resolve(option.grant[check.checked ? 1 : 0]);
document.body.removeChild(outer);
@ -772,6 +751,7 @@ function _receive_websocket_message(message) {
send({event: "hashChange", hash: window.location.hash});
}
document.getElementsByTagName('tf-navigation')[0].version = message.version;
document.getElementById('viewPane').style.display = message.edit_only ? 'none' : 'flex';
send({action: 'enableStats', enabled: true});
} else if (message && message.action == "ping") {
send({action: "pong"});
@ -804,63 +784,13 @@ function _receive_websocket_message(message) {
};
const k_colors = ['#0f0', '#88f', '#ff0', '#f0f', '#0ff', '#f00', '#888'];
let graph_key = k_groups[key]?.group || key;
let graph = gGraphs[graph_key];
if (!graph) {
graph = {
chart: new SmoothieChart({
millisPerPixel: 100,
minValue: 0,
grid: {
millisPerLine: 1000,
verticalSections: 10,
},
tooltip: true,
}),
canvas: document.createElement('canvas'),
title: document.createElement('div'),
series: [],
};
gGraphs[graph_key] = graph;
graph.canvas.width = 240;
graph.canvas.height = 64;
graph.title.innerText = graph_key;
graph.title.style.flex = '0';
document.getElementById('graphs').appendChild(graph.title);
document.getElementById('graphs').appendChild(graph.canvas);
graph.chart.streamTo(graph.canvas, 1000);
}
let timeseries = gTimeSeries[key];
if (!timeseries) {
let is_multi = key != graph_key || graph.series.length > 1;
timeseries = new TimeSeries();
gTimeSeries[key] = timeseries;
graph.chart.addTimeSeries(timeseries, {lineWidth: 2, strokeStyle: is_multi ? k_colors[graph.series.length] : '#fff'});
graph.series.push(k_groups[key]?.name || key);
if (is_multi) {
while (graph.title.firstChild) {
graph.title.removeChild(graph.title.firstChild);
}
function makeColoredText(text, color) {
let element = document.createElement('span');
element.style.color = color;
element.innerText = text;
return element;
}
graph.title.appendChild(makeColoredText(graph_key + ':', '#fff'));
for (let series of graph.series) {
graph.title.appendChild(makeColoredText(' ' + series, k_colors[graph.series.indexOf(series)]));
}
}
}
timeseries.append(now, message.stats[key]);
if (graph_key == 'cpu' || graph_key == 'rpc' || graph_key == 'store') {
if (['cpu', 'rpc', 'store', 'memory'].indexOf(graph_key) != -1) {
let line = document.getElementsByTagName('tf-navigation')[0].get_spark_line(graph_key, { max: 100 });
line.dataset.emoji = {
'cpu': '💻',
'rpc': '🔁',
'store': '💾',
'memory': '🐏',
}[graph_key];
line.append(key, message.stats[key]);
}
@ -903,22 +833,6 @@ function send(value) {
}
}
function dragHover(event) {
event.stopPropagation();
event.preventDefault();
let input = document.getElementById("input");
if (event.type == "dragover") {
if (!input.classList.contains("drop")) {
input.classList.add("drop");
gOriginalInput = input.value;
input.value = "drop file to upload";
}
} else {
input.classList.remove("drop");
input.value = gOriginalInput;
}
}
function fixImage(sourceData, maxWidth, maxHeight, callback) {
let result = sourceData;
let image = new Image();
@ -948,52 +862,6 @@ function sendImage(image) {
});
}
function fileDropRead(event) {
sendImage(event.target.result);
}
function fileDrop(event) {
dragHover(event);
let done = false;
if (!done) {
let files = event.target.files || event.dataTransfer.files;
for (let i = 0; i < files.length; i++) {
let file = files[i];
if (file.type.substring(0, "image/".length) == "image/") {
let reader = new FileReader();
reader.onloadend = fileDropRead;
reader.readAsDataURL(file);
done = true;
}
}
}
if (!done) {
let html = event.dataTransfer.getData("text/html");
let match = /<img.*src="([^"]+)"/.exec(html);
if (match) {
sendImage(match[1]);
done = true;
}
}
if (!done) {
let text = event.dataTransfer.getData("text/plain");
if (text) {
send(text);
done = true;
}
}
}
function enableDragDrop() {
let body = document.body;
body.addEventListener("dragover", dragHover);
body.addEventListener("dragleave", dragHover);
body.addEventListener("drop", fileDrop);
}
function hashChange() {
send({event: 'hashChange', hash: window.location.hash});
}
@ -1072,6 +940,8 @@ function connectSocket(path) {
gSocket.send(JSON.stringify({
action: "hello",
path: connect_path,
url: window.location.href,
edit_only: editing() && is_edit_only(),
api: Object.entries(k_api).map(([key, value]) => [].concat([key], value.args)),
}));
}
@ -1153,7 +1023,6 @@ window.addEventListener("load", function() {
window.addEventListener("message", message, false);
window.addEventListener("online", connectSocket);
document.getElementById("name").value = window.location.pathname;
document.getElementById('closeStats').addEventListener('click', () => closeStats());
document.getElementById('closeEditor').addEventListener('click', () => closeEditor());
document.getElementById('save').addEventListener('click', () => save());
document.getElementById('icon').addEventListener('click', () => changeIcon());
@ -1162,10 +1031,6 @@ window.addEventListener("load", function() {
event.preventDefault();
trace();
});
document.getElementById('stats_button').addEventListener('click', function(event) {
event.preventDefault();
toggleStats();
});
for (let tag of document.getElementsByTagName('a')) {
if (tag.accessKey) {
tag.classList.add('tooltip_parent');
@ -1190,7 +1055,6 @@ window.addEventListener("load", function() {
tag.appendChild(tooltip);
}
}
enableDragDrop();
connectSocket(window.location.pathname);
if (window.localStorage.getItem('editing') == '1') {
@ -1198,9 +1062,4 @@ window.addEventListener("load", function() {
} else {
closeEditor();
}
if (window.localStorage.getItem('stats') == '1') {
stats();
} else {
closeStats();
}
});

View File

@ -72,6 +72,16 @@ const k_global_settings = {
default_value: undefined,
description: 'Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.',
},
blob_fetch_age_seconds: {
type: 'integer',
default_value: (platform() == 'android' ? 0.5 * 365 * 24 * 60 * 60 : undefined),
description: 'Only blobs mentioned more recently than this age will be automatically fetched.',
},
blob_expire_age_seconds: {
type: 'integer',
default_value: (platform() == 'android' ? 1.0 * 365 * 24 * 60 * 60 : undefined),
description: 'Blobs older than this will be automatically deleted.',
},
};
let gGlobalSettings = {
@ -305,6 +315,7 @@ async function getProcessBlob(blobId, key, options) {
throw Error(`Permission denied: ${permission}.`);
}
},
url: options?.url,
}
};
if (process.credentials?.permissions?.administration) {
@ -327,7 +338,7 @@ async function getProcessBlob(blobId, key, options) {
print('Done.');
};
imports.core.deleteUser = function(user) {
return imports.core.permissionTest('delete_user').then(function() {
return Promise.resolve(imports.core.permissionTest('delete_user')).then(function() {
let db = new Database('auth');
db.remove('user:' + user);
@ -391,7 +402,7 @@ async function getProcessBlob(blobId, key, options) {
if (process.credentials &&
process.credentials.session &&
process.credentials.session.name) {
return imports.core.permissionTest('ssb_append').then(function() {
return Promise.resolve(imports.core.permissionTest('ssb_append')).then(function() {
return ssb.appendMessageWithIdentity(process.credentials.session.name, id, message);
});
}
@ -923,10 +934,6 @@ loadSettings().then(function() {
return staticDirectoryHandler(request, response, 'deps/codemirror/', match[1]);
} else if (match = /^\/speedscope\/([\.\w-/]*)$/.exec(request.uri)) {
return staticDirectoryHandler(request, response, 'deps/speedscope/', match[1]);
} else if (match = /^\/split\/([\.\w-/]*)$/.exec(request.uri)) {
return staticDirectoryHandler(request, response, 'deps/split/', match[1]);
} else if (match = /^\/smoothie\/([\.\w-/]*)$/.exec(request.uri)) {
return staticDirectoryHandler(request, response, 'deps/smoothie/', match[1]);
} else if (match = /^\/static(\/.*)/.exec(request.uri)) {
return staticFileHandler(request, response, null, match[1]);
} else if (request.uri == "/robots.txt") {

View File

@ -4,7 +4,7 @@ let gHandlers = [];
let gSocketHandlers = [];
let gBadRequests = {};
const kRequestTimeout = 15000;
const kRequestTimeout = 5000;
const kStallTimeout = 60000;
function logError(error) {
@ -395,41 +395,10 @@ function handleConnection(client) {
let parsing_header = true;
let bodyToRead = -1;
let body;
let requestCount = -1;
let readCount = 0;
let isWebsocket = false;
function resetTimeout(requestIndex) {
if (isWebsocket) {
return;
}
if (bodyToRead == -1) {
setTimeout(function() {
if (requestCount == requestIndex) {
client.info = 'timed out';
if (requestCount == 0) {
badRequest(client, 'Timed out waiting for request.');
} else {
client.close();
}
}
}, kRequestTimeout);
} else {
let lastReadCount = readCount;
setTimeout(function() {
if (readCount == lastReadCount) {
client.info = 'stalled';
if (requestCount == 0) {
badRequest(client, 'Request stalled.');
} else {
client.close();
}
}
}, kStallTimeout);
}
}
resetTimeout(++requestCount);
client.setActivityTimeout(kRequestTimeout);
function reset() {
request = undefined;
@ -438,7 +407,7 @@ function handleConnection(client) {
bodyToRead = -1;
body = undefined;
client.info = 'reset';
resetTimeout(++requestCount);
client.setActivityTimeout(kRequestTimeout);
}
function finish() {
@ -463,9 +432,6 @@ function handleConnection(client) {
client.read(function(data) {
readCount++;
if (data) {
if (bodyToRead != -1 && !isWebsocket) {
resetTimeout(requestCount);
}
let newBuffer = new Uint8Array(inputBuffer.length + data.length);
newBuffer.set(inputBuffer, 0);
newBuffer.set(data, inputBuffer.length);
@ -483,6 +449,7 @@ function handleConnection(client) {
return;
}
} else if (typeof result === 'object') {
client.setActivityTimeout(kStallTimeout);
request = [
result.method,
result.path,
@ -509,7 +476,6 @@ function handleConnection(client) {
}
body = new Uint8Array(bodyToRead);
client.info = 'waiting for body';
resetTimeout(requestCount);
} else if (headers["connection"]
&& headers["connection"].toLowerCase().split(",").map(x => x.trim()).indexOf("upgrade") != -1
&& headers["upgrade"]
@ -520,7 +486,7 @@ function handleConnection(client) {
let response = new Response(requestObject, client);
handleWebSocketRequest(requestObject, response, client);
/* Prevent the timeout from disconnecting us. */
requestCount++;
client.setActivityTimeout();
} else {
finish();
}

View File

@ -9,13 +9,7 @@
<body style="display: flex; flex-flow: column">
<tf-navigation></tf-navigation>
<div id="content" class="hbox" style="flex: 1 1; width: 100%">
<div id="statsPane" class="vbox" style="display: none; flex 1 1">
<div class="hbox">
<input type="button" id="closeStats" name="closeStats" value="Close">
</div>
<div id="graphs" class="vbox" style="height: 100%"></div>
</div>
<div id="editPane" class="vbox" style="display: none">
<div id="editPane" class="vbox" style="flex: 1 1; display: none">
<div class="navigation hbox">
<input type="button" id="closeEditor" name="closeEditor" value="Close">
<input type="button" id="save" name="save" value="Save">
@ -23,7 +17,6 @@
<input type="text" id="name" name="name" style="flex: 1 1; min-width: 1em"></input>
<input type="button" id="delete" name="delete" value="Delete">
<input type="button" id="trace_button" value="Trace">
<input type="button" id="stats_button" value="Stats">
</div>
<div class="hbox" style="height: 100%">
<tf-files-pane></tf-files-pane>
@ -34,13 +27,11 @@
</div>
</div>
</div>
<div id="viewPane" class="vbox" style="flex: 1 0; overflow: auto">
<iframe id="document" sandbox="allow-forms allow-scripts allow-top-navigation allow-modals allow-downloads" style="width: 100%; height: 100%; border: 0"></iframe>
<div id="viewPane" class="vbox" style="flex: 1 1; overflow: auto">
<iframe id="document" sandbox="allow-forms allow-scripts allow-top-navigation allow-modals allow-popups allow-downloads" style="width: 100%; height: 100%; border: 0"></iframe>
</div>
</div>
<script>window.litDisableBundleWarning = true;</script>
<script src="/split/split.min.js"></script>
<script src="/smoothie/smoothie.js"></script>
<script src="/static/client.js" type="module"></script>
</body>
</html>

View File

@ -64,11 +64,6 @@ a:active {
color: #eee8d5;
}
#input.drop {
border: 2px solid;
color: #cb4b16;
}
.CodeMirror {
height: 100%;
padding: 0;
@ -105,33 +100,6 @@ a:active {
float: right;
}
#auth_greeting {
text-align: center;
}
#auth {
display: flex;
flex-flow: row;
align-content: center;
align-items: center;
text-align: center;
justify-content: center;
}
#auth_login {
flex: 0 1 auto;
text-align: right;
}
.auth_or {
flex: 0 1 auto;
padding: 1em;
}
#auth_guest {
flex: 0 1 auto;
}
.notice {
color: #cb4b16;
margin: 1em;

View File

@ -1 +1 @@
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],t):t(CodeMirror)}(function(t){"use strict";function e(t,e){function i(t){clearTimeout(n.doRedraw),n.doRedraw=setTimeout(function(){n.redraw()},t)}this.cm=t,this.options=e,this.buttonHeight=e.scrollButtonHeight||t.getOption("scrollButtonHeight"),this.annotations=[],this.doRedraw=this.doUpdate=null,this.div=t.getWrapperElement().appendChild(document.createElement("div")),this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none",this.computeScale();var n=this;t.on("refresh",this.resizeHandler=function(){clearTimeout(n.doUpdate),n.doUpdate=setTimeout(function(){n.computeScale()&&i(20)},100)}),t.on("markerAdded",this.resizeHandler),t.on("markerCleared",this.resizeHandler),!1!==e.listenForChanges&&t.on("changes",this.changeHandler=function(){i(250)})}t.defineExtension("annotateScrollbar",function(t){return new e(this,t="string"==typeof t?{className:t}:t)}),t.defineOption("scrollButtonHeight",0),e.prototype.computeScale=function(){var t=this.cm,t=(t.getWrapperElement().clientHeight-t.display.barHeight-2*this.buttonHeight)/t.getScrollerElement().scrollHeight;if(t!=this.hScale)return this.hScale=t,!0},e.prototype.update=function(t){this.annotations=t,this.redraw()},e.prototype.redraw=function(t){!1!==t&&this.computeScale();var n=this.cm,e=this.hScale,i=document.createDocumentFragment(),o=this.annotations,r=n.getOption("lineWrapping"),a=r&&1.5*n.defaultTextHeight(),s=null,h=null;function l(t,e){var i;return s!=t.line&&(s=t.line,h=n.getLineHandle(t.line),(i=n.getLineHandleVisualStart(h))!=h&&(s=n.getLineNumber(i),h=i)),h.widgets&&h.widgets.length||r&&h.height>a?n.charCoords(t,"local")[e?"top":"bottom"]:n.heightAtLine(h,"local")+(e?0:h.height)}var d=n.lastLine();if(n.display.barWidth)for(var c,p=0;p<o.length;p++){var u=o[p];if(!(u.to.line>d)){for(var m,f,g=c||l(u.from,!0)*e,H=l(u.to,!1)*e;p<o.length-1&&!(o[p+1].to.line>d)&&!(H+.9<(c=l(o[p+1].from,!0)*e));)H=l((u=o[++p]).to,!1)*e;H!=g&&(m=Math.max(H-g,3),(f=i.appendChild(document.createElement("div"))).style.cssText="position: absolute; right: 0px; width: "+Math.max(n.display.barWidth-1,2)+"px; top: "+(g+this.buttonHeight)+"px; height: "+m+"px",f.className=this.options.className,u.id&&f.setAttribute("annotation-id",u.id))}}this.div.textContent="",this.div.appendChild(i)},e.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler),this.cm.off("markerAdded",this.resizeHandler),this.cm.off("markerCleared",this.resizeHandler),this.changeHandler&&this.cm.off("changes",this.changeHandler),this.div.parentNode.removeChild(this.div)}});
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],t):t(CodeMirror)}(function(t){"use strict";function e(t,e){function i(t){clearTimeout(n.doRedraw),n.doRedraw=setTimeout(function(){n.redraw()},t)}this.cm=t,this.options=e,this.buttonHeight=e.scrollButtonHeight||t.getOption("scrollButtonHeight"),this.annotations=[],this.doRedraw=this.doUpdate=null,this.div=t.getWrapperElement().appendChild(document.createElement("div")),this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none",this.computeScale();var n=this;t.on("refresh",this.resizeHandler=function(){clearTimeout(n.doUpdate),n.doUpdate=setTimeout(function(){n.computeScale()&&i(20)},100)}),t.on("markerAdded",this.resizeHandler),t.on("markerCleared",this.resizeHandler),!1!==e.listenForChanges&&t.on("changes",this.changeHandler=function(){i(250)})}t.defineExtension("annotateScrollbar",function(t){return new e(this,t="string"==typeof t?{className:t}:t)}),t.defineOption("scrollButtonHeight",0),e.prototype.computeScale=function(){var t=this.cm,t=(t.getWrapperElement().clientHeight-t.display.barHeight-2*this.buttonHeight)/t.getScrollerElement().scrollHeight;if(t!=this.hScale)return this.hScale=t,!0},e.prototype.update=function(t){this.annotations=t,this.redraw()},e.prototype.redraw=function(t){!1!==t&&this.computeScale();var n=this.cm,e=this.hScale,i=document.createDocumentFragment(),o=this.annotations,r=n.getOption("lineWrapping"),a=r&&1.5*n.defaultTextHeight(),s=null,h=null;function l(t,e){var i;return s!=t.line&&(s=t.line,h=n.getLineHandle(t.line),(i=n.getLineHandleVisualStart(h))!=h)&&(s=n.getLineNumber(i),h=i),h.widgets&&h.widgets.length||r&&h.height>a?n.charCoords(t,"local")[e?"top":"bottom"]:n.heightAtLine(h,"local")+(e?0:h.height)}var d=n.lastLine();if(n.display.barWidth)for(var c,p=0;p<o.length;p++){var u=o[p];if(!(u.to.line>d)){for(var m,f,g=c||l(u.from,!0)*e,H=l(u.to,!1)*e;p<o.length-1&&!(o[p+1].to.line>d)&&!(H+.9<(c=l(o[p+1].from,!0)*e));)H=l((u=o[++p]).to,!1)*e;H!=g&&(m=Math.max(H-g,3),(f=i.appendChild(document.createElement("div"))).style.cssText="position: absolute; right: 0px; width: "+Math.max(n.display.barWidth-1,2)+"px; top: "+(g+this.buttonHeight)+"px; height: "+m+"px",f.className=this.options.className,u.id)&&f.setAttribute("annotation-id",u.id)}}this.div.textContent="",this.div.appendChild(i)},e.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler),this.cm.off("markerAdded",this.resizeHandler),this.cm.off("markerCleared",this.resizeHandler),this.changeHandler&&this.cm.off("changes",this.changeHandler),this.div.parentNode.removeChild(this.div)}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(s){function f(e,o,n){var e=e.getWrapperElement(),t=e.appendChild(document.createElement("div"));return t.className=n?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof o?t.innerHTML=o:t.appendChild(o),s.addClass(e,"dialog-opened"),t}function p(e,o){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=o}s.defineExtension("openDialog",function(e,o,n){n=n||{},p(this,null);var t=f(this,e,n.bottom),i=!1,r=this;function u(e){"string"==typeof e?l.value=e:i||(i=!0,s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t),r.focus(),n.onClose&&n.onClose(t))}var l=t.getElementsByTagName("input")[0];return l?(l.focus(),n.value&&(l.value=n.value,!1!==n.selectValueOnOpen&&l.select()),n.onInput&&s.on(l,"input",function(e){n.onInput(e,l.value,u)}),n.onKeyUp&&s.on(l,"keyup",function(e){n.onKeyUp(e,l.value,u)}),s.on(l,"keydown",function(e){n&&n.onKeyDown&&n.onKeyDown(e,l.value,u)||((27==e.keyCode||!1!==n.closeOnEnter&&13==e.keyCode)&&(l.blur(),s.e_stop(e),u()),13==e.keyCode&&o(l.value,e))}),!1!==n.closeOnBlur&&s.on(t,"focusout",function(e){null!==e.relatedTarget&&u()})):(e=t.getElementsByTagName("button")[0])&&(s.on(e,"click",function(){u(),r.focus()}),!1!==n.closeOnBlur&&s.on(e,"blur",u),e.focus()),u}),s.defineExtension("openConfirm",function(e,o,n){p(this,null);var t=f(this,e,n&&n.bottom),i=t.getElementsByTagName("button"),r=!1,u=this,l=1;function a(){r||(r=!0,s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t),u.focus())}i[0].focus();for(var c=0;c<i.length;++c){var d=i[c];!function(o){s.on(d,"click",function(e){s.e_preventDefault(e),a(),o&&o(u)})}(o[c]),s.on(d,"blur",function(){--l,setTimeout(function(){l<=0&&a()},200)}),s.on(d,"focus",function(){++l})}}),s.defineExtension("openNotification",function(e,o){p(this,r);var n,t=f(this,e,o&&o.bottom),i=!1,e=o&&void 0!==o.duration?o.duration:5e3;function r(){i||(i=!0,clearTimeout(n),s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t))}return s.on(t,"click",function(e){s.e_preventDefault(e),r()}),e&&(n=setTimeout(r,e)),r})});
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(s){function f(e,o,n){var e=e.getWrapperElement(),t=e.appendChild(document.createElement("div"));return t.className=n?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof o?t.innerHTML=o:t.appendChild(o),s.addClass(e,"dialog-opened"),t}function p(e,o){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=o}s.defineExtension("openDialog",function(e,o,n){n=n||{},p(this,null);var t=f(this,e,n.bottom),i=!1,r=this;function u(e){"string"==typeof e?l.value=e:i||(i=!0,s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t),r.focus(),n.onClose&&n.onClose(t))}var l=t.getElementsByTagName("input")[0];return l?(l.focus(),n.value&&(l.value=n.value,!1!==n.selectValueOnOpen)&&l.select(),n.onInput&&s.on(l,"input",function(e){n.onInput(e,l.value,u)}),n.onKeyUp&&s.on(l,"keyup",function(e){n.onKeyUp(e,l.value,u)}),s.on(l,"keydown",function(e){n&&n.onKeyDown&&n.onKeyDown(e,l.value,u)||((27==e.keyCode||!1!==n.closeOnEnter&&13==e.keyCode)&&(l.blur(),s.e_stop(e),u()),13==e.keyCode&&o(l.value,e))}),!1!==n.closeOnBlur&&s.on(t,"focusout",function(e){null!==e.relatedTarget&&u()})):(e=t.getElementsByTagName("button")[0])&&(s.on(e,"click",function(){u(),r.focus()}),!1!==n.closeOnBlur&&s.on(e,"blur",u),e.focus()),u}),s.defineExtension("openConfirm",function(e,o,n){p(this,null);var t=f(this,e,n&&n.bottom),i=t.getElementsByTagName("button"),r=!1,u=this,l=1;function a(){r||(r=!0,s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t),u.focus())}i[0].focus();for(var c=0;c<i.length;++c){var d=i[c];!function(o){s.on(d,"click",function(e){s.e_preventDefault(e),a(),o&&o(u)})}(o[c]),s.on(d,"blur",function(){--l,setTimeout(function(){l<=0&&a()},200)}),s.on(d,"focus",function(){++l})}}),s.defineExtension("openNotification",function(e,o){p(this,r);var n,t=f(this,e,o&&o.bottom),i=!1,e=o&&void 0!==o.duration?o.duration:5e3;function r(){i||(i=!0,clearTimeout(n),s.rmClass(t.parentNode,"dialog-opened"),t.parentNode.removeChild(t))}return s.on(t,"click",function(e){s.e_preventDefault(e),r()}),e&&(n=setTimeout(r,e)),r})});

View File

@ -1 +1 @@
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror"),require("../xml/xml"),require("../javascript/javascript"),require("../css/css")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","../xml/xml","../javascript/javascript","../css/css"],t):t(CodeMirror)}(function(m){"use strict";var l={script:[["lang",/(javascript|babel)/i,"javascript"],["type",/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,"javascript"],["type",/./,"text/plain"],[null,null,"javascript"]],style:[["lang",/^css$/i,"css"],["type",/^(text\/)?(x-)?(stylesheet|css)$/i,"css"],["type",/./,"text/plain"],[null,null,"css"]]};var a={};function d(t,e){e=t.match(a[t=e]||(a[t]=new RegExp("\\s+"+t+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")));return e?/^\s*(.*?)\s*$/.exec(e[2])[1]:""}function g(t,e){return new RegExp((e?"^":"")+"</\\s*"+t+"\\s*>","i")}function o(t,e){for(var a in t)for(var n=e[a]||(e[a]=[]),l=t[a],o=l.length-1;0<=o;o--)n.unshift(l[o])}m.defineMode("htmlmixed",function(i,t){var c=m.getMode(i,{name:"xml",htmlMode:!0,multilineTagIndentFactor:t.multilineTagIndentFactor,multilineTagIndentPastTag:t.multilineTagIndentPastTag,allowMissingTagName:t.allowMissingTagName}),s={},e=t&&t.tags,a=t&&t.scriptTypes;if(o(l,s),e&&o(e,s),a)for(var n=a.length-1;0<=n;n--)s.script.unshift(["type",a[n].matches,a[n].mode]);function u(t,e){var a,o,r,n=c.token(t,e.htmlState),l=/\btag\b/.test(n);return l&&!/[<>\s\/]/.test(t.current())&&(a=e.htmlState.tagName&&e.htmlState.tagName.toLowerCase())&&s.hasOwnProperty(a)?e.inTag=a+" ":e.inTag&&l&&/>$/.test(t.current())?(a=/^([\S]+) (.*)/.exec(e.inTag),e.inTag=null,l=">"==t.current()&&function(t,e){for(var a=0;a<t.length;a++){var n=t[a];if(!n[0]||n[1].test(d(e,n[0])))return n[2]}}(s[a[1]],a[2]),l=m.getMode(i,l),o=g(a[1],!0),r=g(a[1],!1),e.token=function(t,e){return t.match(o,!1)?(e.token=u,e.localState=e.localMode=null):(a=t,n=r,t=e.localMode.token(t,e.localState),e=a.current(),-1<(l=e.search(n))?a.backUp(e.length-l):e.match(/<\/?$/)&&(a.backUp(e.length),a.match(n,!1)||a.match(e)),t);var a,n,l},e.localMode=l,e.localState=m.startState(l,c.indent(e.htmlState,"",""))):e.inTag&&(e.inTag+=t.current(),t.eol()&&(e.inTag+=" ")),n}return{startState:function(){return{token:u,inTag:null,localMode:null,localState:null,htmlState:m.startState(c)}},copyState:function(t){var e;return t.localState&&(e=m.copyState(t.localMode,t.localState)),{token:t.token,inTag:t.inTag,localMode:t.localMode,localState:e,htmlState:m.copyState(c,t.htmlState)}},token:function(t,e){return e.token(t,e)},indent:function(t,e,a){return!t.localMode||/^\s*<\//.test(e)?c.indent(t.htmlState,e,a):t.localMode.indent?t.localMode.indent(t.localState,e,a):m.Pass},innerMode:function(t){return{state:t.localState||t.htmlState,mode:t.localMode||c}}}},"xml","javascript","css"),m.defineMIME("text/html","htmlmixed")});
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror"),require("../xml/xml"),require("../javascript/javascript"),require("../css/css")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","../xml/xml","../javascript/javascript","../css/css"],t):t(CodeMirror)}(function(m){"use strict";var l={script:[["lang",/(javascript|babel)/i,"javascript"],["type",/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,"javascript"],["type",/./,"text/plain"],[null,null,"javascript"]],style:[["lang",/^css$/i,"css"],["type",/^(text\/)?(x-)?(stylesheet|css)$/i,"css"],["type",/./,"text/plain"],[null,null,"css"]]};var a={};function d(t,e){e=t.match(a[t=e]||(a[t]=new RegExp("\\s+"+t+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")));return e?/^\s*(.*?)\s*$/.exec(e[2])[1]:""}function g(t,e){return new RegExp((e?"^":"")+"</\\s*"+t+"\\s*>","i")}function o(t,e){for(var a in t)for(var n=e[a]||(e[a]=[]),l=t[a],o=l.length-1;0<=o;o--)n.unshift(l[o])}m.defineMode("htmlmixed",function(i,t){var c=m.getMode(i,{name:"xml",htmlMode:!0,multilineTagIndentFactor:t.multilineTagIndentFactor,multilineTagIndentPastTag:t.multilineTagIndentPastTag,allowMissingTagName:t.allowMissingTagName}),s={},e=t&&t.tags,a=t&&t.scriptTypes;if(o(l,s),e&&o(e,s),a)for(var n=a.length-1;0<=n;n--)s.script.unshift(["type",a[n].matches,a[n].mode]);function u(t,e){var a,o,r,n=c.token(t,e.htmlState),l=/\btag\b/.test(n);return l&&!/[<>\s\/]/.test(t.current())&&(a=e.htmlState.tagName&&e.htmlState.tagName.toLowerCase())&&s.hasOwnProperty(a)?e.inTag=a+" ":e.inTag&&l&&/>$/.test(t.current())?(a=/^([\S]+) (.*)/.exec(e.inTag),e.inTag=null,l=">"==t.current()&&function(t,e){for(var a=0;a<t.length;a++){var n=t[a];if(!n[0]||n[1].test(d(e,n[0])))return n[2]}}(s[a[1]],a[2]),l=m.getMode(i,l),o=g(a[1],!0),r=g(a[1],!1),e.token=function(t,e){return t.match(o,!1)?(e.token=u,e.localState=e.localMode=null):(a=t,n=r,t=e.localMode.token(t,e.localState),e=a.current(),-1<(l=e.search(n))?a.backUp(e.length-l):e.match(/<\/?$/)&&(a.backUp(e.length),a.match(n,!1)||a.match(e)),t);var a,n,l},e.localMode=l,e.localState=m.startState(l,c.indent(e.htmlState,"",""))):e.inTag&&(e.inTag+=t.current(),t.eol())&&(e.inTag+=" "),n}return{startState:function(){return{token:u,inTag:null,localMode:null,localState:null,htmlState:m.startState(c)}},copyState:function(t){var e;return t.localState&&(e=m.copyState(t.localMode,t.localState)),{token:t.token,inTag:t.inTag,localMode:t.localMode,localState:e,htmlState:m.copyState(c,t.htmlState)}},token:function(t,e){return e.token(t,e)},indent:function(t,e,a){return!t.localMode||/^\s*<\//.test(e)?c.indent(t.htmlState,e,a):t.localMode.indent?t.localMode.indent(t.localState,e,a):m.Pass},innerMode:function(t){return{state:t.localState||t.htmlState,mode:t.localMode||c}}}},"xml","javascript","css"),m.defineMIME("text/html","htmlmixed")});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,20 +1,20 @@
LINKS="
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/dialog/dialog.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/dialog/dialog.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/edit/trailingspace.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/lint/javascript-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/scroll/annotatescrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/search/matchesonscrollbar.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/search/matchesonscrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/search/search.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/addon/search/searchcursor.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/codemirror.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/codemirror.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/mode/css/css.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/mode/htmlmixed/htmlmixed.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/mode/javascript/javascript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/mode/xml/xml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.12/theme/base16-dark.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/dialog/dialog.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/dialog/dialog.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/edit/trailingspace.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/lint/javascript-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/scroll/annotatescrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/search/matchesonscrollbar.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/search/matchesonscrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/search/search.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/addon/search/searchcursor.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/codemirror.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/codemirror.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/mode/css/css.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/mode/htmlmixed/htmlmixed.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/mode/javascript/javascript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/mode/xml/xml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.14/theme/base16-dark.min.css
"
for link in $LINKS; do

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,251 +0,0 @@
#!/usr/bin/env perl
# WARNING: do not edit!
# Generated by Makefile from tools/c_rehash.in
# Copyright 1999-2022 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
# Perl c_rehash script, scan all files in a directory
# and add symbolic links to their hash values.
my $dir = "";
my $prefix = "";
my $errorcount = 0;
my $openssl = $ENV{OPENSSL} || "openssl";
my $pwd;
my $x509hash = "-subject_hash";
my $crlhash = "-hash";
my $verbose = 0;
my $symlink_exists=eval {symlink("",""); 1};
my $removelinks = 1;
## Parse flags.
while ( $ARGV[0] =~ /^-/ ) {
my $flag = shift @ARGV;
last if ( $flag eq '--');
if ( $flag eq '-old') {
$x509hash = "-subject_hash_old";
$crlhash = "-hash_old";
} elsif ( $flag eq '-h' || $flag eq '-help' ) {
help();
} elsif ( $flag eq '-n' ) {
$removelinks = 0;
} elsif ( $flag eq '-v' ) {
$verbose++;
}
else {
print STDERR "Usage error; try -h.\n";
exit 1;
}
}
sub help {
print "Usage: c_rehash [-old] [-h] [-help] [-v] [dirs...]\n";
print " -old use old-style digest\n";
print " -h or -help print this help text\n";
print " -v print files removed and linked\n";
exit 0;
}
eval "require Cwd";
if (defined(&Cwd::getcwd)) {
$pwd=Cwd::getcwd();
} else {
$pwd=`pwd`;
chomp($pwd);
}
# DOS/Win32 or Unix delimiter? Prefix our installdir, then search.
my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':';
$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : "");
if (! -x $openssl) {
my $found = 0;
foreach (split /$path_delim/, $ENV{PATH}) {
if (-x "$_/$openssl") {
$found = 1;
$openssl = "$_/$openssl";
last;
}
}
if ($found == 0) {
print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n";
exit 0;
}
}
if (@ARGV) {
@dirlist = @ARGV;
} elsif ($ENV{SSL_CERT_DIR}) {
@dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR};
} else {
$dirlist[0] = "$dir/certs";
}
if (-d $dirlist[0]) {
chdir $dirlist[0];
$openssl="$pwd/$openssl" if (!-x $openssl);
chdir $pwd;
}
foreach (@dirlist) {
if (-d $_ ) {
if ( -w $_) {
hash_dir($_);
} else {
print "Skipping $_, can't write\n";
$errorcount++;
}
}
}
exit($errorcount);
sub copy_file {
my ($src_fname, $dst_fname) = @_;
if (open(my $in, "<", $src_fname)) {
if (open(my $out, ">", $dst_fname)) {
print $out $_ while (<$in>);
close $out;
} else {
warn "Cannot open $dst_fname for write, $!";
}
close $in;
} else {
warn "Cannot open $src_fname for read, $!";
}
}
sub hash_dir {
my $dir = shift;
my %hashlist;
print "Doing $dir\n";
if (!chdir $dir) {
print STDERR "WARNING: Cannot chdir to '$dir', $!\n";
return;
}
opendir(DIR, ".") || print STDERR "WARNING: Cannot opendir '.', $!\n";
my @flist = sort readdir(DIR);
closedir DIR;
if ( $removelinks ) {
# Delete any existing symbolic links
foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
if (-l $_) {
print "unlink $_\n" if $verbose;
unlink $_ || warn "Can't unlink $_, $!\n";
}
}
}
FILE: foreach $fname (grep {/\.(pem)|(crt)|(cer)|(crl)$/} @flist) {
# Check to see if certificates and/or CRLs present.
my ($cert, $crl) = check_file($fname);
if (!$cert && !$crl) {
print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
next;
}
link_hash_cert($fname) if ($cert);
link_hash_crl($fname) if ($crl);
}
chdir $pwd;
}
sub check_file {
my ($is_cert, $is_crl) = (0,0);
my $fname = $_[0];
open(my $in, "<", $fname);
while(<$in>) {
if (/^-----BEGIN (.*)-----/) {
my $hdr = $1;
if ($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) {
$is_cert = 1;
last if ($is_crl);
} elsif ($hdr eq "X509 CRL") {
$is_crl = 1;
last if ($is_cert);
}
}
}
close $in;
return ($is_cert, $is_crl);
}
sub compute_hash {
my $fh;
if ( $^O eq "VMS" ) {
# VMS uses the open through shell
# The file names are safe there and list form is unsupported
if (!open($fh, "-|", join(' ', @_))) {
print STDERR "Cannot compute hash on '$fname'\n";
return;
}
} else {
if (!open($fh, "-|", @_)) {
print STDERR "Cannot compute hash on '$fname'\n";
return;
}
}
return (<$fh>, <$fh>);
}
# Link a certificate to its subject name hash value, each hash is of
# the form <hash>.<n> where n is an integer. If the hash value already exists
# then we need to up the value of n, unless its a duplicate in which
# case we skip the link. We check for duplicates by comparing the
# certificate fingerprints
sub link_hash_cert {
link_hash($_[0], 'cert');
}
# Same as above except for a CRL. CRL links are of the form <hash>.r<n>
sub link_hash_crl {
link_hash($_[0], 'crl');
}
sub link_hash {
my ($fname, $type) = @_;
my $is_cert = $type eq 'cert';
my ($hash, $fprint) = compute_hash($openssl,
$is_cert ? "x509" : "crl",
$is_cert ? $x509hash : $crlhash,
"-fingerprint", "-noout",
"-in", $fname);
chomp $hash;
chomp $fprint;
return if !$hash;
$fprint =~ s/^.*=//;
$fprint =~ tr/://d;
my $suffix = 0;
# Search for an unused hash filename
my $crlmark = $is_cert ? "" : "r";
while(exists $hashlist{"$hash.$crlmark$suffix"}) {
# Hash matches: if fingerprint matches its a duplicate cert
if ($hashlist{"$hash.$crlmark$suffix"} eq $fprint) {
my $what = $is_cert ? 'certificate' : 'CRL';
print STDERR "WARNING: Skipping duplicate $what $fname\n";
return;
}
$suffix++;
}
$hash .= ".$crlmark$suffix";
if ($symlink_exists) {
print "link $fname -> $hash\n" if $verbose;
symlink $fname, $hash || warn "Can't symlink, $!";
} else {
print "copy $fname -> $hash\n" if $verbose;
copy_file($fname, $hash);
}
$hashlist{$hash} = $fprint;
}

View File

@ -0,0 +1,16 @@
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* This file is only used by HP C on VMS, and is included automatically
* after each header file from this directory
*/
/* restore state. Must correspond to the save in __decc_include_prologue.h */
#pragma names restore

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* This file is only used by HP C on VMS, and is included automatically
* after each header file from this directory
*/
/* save state */
#pragma names save
/* have the compiler shorten symbols larger than 31 chars to 23 chars
* followed by a 8 hex char CRC
*/
#pragma names as_is,shortened

View File

@ -0,0 +1,50 @@
/*
* Copyright 2006-2018 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/* Copyright (c) 2017 National Security Research Institute. All rights reserved. */
#ifndef OSSL_CRYPTO_ARIA_H
# define OSSL_CRYPTO_ARIA_H
# include <openssl/opensslconf.h>
# ifdef OPENSSL_NO_ARIA
# error ARIA is disabled.
# endif
# define ARIA_ENCRYPT 1
# define ARIA_DECRYPT 0
# define ARIA_BLOCK_SIZE 16 /* Size of each encryption/decryption block */
# define ARIA_MAX_KEYS 17 /* Number of keys needed in the worst case */
typedef union {
unsigned char c[ARIA_BLOCK_SIZE];
unsigned int u[ARIA_BLOCK_SIZE / sizeof(unsigned int)];
} ARIA_u128;
typedef unsigned char ARIA_c128[ARIA_BLOCK_SIZE];
struct aria_key_st {
ARIA_u128 rd_key[ARIA_MAX_KEYS];
unsigned int rounds;
};
typedef struct aria_key_st ARIA_KEY;
int aria_set_encrypt_key(const unsigned char *userKey, const int bits,
ARIA_KEY *key);
int aria_set_decrypt_key(const unsigned char *userKey, const int bits,
ARIA_KEY *key);
void aria_encrypt(const unsigned char *in, unsigned char *out,
const ARIA_KEY *key);
#endif

View File

@ -0,0 +1,113 @@
/*
* Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/* Internal ASN1 structures and functions: not for application use */
/* ASN1 public key method structure */
struct evp_pkey_asn1_method_st {
int pkey_id;
int pkey_base_id;
unsigned long pkey_flags;
char *pem_str;
char *info;
int (*pub_decode) (EVP_PKEY *pk, X509_PUBKEY *pub);
int (*pub_encode) (X509_PUBKEY *pub, const EVP_PKEY *pk);
int (*pub_cmp) (const EVP_PKEY *a, const EVP_PKEY *b);
int (*pub_print) (BIO *out, const EVP_PKEY *pkey, int indent,
ASN1_PCTX *pctx);
int (*priv_decode) (EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf);
int (*priv_encode) (PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk);
int (*priv_print) (BIO *out, const EVP_PKEY *pkey, int indent,
ASN1_PCTX *pctx);
int (*pkey_size) (const EVP_PKEY *pk);
int (*pkey_bits) (const EVP_PKEY *pk);
int (*pkey_security_bits) (const EVP_PKEY *pk);
int (*param_decode) (EVP_PKEY *pkey,
const unsigned char **pder, int derlen);
int (*param_encode) (const EVP_PKEY *pkey, unsigned char **pder);
int (*param_missing) (const EVP_PKEY *pk);
int (*param_copy) (EVP_PKEY *to, const EVP_PKEY *from);
int (*param_cmp) (const EVP_PKEY *a, const EVP_PKEY *b);
int (*param_print) (BIO *out, const EVP_PKEY *pkey, int indent,
ASN1_PCTX *pctx);
int (*sig_print) (BIO *out,
const X509_ALGOR *sigalg, const ASN1_STRING *sig,
int indent, ASN1_PCTX *pctx);
void (*pkey_free) (EVP_PKEY *pkey);
int (*pkey_ctrl) (EVP_PKEY *pkey, int op, long arg1, void *arg2);
/* Legacy functions for old PEM */
int (*old_priv_decode) (EVP_PKEY *pkey,
const unsigned char **pder, int derlen);
int (*old_priv_encode) (const EVP_PKEY *pkey, unsigned char **pder);
/* Custom ASN1 signature verification */
int (*item_verify) (EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
X509_ALGOR *a, ASN1_BIT_STRING *sig, EVP_PKEY *pkey);
int (*item_sign) (EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
X509_ALGOR *alg1, X509_ALGOR *alg2,
ASN1_BIT_STRING *sig);
int (*siginf_set) (X509_SIG_INFO *siginf, const X509_ALGOR *alg,
const ASN1_STRING *sig);
/* Check */
int (*pkey_check) (const EVP_PKEY *pk);
int (*pkey_public_check) (const EVP_PKEY *pk);
int (*pkey_param_check) (const EVP_PKEY *pk);
/* Get/set raw private/public key data */
int (*set_priv_key) (EVP_PKEY *pk, const unsigned char *priv, size_t len);
int (*set_pub_key) (EVP_PKEY *pk, const unsigned char *pub, size_t len);
int (*get_priv_key) (const EVP_PKEY *pk, unsigned char *priv, size_t *len);
int (*get_pub_key) (const EVP_PKEY *pk, unsigned char *pub, size_t *len);
} /* EVP_PKEY_ASN1_METHOD */ ;
DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)
extern const EVP_PKEY_ASN1_METHOD cmac_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD dh_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD dhx_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5];
extern const EVP_PKEY_ASN1_METHOD eckey_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ecx448_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ed448_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD sm2_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD poly1305_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2];
extern const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD siphash_asn1_meth;
/*
* These are used internally in the ASN1_OBJECT to keep track of whether the
* names and data need to be free()ed
*/
# define ASN1_OBJECT_FLAG_DYNAMIC 0x01/* internal use */
# define ASN1_OBJECT_FLAG_CRITICAL 0x02/* critical x509v3 object id */
# define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04/* internal use */
# define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08/* internal use */
struct asn1_object_st {
const char *sn, *ln;
int nid;
int length;
const unsigned char *data; /* data remains const after init */
int flags; /* Should we free this one */
};
/* ASN1 print context structure */
struct asn1_pctx_st {
unsigned long flags;
unsigned long nm_flags;
unsigned long cert_flags;
unsigned long oid_flags;
unsigned long str_flags;
} /* ASN1_PCTX */ ;
int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);

View File

@ -0,0 +1,15 @@
/*
* Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/async.h>
int async_init(void);
void async_deinit(void);
void async_delete_thread_state(void);

View File

@ -0,0 +1,90 @@
/*
* Copyright 2014-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_BN_H
# define OSSL_CRYPTO_BN_H
# include <openssl/bn.h>
# include <limits.h>
BIGNUM *bn_wexpand(BIGNUM *a, int words);
BIGNUM *bn_expand2(BIGNUM *a, int words);
void bn_correct_top(BIGNUM *a);
/*
* Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
* This is an array r[] of values that are either zero or odd with an
* absolute value less than 2^w satisfying scalar = \sum_j r[j]*2^j where at
* most one of any w+1 consecutive digits is non-zero with the exception that
* the most significant digit may be only w-1 zeros away from that next
* non-zero digit.
*/
signed char *bn_compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len);
int bn_get_top(const BIGNUM *a);
int bn_get_dmax(const BIGNUM *a);
/* Set all words to zero */
void bn_set_all_zero(BIGNUM *a);
/*
* Copy the internal BIGNUM words into out which holds size elements (and size
* must be bigger than top)
*/
int bn_copy_words(BN_ULONG *out, const BIGNUM *in, int size);
BN_ULONG *bn_get_words(const BIGNUM *a);
/*
* Set the internal data words in a to point to words which contains size
* elements. The BN_FLG_STATIC_DATA flag is set
*/
void bn_set_static_words(BIGNUM *a, const BN_ULONG *words, int size);
/*
* Copy words into the BIGNUM |a|, reallocating space as necessary.
* The negative flag of |a| is not modified.
* Returns 1 on success and 0 on failure.
*/
/*
* |num_words| is int because bn_expand2 takes an int. This is an internal
* function so we simply trust callers not to pass negative values.
*/
int bn_set_words(BIGNUM *a, const BN_ULONG *words, int num_words);
/*
* Some BIGNUM functions assume most significant limb to be non-zero, which
* is customarily arranged by bn_correct_top. Output from below functions
* is not processed with bn_correct_top, and for this reason it may not be
* returned out of public API. It may only be passed internally into other
* functions known to support non-minimal or zero-padded BIGNUMs. Even
* though the goal is to facilitate constant-time-ness, not each subroutine
* is constant-time by itself. They all have pre-conditions, consult source
* code...
*/
int bn_mul_mont_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
BN_MONT_CTX *mont, BN_CTX *ctx);
int bn_to_mont_fixed_top(BIGNUM *r, const BIGNUM *a, BN_MONT_CTX *mont,
BN_CTX *ctx);
int bn_from_mont_fixed_top(BIGNUM *r, const BIGNUM *a, BN_MONT_CTX *mont,
BN_CTX *ctx);
int bn_mod_add_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m);
int bn_mod_sub_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m);
int bn_mul_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
int bn_sqr_fixed_top(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx);
int bn_lshift_fixed_top(BIGNUM *r, const BIGNUM *a, int n);
int bn_rshift_fixed_top(BIGNUM *r, const BIGNUM *a, int n);
int bn_div_fixed_top(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m,
const BIGNUM *d, BN_CTX *ctx);
#endif

View File

@ -0,0 +1,28 @@
/* WARNING: do not edit! */
/* Generated by Makefile from include/crypto/bn_conf.h.in */
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_BN_CONF_H
# define OSSL_CRYPTO_BN_CONF_H
/*
* The contents of this file are not used in the UEFI build, as
* both 32-bit and 64-bit builds are supported from a single run
* of the Configure script.
*/
/* Should we define BN_DIV2W here? */
/* Only one for the following should be defined */
#define SIXTY_FOUR_BIT_LONG
#undef SIXTY_FOUR_BIT
#undef THIRTY_TWO_BIT
#endif

View File

@ -0,0 +1,27 @@
{- join("\n",map { "/* $_ */" } @autowarntext) -}
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_BN_CONF_H
# define OSSL_CRYPTO_BN_CONF_H
/*
* The contents of this file are not used in the UEFI build, as
* both 32-bit and 64-bit builds are supported from a single run
* of the Configure script.
*/
/* Should we define BN_DIV2W here? */
/* Only one for the following should be defined */
{- $config{b64l} ? "#define" : "#undef" -} SIXTY_FOUR_BIT_LONG
{- $config{b64} ? "#define" : "#undef" -} SIXTY_FOUR_BIT
{- $config{b32} ? "#define" : "#undef" -} THIRTY_TWO_BIT
#endif

View File

@ -0,0 +1,24 @@
/*
* Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#define declare_dh_bn(x) \
extern const BIGNUM _bignum_dh##x##_p; \
extern const BIGNUM _bignum_dh##x##_g; \
extern const BIGNUM _bignum_dh##x##_q;
declare_dh_bn(1024_160)
declare_dh_bn(2048_224)
declare_dh_bn(2048_256)
extern const BIGNUM _bignum_ffdhe2048_p;
extern const BIGNUM _bignum_ffdhe3072_p;
extern const BIGNUM _bignum_ffdhe4096_p;
extern const BIGNUM _bignum_ffdhe6144_p;
extern const BIGNUM _bignum_ffdhe8192_p;
extern const BIGNUM _bignum_const_2;

View File

@ -0,0 +1,32 @@
/*
* Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OPENSSL_NO_SRP
extern const BIGNUM bn_group_1024;
extern const BIGNUM bn_group_1536;
extern const BIGNUM bn_group_2048;
extern const BIGNUM bn_group_3072;
extern const BIGNUM bn_group_4096;
extern const BIGNUM bn_group_6144;
extern const BIGNUM bn_group_8192;
extern const BIGNUM bn_generator_19;
extern const BIGNUM bn_generator_5;
extern const BIGNUM bn_generator_2;
#endif

View File

@ -0,0 +1,42 @@
/*
* Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_CHACHA_H
#define OSSL_CRYPTO_CHACHA_H
#include <stddef.h>
/*
* ChaCha20_ctr32 encrypts |len| bytes from |inp| with the given key and
* nonce and writes the result to |out|, which may be equal to |inp|.
* The |key| is not 32 bytes of verbatim key material though, but the
* said material collected into 8 32-bit elements array in host byte
* order. Same approach applies to nonce: the |counter| argument is
* pointer to concatenated nonce and counter values collected into 4
* 32-bit elements. This, passing crypto material collected into 32-bit
* elements as opposite to passing verbatim byte vectors, is chosen for
* efficiency in multi-call scenarios.
*/
void ChaCha20_ctr32(unsigned char *out, const unsigned char *inp,
size_t len, const unsigned int key[8],
const unsigned int counter[4]);
/*
* You can notice that there is no key setup procedure. Because it's
* as trivial as collecting bytes into 32-bit elements, it's reckoned
* that below macro is sufficient.
*/
#define CHACHA_U8TOU32(p) ( \
((unsigned int)(p)[0]) | ((unsigned int)(p)[1]<<8) | \
((unsigned int)(p)[2]<<16) | ((unsigned int)(p)[3]<<24) )
#define CHACHA_KEY_SIZE 32
#define CHACHA_CTR_SIZE 16
#define CHACHA_BLK_SIZE 64
#endif

View File

@ -0,0 +1,35 @@
/*
* Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/cryptlib.h"
/* This file is not scanned by mkdef.pl, whereas cryptlib.h is */
struct thread_local_inits_st {
int async;
int err_state;
int rand;
};
int ossl_init_thread_start(uint64_t opts);
/*
* OPENSSL_INIT flags. The primary list of these is in crypto.h. Flags below
* are those omitted from crypto.h because they are "reserved for internal
* use".
*/
# define OPENSSL_INIT_ZLIB 0x00010000L
# define OPENSSL_INIT_BASE_ONLY 0x00040000L
/* OPENSSL_INIT_THREAD flags */
# define OPENSSL_INIT_THREAD_ASYNC 0x01
# define OPENSSL_INIT_THREAD_ERR_STATE 0x02
# define OPENSSL_INIT_THREAD_RAND 0x04
void ossl_malloc_setup_failures(void);

View File

@ -0,0 +1,82 @@
/*
* Copyright 2017-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* This version of ctype.h provides a standardised and platform
* independent implementation that supports seven bit ASCII characters.
* The specific intent is to not pass extended ASCII characters (> 127)
* even if the host operating system would.
*
* There is EBCDIC support included for machines which use this. However,
* there are a number of concerns about how well EBCDIC is supported
* throughout the rest of the source code. Refer to issue #4154 for
* details.
*/
#ifndef OSSL_CRYPTO_CTYPE_H
# define OSSL_CRYPTO_CTYPE_H
# define CTYPE_MASK_lower 0x1
# define CTYPE_MASK_upper 0x2
# define CTYPE_MASK_digit 0x4
# define CTYPE_MASK_space 0x8
# define CTYPE_MASK_xdigit 0x10
# define CTYPE_MASK_blank 0x20
# define CTYPE_MASK_cntrl 0x40
# define CTYPE_MASK_graph 0x80
# define CTYPE_MASK_print 0x100
# define CTYPE_MASK_punct 0x200
# define CTYPE_MASK_base64 0x400
# define CTYPE_MASK_asn1print 0x800
# define CTYPE_MASK_alpha (CTYPE_MASK_lower | CTYPE_MASK_upper)
# define CTYPE_MASK_alnum (CTYPE_MASK_alpha | CTYPE_MASK_digit)
/*
* The ascii mask assumes that any other classification implies that
* the character is ASCII and that there are no ASCII characters
* that aren't in any of the classifications.
*
* This assumption holds at the moment, but it might not in the future.
*/
# define CTYPE_MASK_ascii (~0)
# ifdef CHARSET_EBCDIC
int ossl_toascii(int c);
int ossl_fromascii(int c);
# else
# define ossl_toascii(c) (c)
# define ossl_fromascii(c) (c)
# endif
int ossl_ctype_check(int c, unsigned int mask);
int ossl_tolower(int c);
int ossl_toupper(int c);
int ascii_isdigit(const char inchar);
# define ossl_isalnum(c) (ossl_ctype_check((c), CTYPE_MASK_alnum))
# define ossl_isalpha(c) (ossl_ctype_check((c), CTYPE_MASK_alpha))
# ifdef CHARSET_EBCDIC
# define ossl_isascii(c) (ossl_ctype_check((c), CTYPE_MASK_ascii))
# else
# define ossl_isascii(c) (((c) & ~127) == 0)
# endif
# define ossl_isblank(c) (ossl_ctype_check((c), CTYPE_MASK_blank))
# define ossl_iscntrl(c) (ossl_ctype_check((c), CTYPE_MASK_cntrl))
# define ossl_isdigit(c) (ossl_ctype_check((c), CTYPE_MASK_digit))
# define ossl_isgraph(c) (ossl_ctype_check((c), CTYPE_MASK_graph))
# define ossl_islower(c) (ossl_ctype_check((c), CTYPE_MASK_lower))
# define ossl_isprint(c) (ossl_ctype_check((c), CTYPE_MASK_print))
# define ossl_ispunct(c) (ossl_ctype_check((c), CTYPE_MASK_punct))
# define ossl_isspace(c) (ossl_ctype_check((c), CTYPE_MASK_space))
# define ossl_isupper(c) (ossl_ctype_check((c), CTYPE_MASK_upper))
# define ossl_isxdigit(c) (ossl_ctype_check((c), CTYPE_MASK_xdigit))
# define ossl_isbase64(c) (ossl_ctype_check((c), CTYPE_MASK_base64))
# define ossl_isasn1print(c) (ossl_ctype_check((c), CTYPE_MASK_asn1print))
#endif

View File

@ -0,0 +1,17 @@
/* WARNING: do not edit! */
/* Generated by Makefile from include/crypto/dso_conf.h.in */
/*
* Copyright 2016-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_DSO_CONF_H
# define OSSL_CRYPTO_DSO_CONF_H
# define DSO_DLFCN
# define HAVE_DLFCN_H
# define DSO_EXTENSION ".so"
#endif

View File

@ -0,0 +1,31 @@
{- join("\n",map { "/* $_ */" } @autowarntext) -}
/*
* Copyright 2016-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_DSO_CONF_H
# define OSSL_CRYPTO_DSO_CONF_H
{- # The DSO code currently always implements all functions so that no
# applications will have to worry about that from a compilation point
# of view. However, the "method"s may return zero unless that platform
# has support compiled in for them. Currently each method is enabled
# by a define "DSO_<name>" ... we translate the "dso_scheme" config
# string entry into using the following logic;
my $scheme = $disabled{dso} ? undef : uc $target{dso_scheme};
if (!$scheme) {
$scheme = "NONE";
}
my @macros = ( "DSO_$scheme" );
if ($scheme eq 'DLFCN') {
@macros = ( "DSO_DLFCN", "HAVE_DLFCN_H" );
} elsif ($scheme eq "DLFCN_NO_H") {
@macros = ( "DSO_DLFCN" );
}
join("\n", map { "# define $_" } @macros); -}
# define DSO_EXTENSION "{- $target{dso_extension} -}"
#endif

View File

@ -0,0 +1,53 @@
/*
* Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/* Internal EC functions for other submodules: not for application use */
#ifndef OSSL_CRYPTO_EC_H
# define OSSL_CRYPTO_EC_H
# include <openssl/opensslconf.h>
# ifndef OPENSSL_NO_EC
# include <openssl/ec.h>
/*-
* Computes the multiplicative inverse of x in the range
* [1,EC_GROUP::order), where EC_GROUP::order is the cardinality of the
* subgroup generated by the generator G:
*
* res := x^(-1) (mod EC_GROUP::order).
*
* This function expects the following two conditions to hold:
* - the EC_GROUP order is prime, and
* - x is included in the range [1, EC_GROUP::order).
*
* This function returns 1 on success, 0 on error.
*
* If the EC_GROUP order is even, this function explicitly returns 0 as
* an error.
* In case any of the two conditions stated above is not satisfied,
* the correctness of its output is not guaranteed, even if the return
* value could still be 1 (as primality testing and a conditional modular
* reduction round on the input can be omitted by the underlying
* implementations for better SCA properties on regular input values).
*/
__owur int ec_group_do_inverse_ord(const EC_GROUP *group, BIGNUM *res,
const BIGNUM *x, BN_CTX *ctx);
/*-
* ECDH Key Derivation Function as defined in ANSI X9.63
*/
int ecdh_KDF_X9_63(unsigned char *out, size_t outlen,
const unsigned char *Z, size_t Zlen,
const unsigned char *sinfo, size_t sinfolen,
const EVP_MD *md);
# endif /* OPENSSL_NO_EC */
#endif

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/engine.h>
void engine_load_openssl_int(void);
void engine_load_devcrypto_int(void);
void engine_load_rdrand_int(void);
void engine_load_dynamic_int(void);
void engine_load_padlock_int(void);
void engine_load_capi_int(void);
void engine_load_dasync_int(void);
void engine_load_afalg_int(void);
void engine_cleanup_int(void);

View File

@ -0,0 +1,19 @@
/*
* Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_ERR_H
# define OSSL_CRYPTO_ERR_H
int err_load_crypto_strings_int(void);
void err_cleanup(void);
void err_delete_thread_state(void);
int err_shelve_state(void **);
void err_unshelve_state(void *);
#endif

View File

@ -0,0 +1,442 @@
/*
* Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/evp.h>
#include "internal/refcount.h"
/*
* Don't free up md_ctx->pctx in EVP_MD_CTX_reset, use the reserved flag
* values in evp.h
*/
#define EVP_MD_CTX_FLAG_KEEP_PKEY_CTX 0x0400
struct evp_pkey_ctx_st {
/* Method associated with this operation */
const EVP_PKEY_METHOD *pmeth;
/* Engine that implements this method or NULL if builtin */
ENGINE *engine;
/* Key: may be NULL */
EVP_PKEY *pkey;
/* Peer key for key agreement, may be NULL */
EVP_PKEY *peerkey;
/* Actual operation */
int operation;
/* Algorithm specific data */
void *data;
/* Application specific data */
void *app_data;
/* Keygen callback */
EVP_PKEY_gen_cb *pkey_gencb;
/* implementation specific keygen data */
int *keygen_info;
int keygen_info_count;
} /* EVP_PKEY_CTX */ ;
#define EVP_PKEY_FLAG_DYNAMIC 1
struct evp_pkey_method_st {
int pkey_id;
int flags;
int (*init) (EVP_PKEY_CTX *ctx);
int (*copy) (EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
void (*cleanup) (EVP_PKEY_CTX *ctx);
int (*paramgen_init) (EVP_PKEY_CTX *ctx);
int (*paramgen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
int (*keygen_init) (EVP_PKEY_CTX *ctx);
int (*keygen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
int (*sign_init) (EVP_PKEY_CTX *ctx);
int (*sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen);
int (*verify_init) (EVP_PKEY_CTX *ctx);
int (*verify) (EVP_PKEY_CTX *ctx,
const unsigned char *sig, size_t siglen,
const unsigned char *tbs, size_t tbslen);
int (*verify_recover_init) (EVP_PKEY_CTX *ctx);
int (*verify_recover) (EVP_PKEY_CTX *ctx,
unsigned char *rout, size_t *routlen,
const unsigned char *sig, size_t siglen);
int (*signctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
int (*signctx) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
EVP_MD_CTX *mctx);
int (*verifyctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
int (*verifyctx) (EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen,
EVP_MD_CTX *mctx);
int (*encrypt_init) (EVP_PKEY_CTX *ctx);
int (*encrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
int (*decrypt_init) (EVP_PKEY_CTX *ctx);
int (*decrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
int (*derive_init) (EVP_PKEY_CTX *ctx);
int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value);
int (*digestsign) (EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen);
int (*digestverify) (EVP_MD_CTX *ctx, const unsigned char *sig,
size_t siglen, const unsigned char *tbs,
size_t tbslen);
int (*check) (EVP_PKEY *pkey);
int (*public_check) (EVP_PKEY *pkey);
int (*param_check) (EVP_PKEY *pkey);
int (*digest_custom) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
} /* EVP_PKEY_METHOD */ ;
DEFINE_STACK_OF_CONST(EVP_PKEY_METHOD)
void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx);
extern const EVP_PKEY_METHOD cmac_pkey_meth;
extern const EVP_PKEY_METHOD dh_pkey_meth;
extern const EVP_PKEY_METHOD dhx_pkey_meth;
extern const EVP_PKEY_METHOD dsa_pkey_meth;
extern const EVP_PKEY_METHOD ec_pkey_meth;
extern const EVP_PKEY_METHOD sm2_pkey_meth;
extern const EVP_PKEY_METHOD ecx25519_pkey_meth;
extern const EVP_PKEY_METHOD ecx448_pkey_meth;
extern const EVP_PKEY_METHOD ed25519_pkey_meth;
extern const EVP_PKEY_METHOD ed448_pkey_meth;
extern const EVP_PKEY_METHOD hmac_pkey_meth;
extern const EVP_PKEY_METHOD rsa_pkey_meth;
extern const EVP_PKEY_METHOD rsa_pss_pkey_meth;
extern const EVP_PKEY_METHOD scrypt_pkey_meth;
extern const EVP_PKEY_METHOD tls1_prf_pkey_meth;
extern const EVP_PKEY_METHOD hkdf_pkey_meth;
extern const EVP_PKEY_METHOD poly1305_pkey_meth;
extern const EVP_PKEY_METHOD siphash_pkey_meth;
struct evp_md_st {
int type;
int pkey_type;
int md_size;
unsigned long flags;
int (*init) (EVP_MD_CTX *ctx);
int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
int (*final) (EVP_MD_CTX *ctx, unsigned char *md);
int (*copy) (EVP_MD_CTX *to, const EVP_MD_CTX *from);
int (*cleanup) (EVP_MD_CTX *ctx);
int block_size;
int ctx_size; /* how big does the ctx->md_data need to be */
/* control function */
int (*md_ctrl) (EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
} /* EVP_MD */ ;
struct evp_cipher_st {
int nid;
int block_size;
/* Default value for variable length ciphers */
int key_len;
int iv_len;
/* Various flags */
unsigned long flags;
/* init key */
int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,
const unsigned char *iv, int enc);
/* encrypt/decrypt data */
int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t inl);
/* cleanup ctx */
int (*cleanup) (EVP_CIPHER_CTX *);
/* how big ctx->cipher_data needs to be */
int ctx_size;
/* Populate a ASN1_TYPE with parameters */
int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
/* Get parameters from a ASN1_TYPE */
int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
/* Miscellaneous operations */
int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr);
/* Application data */
void *app_data;
} /* EVP_CIPHER */ ;
/* Macros to code block cipher wrappers */
/* Wrapper functions for each cipher mode */
#define EVP_C_DATA(kstruct, ctx) \
((kstruct *)EVP_CIPHER_CTX_get_cipher_data(ctx))
#define BLOCK_CIPHER_ecb_loop() \
size_t i, bl; \
bl = EVP_CIPHER_CTX_cipher(ctx)->block_size; \
if (inl < bl) return 1;\
inl -= bl; \
for (i=0; i <= inl; i+=bl)
#define BLOCK_CIPHER_func_ecb(cname, cprefix, kstruct, ksched) \
static int cname##_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
{\
BLOCK_CIPHER_ecb_loop() \
cprefix##_ecb_encrypt(in + i, out + i, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_encrypting(ctx)); \
return 1;\
}
#define EVP_MAXCHUNK ((size_t)1<<(sizeof(long)*8-2))
#define BLOCK_CIPHER_func_ofb(cname, cprefix, cbits, kstruct, ksched) \
static int cname##_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
{\
while(inl>=EVP_MAXCHUNK) {\
int num = EVP_CIPHER_CTX_num(ctx);\
cprefix##_ofb##cbits##_encrypt(in, out, (long)EVP_MAXCHUNK, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), &num); \
EVP_CIPHER_CTX_set_num(ctx, num);\
inl-=EVP_MAXCHUNK;\
in +=EVP_MAXCHUNK;\
out+=EVP_MAXCHUNK;\
}\
if (inl) {\
int num = EVP_CIPHER_CTX_num(ctx);\
cprefix##_ofb##cbits##_encrypt(in, out, (long)inl, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), &num); \
EVP_CIPHER_CTX_set_num(ctx, num);\
}\
return 1;\
}
#define BLOCK_CIPHER_func_cbc(cname, cprefix, kstruct, ksched) \
static int cname##_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
{\
while(inl>=EVP_MAXCHUNK) \
{\
cprefix##_cbc_encrypt(in, out, (long)EVP_MAXCHUNK, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), EVP_CIPHER_CTX_encrypting(ctx));\
inl-=EVP_MAXCHUNK;\
in +=EVP_MAXCHUNK;\
out+=EVP_MAXCHUNK;\
}\
if (inl)\
cprefix##_cbc_encrypt(in, out, (long)inl, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), EVP_CIPHER_CTX_encrypting(ctx));\
return 1;\
}
#define BLOCK_CIPHER_func_cfb(cname, cprefix, cbits, kstruct, ksched) \
static int cname##_cfb##cbits##_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
{\
size_t chunk = EVP_MAXCHUNK;\
if (cbits == 1) chunk >>= 3;\
if (inl < chunk) chunk = inl;\
while (inl && inl >= chunk)\
{\
int num = EVP_CIPHER_CTX_num(ctx);\
cprefix##_cfb##cbits##_encrypt(in, out, (long) \
((cbits == 1) \
&& !EVP_CIPHER_CTX_test_flags(ctx, EVP_CIPH_FLAG_LENGTH_BITS) \
? chunk*8 : chunk), \
&EVP_C_DATA(kstruct, ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx),\
&num, EVP_CIPHER_CTX_encrypting(ctx));\
EVP_CIPHER_CTX_set_num(ctx, num);\
inl -= chunk;\
in += chunk;\
out += chunk;\
if (inl < chunk) chunk = inl;\
}\
return 1;\
}
#define BLOCK_CIPHER_all_funcs(cname, cprefix, cbits, kstruct, ksched) \
BLOCK_CIPHER_func_cbc(cname, cprefix, kstruct, ksched) \
BLOCK_CIPHER_func_cfb(cname, cprefix, cbits, kstruct, ksched) \
BLOCK_CIPHER_func_ecb(cname, cprefix, kstruct, ksched) \
BLOCK_CIPHER_func_ofb(cname, cprefix, cbits, kstruct, ksched)
#define BLOCK_CIPHER_def1(cname, nmode, mode, MODE, kstruct, nid, block_size, \
key_len, iv_len, flags, init_key, cleanup, \
set_asn1, get_asn1, ctrl) \
static const EVP_CIPHER cname##_##mode = { \
nid##_##nmode, block_size, key_len, iv_len, \
flags | EVP_CIPH_##MODE##_MODE, \
init_key, \
cname##_##mode##_cipher, \
cleanup, \
sizeof(kstruct), \
set_asn1, get_asn1,\
ctrl, \
NULL \
}; \
const EVP_CIPHER *EVP_##cname##_##mode(void) { return &cname##_##mode; }
#define BLOCK_CIPHER_def_cbc(cname, kstruct, nid, block_size, key_len, \
iv_len, flags, init_key, cleanup, set_asn1, \
get_asn1, ctrl) \
BLOCK_CIPHER_def1(cname, cbc, cbc, CBC, kstruct, nid, block_size, key_len, \
iv_len, flags, init_key, cleanup, set_asn1, get_asn1, ctrl)
#define BLOCK_CIPHER_def_cfb(cname, kstruct, nid, key_len, \
iv_len, cbits, flags, init_key, cleanup, \
set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def1(cname, cfb##cbits, cfb##cbits, CFB, kstruct, nid, 1, \
key_len, iv_len, flags, init_key, cleanup, set_asn1, \
get_asn1, ctrl)
#define BLOCK_CIPHER_def_ofb(cname, kstruct, nid, key_len, \
iv_len, cbits, flags, init_key, cleanup, \
set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def1(cname, ofb##cbits, ofb, OFB, kstruct, nid, 1, \
key_len, iv_len, flags, init_key, cleanup, set_asn1, \
get_asn1, ctrl)
#define BLOCK_CIPHER_def_ecb(cname, kstruct, nid, block_size, key_len, \
flags, init_key, cleanup, set_asn1, \
get_asn1, ctrl) \
BLOCK_CIPHER_def1(cname, ecb, ecb, ECB, kstruct, nid, block_size, key_len, \
0, flags, init_key, cleanup, set_asn1, get_asn1, ctrl)
#define BLOCK_CIPHER_defs(cname, kstruct, \
nid, block_size, key_len, iv_len, cbits, flags, \
init_key, cleanup, set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def_cbc(cname, kstruct, nid, block_size, key_len, iv_len, flags, \
init_key, cleanup, set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def_cfb(cname, kstruct, nid, key_len, iv_len, cbits, \
flags, init_key, cleanup, set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def_ofb(cname, kstruct, nid, key_len, iv_len, cbits, \
flags, init_key, cleanup, set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_def_ecb(cname, kstruct, nid, block_size, key_len, flags, \
init_key, cleanup, set_asn1, get_asn1, ctrl)
/*-
#define BLOCK_CIPHER_defs(cname, kstruct, \
nid, block_size, key_len, iv_len, flags,\
init_key, cleanup, set_asn1, get_asn1, ctrl)\
static const EVP_CIPHER cname##_cbc = {\
nid##_cbc, block_size, key_len, iv_len, \
flags | EVP_CIPH_CBC_MODE,\
init_key,\
cname##_cbc_cipher,\
cleanup,\
sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
set_asn1, get_asn1,\
ctrl, \
NULL \
};\
const EVP_CIPHER *EVP_##cname##_cbc(void) { return &cname##_cbc; }\
static const EVP_CIPHER cname##_cfb = {\
nid##_cfb64, 1, key_len, iv_len, \
flags | EVP_CIPH_CFB_MODE,\
init_key,\
cname##_cfb_cipher,\
cleanup,\
sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
set_asn1, get_asn1,\
ctrl,\
NULL \
};\
const EVP_CIPHER *EVP_##cname##_cfb(void) { return &cname##_cfb; }\
static const EVP_CIPHER cname##_ofb = {\
nid##_ofb64, 1, key_len, iv_len, \
flags | EVP_CIPH_OFB_MODE,\
init_key,\
cname##_ofb_cipher,\
cleanup,\
sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
set_asn1, get_asn1,\
ctrl,\
NULL \
};\
const EVP_CIPHER *EVP_##cname##_ofb(void) { return &cname##_ofb; }\
static const EVP_CIPHER cname##_ecb = {\
nid##_ecb, block_size, key_len, iv_len, \
flags | EVP_CIPH_ECB_MODE,\
init_key,\
cname##_ecb_cipher,\
cleanup,\
sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
set_asn1, get_asn1,\
ctrl,\
NULL \
};\
const EVP_CIPHER *EVP_##cname##_ecb(void) { return &cname##_ecb; }
*/
#define IMPLEMENT_BLOCK_CIPHER(cname, ksched, cprefix, kstruct, nid, \
block_size, key_len, iv_len, cbits, \
flags, init_key, \
cleanup, set_asn1, get_asn1, ctrl) \
BLOCK_CIPHER_all_funcs(cname, cprefix, cbits, kstruct, ksched) \
BLOCK_CIPHER_defs(cname, kstruct, nid, block_size, key_len, iv_len, \
cbits, flags, init_key, cleanup, set_asn1, \
get_asn1, ctrl)
#define IMPLEMENT_CFBR(cipher,cprefix,kstruct,ksched,keysize,cbits,iv_len,fl) \
BLOCK_CIPHER_func_cfb(cipher##_##keysize,cprefix,cbits,kstruct,ksched) \
BLOCK_CIPHER_def_cfb(cipher##_##keysize,kstruct, \
NID_##cipher##_##keysize, keysize/8, iv_len, cbits, \
(fl)|EVP_CIPH_FLAG_DEFAULT_ASN1, \
cipher##_init_key, NULL, NULL, NULL, NULL)
# ifndef OPENSSL_NO_EC
#define X25519_KEYLEN 32
#define X448_KEYLEN 56
#define ED448_KEYLEN 57
#define MAX_KEYLEN ED448_KEYLEN
typedef struct {
unsigned char pubkey[MAX_KEYLEN];
unsigned char *privkey;
} ECX_KEY;
#endif
/*
* Type needs to be a bit field Sub-type needs to be for variations on the
* method, as in, can it do arbitrary encryption....
*/
struct evp_pkey_st {
int type;
int save_type;
CRYPTO_REF_COUNT references;
const EVP_PKEY_ASN1_METHOD *ameth;
ENGINE *engine;
ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
union {
void *ptr;
# ifndef OPENSSL_NO_RSA
struct rsa_st *rsa; /* RSA */
# endif
# ifndef OPENSSL_NO_DSA
struct dsa_st *dsa; /* DSA */
# endif
# ifndef OPENSSL_NO_DH
struct dh_st *dh; /* DH */
# endif
# ifndef OPENSSL_NO_EC
struct ec_key_st *ec; /* ECC */
ECX_KEY *ecx; /* X25519, X448, Ed25519, Ed448 */
# endif
} pkey;
int save_parameters;
STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
CRYPTO_RWLOCK *lock;
} /* EVP_PKEY */ ;
void openssl_add_all_ciphers_int(void);
void openssl_add_all_digests_int(void);
void evp_cleanup_int(void);
void evp_app_cleanup_int(void);
/* Pulling defines out of C source files */
#define EVP_RC4_KEY_SIZE 16
#ifndef TLS1_1_VERSION
# define TLS1_1_VERSION 0x0302
#endif
void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags);
/* EVP_ENCODE_CTX flags */
/* Don't generate new lines when encoding */
#define EVP_ENCODE_CTX_NO_NEWLINES 1
/* Use the SRP base64 alphabet instead of the standard one */
#define EVP_ENCODE_CTX_USE_SRP_ALPHABET 2

View File

@ -0,0 +1,15 @@
/*
* Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_LHASH_H
# define OSSL_CRYPTO_LHASH_H
unsigned long openssl_lh_strcasehash(const char *);
#endif

View File

@ -0,0 +1,256 @@
/*
* Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*-
* This is a generic 32 bit "collector" for message digest algorithms.
* Whenever needed it collects input character stream into chunks of
* 32 bit values and invokes a block function that performs actual hash
* calculations.
*
* Porting guide.
*
* Obligatory macros:
*
* DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
* this macro defines byte order of input stream.
* HASH_CBLOCK
* size of a unit chunk HASH_BLOCK operates on.
* HASH_LONG
* has to be at least 32 bit wide.
* HASH_CTX
* context structure that at least contains following
* members:
* typedef struct {
* ...
* HASH_LONG Nl,Nh;
* either {
* HASH_LONG data[HASH_LBLOCK];
* unsigned char data[HASH_CBLOCK];
* };
* unsigned int num;
* ...
* } HASH_CTX;
* data[] vector is expected to be zeroed upon first call to
* HASH_UPDATE.
* HASH_UPDATE
* name of "Update" function, implemented here.
* HASH_TRANSFORM
* name of "Transform" function, implemented here.
* HASH_FINAL
* name of "Final" function, implemented here.
* HASH_BLOCK_DATA_ORDER
* name of "block" function capable of treating *unaligned* input
* message in original (data) byte order, implemented externally.
* HASH_MAKE_STRING
* macro converting context variables to an ASCII hash string.
*
* MD5 example:
*
* #define DATA_ORDER_IS_LITTLE_ENDIAN
*
* #define HASH_LONG MD5_LONG
* #define HASH_CTX MD5_CTX
* #define HASH_CBLOCK MD5_CBLOCK
* #define HASH_UPDATE MD5_Update
* #define HASH_TRANSFORM MD5_Transform
* #define HASH_FINAL MD5_Final
* #define HASH_BLOCK_DATA_ORDER md5_block_data_order
*/
#include <openssl/crypto.h>
#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
# error "DATA_ORDER must be defined!"
#endif
#ifndef HASH_CBLOCK
# error "HASH_CBLOCK must be defined!"
#endif
#ifndef HASH_LONG
# error "HASH_LONG must be defined!"
#endif
#ifndef HASH_CTX
# error "HASH_CTX must be defined!"
#endif
#ifndef HASH_UPDATE
# error "HASH_UPDATE must be defined!"
#endif
#ifndef HASH_TRANSFORM
# error "HASH_TRANSFORM must be defined!"
#endif
#ifndef HASH_FINAL
# error "HASH_FINAL must be defined!"
#endif
#ifndef HASH_BLOCK_DATA_ORDER
# error "HASH_BLOCK_DATA_ORDER must be defined!"
#endif
#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++))) ) )
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l) )&0xff), \
l)
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24) )
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>>24)&0xff), \
l)
#endif
/*
* Time for some action :-)
*/
int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
{
const unsigned char *data = data_;
unsigned char *p;
HASH_LONG l;
size_t n;
if (len == 0)
return 1;
l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
if (l < c->Nl) /* overflow */
c->Nh++;
c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
* 16-bit */
c->Nl = l;
n = c->num;
if (n != 0) {
p = (unsigned char *)c->data;
if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
memcpy(p + n, data, HASH_CBLOCK - n);
HASH_BLOCK_DATA_ORDER(c, p, 1);
n = HASH_CBLOCK - n;
data += n;
len -= n;
c->num = 0;
/*
* We use memset rather than OPENSSL_cleanse() here deliberately.
* Using OPENSSL_cleanse() here could be a performance issue. It
* will get properly cleansed on finalisation so this isn't a
* security problem.
*/
memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
} else {
memcpy(p + n, data, len);
c->num += (unsigned int)len;
return 1;
}
}
n = len / HASH_CBLOCK;
if (n > 0) {
HASH_BLOCK_DATA_ORDER(c, data, n);
n *= HASH_CBLOCK;
data += n;
len -= n;
}
if (len != 0) {
p = (unsigned char *)c->data;
c->num = (unsigned int)len;
memcpy(p, data, len);
}
return 1;
}
void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
{
HASH_BLOCK_DATA_ORDER(c, data, 1);
}
int HASH_FINAL(unsigned char *md, HASH_CTX *c)
{
unsigned char *p = (unsigned char *)c->data;
size_t n = c->num;
p[n] = 0x80; /* there is always room for one */
n++;
if (n > (HASH_CBLOCK - 8)) {
memset(p + n, 0, HASH_CBLOCK - n);
n = 0;
HASH_BLOCK_DATA_ORDER(c, p, 1);
}
memset(p + n, 0, HASH_CBLOCK - 8 - n);
p += HASH_CBLOCK - 8;
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
(void)HOST_l2c(c->Nh, p);
(void)HOST_l2c(c->Nl, p);
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
(void)HOST_l2c(c->Nl, p);
(void)HOST_l2c(c->Nh, p);
#endif
p -= HASH_CBLOCK;
HASH_BLOCK_DATA_ORDER(c, p, 1);
c->num = 0;
OPENSSL_cleanse(p, HASH_CBLOCK);
#ifndef HASH_MAKE_STRING
# error "HASH_MAKE_STRING must be defined!"
#else
HASH_MAKE_STRING(c, md);
#endif
return 1;
}
#ifndef MD32_REG_T
# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
# define MD32_REG_T long
/*
* This comment was originally written for MD5, which is why it
* discusses A-D. But it basically applies to all 32-bit digests,
* which is why it was moved to common header file.
*
* In case you wonder why A-D are declared as long and not
* as MD5_LONG. Doing so results in slight performance
* boost on LP64 architectures. The catch is we don't
* really care if 32 MSBs of a 64-bit register get polluted
* with eventual overflows as we *save* only 32 LSBs in
* *either* case. Now declaring 'em long excuses the compiler
* from keeping 32 MSBs zeroed resulting in 13% performance
* improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
* Well, to be honest it should say that this *prevents*
* performance degradation.
*/
# else
/*
* Above is not absolute and there are LP64 compilers that
* generate better code if MD32_REG_T is defined int. The above
* pre-processor condition reflects the circumstances under which
* the conclusion was made and is subject to further extension.
*/
# define MD32_REG_T int
# endif
#endif

View File

@ -0,0 +1,12 @@
/*
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/objects.h>
void obj_cleanup_int(void);

View File

@ -0,0 +1,21 @@
/*
* Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stddef.h>
#define POLY1305_BLOCK_SIZE 16
#define POLY1305_DIGEST_SIZE 16
#define POLY1305_KEY_SIZE 32
typedef struct poly1305_context POLY1305;
size_t Poly1305_ctx_size(void);
void Poly1305_Init(POLY1305 *ctx, const unsigned char key[32]);
void Poly1305_Update(POLY1305 *ctx, const unsigned char *inp, size_t len);
void Poly1305_Final(POLY1305 *ctx, unsigned char mac[16]);

View File

@ -0,0 +1,144 @@
/*
* Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* Licensed under the OpenSSL licenses, (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.openssl.org/source/license.html
* or in the file LICENSE in the source distribution.
*/
#ifndef OSSL_CRYPTO_RAND_H
# define OSSL_CRYPTO_RAND_H
# include <openssl/rand.h>
# if defined(__APPLE__) && !defined(OPENSSL_NO_APPLE_CRYPTO_RANDOM)
# include <Availability.h>
# if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || \
(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000)
# define OPENSSL_APPLE_CRYPTO_RANDOM 1
# include <CommonCrypto/CommonCryptoError.h>
# include <CommonCrypto/CommonRandom.h>
# endif
# endif
/* forward declaration */
typedef struct rand_pool_st RAND_POOL;
void rand_cleanup_int(void);
void rand_drbg_cleanup_int(void);
void drbg_delete_thread_state(void);
/* Hardware-based seeding functions. */
size_t rand_acquire_entropy_from_tsc(RAND_POOL *pool);
size_t rand_acquire_entropy_from_cpu(RAND_POOL *pool);
/* DRBG entropy callbacks. */
size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
unsigned char **pout,
int entropy, size_t min_len, size_t max_len,
int prediction_resistance);
void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
unsigned char *out, size_t outlen);
size_t rand_drbg_get_nonce(RAND_DRBG *drbg,
unsigned char **pout,
int entropy, size_t min_len, size_t max_len);
void rand_drbg_cleanup_nonce(RAND_DRBG *drbg,
unsigned char *out, size_t outlen);
size_t rand_drbg_get_additional_data(RAND_POOL *pool, unsigned char **pout);
void rand_drbg_cleanup_additional_data(RAND_POOL *pool, unsigned char *out);
/*
* RAND_POOL functions
*/
RAND_POOL *rand_pool_new(int entropy_requested, int secure,
size_t min_len, size_t max_len);
RAND_POOL *rand_pool_attach(const unsigned char *buffer, size_t len,
size_t entropy);
void rand_pool_free(RAND_POOL *pool);
const unsigned char *rand_pool_buffer(RAND_POOL *pool);
unsigned char *rand_pool_detach(RAND_POOL *pool);
void rand_pool_reattach(RAND_POOL *pool, unsigned char *buffer);
size_t rand_pool_entropy(RAND_POOL *pool);
size_t rand_pool_length(RAND_POOL *pool);
size_t rand_pool_entropy_available(RAND_POOL *pool);
size_t rand_pool_entropy_needed(RAND_POOL *pool);
/* |entropy_factor| expresses how many bits of data contain 1 bit of entropy */
size_t rand_pool_bytes_needed(RAND_POOL *pool, unsigned int entropy_factor);
size_t rand_pool_bytes_remaining(RAND_POOL *pool);
int rand_pool_add(RAND_POOL *pool,
const unsigned char *buffer, size_t len, size_t entropy);
unsigned char *rand_pool_add_begin(RAND_POOL *pool, size_t len);
int rand_pool_add_end(RAND_POOL *pool, size_t len, size_t entropy);
/*
* Add random bytes to the pool to acquire requested amount of entropy
*
* This function is platform specific and tries to acquire the requested
* amount of entropy by polling platform specific entropy sources.
*
* If the function succeeds in acquiring at least |entropy_requested| bits
* of entropy, the total entropy count is returned. If it fails, it returns
* an entropy count of 0.
*/
size_t rand_pool_acquire_entropy(RAND_POOL *pool);
/*
* Add some application specific nonce data
*
* This function is platform specific and adds some application specific
* data to the nonce used for instantiating the drbg.
*
* This data currently consists of the process and thread id, and a high
* resolution timestamp. The data does not include an atomic counter,
* because that is added by the calling function rand_drbg_get_nonce().
*
* Returns 1 on success and 0 on failure.
*/
int rand_pool_add_nonce_data(RAND_POOL *pool);
/*
* Add some platform specific additional data
*
* This function is platform specific and adds some random noise to the
* additional data used for generating random bytes and for reseeding
* the drbg.
*
* Returns 1 on success and 0 on failure.
*/
int rand_pool_add_additional_data(RAND_POOL *pool);
/*
* Initialise the random pool reseeding sources.
*
* Returns 1 on success and 0 on failure.
*/
int rand_pool_init(void);
/*
* Finalise the random pool reseeding sources.
*/
void rand_pool_cleanup(void);
/*
* Control the random pool use of open file descriptors.
*/
void rand_pool_keep_random_devices_open(int keep);
#endif

View File

@ -0,0 +1,19 @@
/*
* Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_SHA_H
# define OSSL_CRYPTO_SHA_H
# include <openssl/opensslconf.h>
int sha512_224_init(SHA512_CTX *);
int sha512_256_init(SHA512_CTX *);
#endif

View File

@ -0,0 +1,25 @@
/*
* Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stddef.h>
#define SIPHASH_BLOCK_SIZE 8
#define SIPHASH_KEY_SIZE 16
#define SIPHASH_MIN_DIGEST_SIZE 8
#define SIPHASH_MAX_DIGEST_SIZE 16
typedef struct siphash_st SIPHASH;
size_t SipHash_ctx_size(void);
size_t SipHash_hash_size(SIPHASH *ctx);
int SipHash_set_hash_size(SIPHASH *ctx, size_t hash_size);
int SipHash_Init(SIPHASH *ctx, const unsigned char *k,
int crounds, int drounds);
void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen);
int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen);

View File

@ -0,0 +1,77 @@
/*
* Copyright 2017-2021 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 2017 Ribose Inc. All Rights Reserved.
* Ported from Ribose contributions from Botan.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_SM2_H
# define OSSL_CRYPTO_SM2_H
# include <openssl/opensslconf.h>
# ifndef OPENSSL_NO_SM2
# include <openssl/ec.h>
/* The default user id as specified in GM/T 0009-2012 */
# define SM2_DEFAULT_USERID "1234567812345678"
int sm2_compute_z_digest(uint8_t *out,
const EVP_MD *digest,
const uint8_t *id,
const size_t id_len,
const EC_KEY *key);
/*
* SM2 signature operation. Computes Z and then signs H(Z || msg) using SM2
*/
ECDSA_SIG *sm2_do_sign(const EC_KEY *key,
const EVP_MD *digest,
const uint8_t *id,
const size_t id_len,
const uint8_t *msg, size_t msg_len);
int sm2_do_verify(const EC_KEY *key,
const EVP_MD *digest,
const ECDSA_SIG *signature,
const uint8_t *id,
const size_t id_len,
const uint8_t *msg, size_t msg_len);
/*
* SM2 signature generation.
*/
int sm2_sign(const unsigned char *dgst, int dgstlen,
unsigned char *sig, unsigned int *siglen, EC_KEY *eckey);
/*
* SM2 signature verification.
*/
int sm2_verify(const unsigned char *dgst, int dgstlen,
const unsigned char *sig, int siglen, EC_KEY *eckey);
/*
* SM2 encryption
*/
int sm2_ciphertext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
size_t *ct_size);
int sm2_plaintext_size(const unsigned char *ct, size_t ct_size, size_t *pt_size);
int sm2_encrypt(const EC_KEY *key,
const EVP_MD *digest,
const uint8_t *msg,
size_t msg_len,
uint8_t *ciphertext_buf, size_t *ciphertext_len);
int sm2_decrypt(const EC_KEY *key,
const EVP_MD *digest,
const uint8_t *ciphertext,
size_t ciphertext_len, uint8_t *ptext_buf, size_t *ptext_len);
# endif /* OPENSSL_NO_SM2 */
#endif

View File

@ -0,0 +1,65 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
* Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_SM2ERR_H
# define OSSL_CRYPTO_SM2ERR_H
# ifndef HEADER_SYMHACKS_H
# include <openssl/symhacks.h>
# endif
# include <openssl/opensslconf.h>
# ifndef OPENSSL_NO_SM2
# ifdef __cplusplus
extern "C"
# endif
int ERR_load_SM2_strings(void);
/*
* SM2 function codes.
*/
# define SM2_F_PKEY_SM2_COPY 115
# define SM2_F_PKEY_SM2_CTRL 109
# define SM2_F_PKEY_SM2_CTRL_STR 110
# define SM2_F_PKEY_SM2_DIGEST_CUSTOM 114
# define SM2_F_PKEY_SM2_INIT 111
# define SM2_F_PKEY_SM2_SIGN 112
# define SM2_F_SM2_COMPUTE_MSG_HASH 100
# define SM2_F_SM2_COMPUTE_USERID_DIGEST 101
# define SM2_F_SM2_COMPUTE_Z_DIGEST 113
# define SM2_F_SM2_DECRYPT 102
# define SM2_F_SM2_ENCRYPT 103
# define SM2_F_SM2_PLAINTEXT_SIZE 104
# define SM2_F_SM2_SIGN 105
# define SM2_F_SM2_SIG_GEN 106
# define SM2_F_SM2_SIG_VERIFY 107
# define SM2_F_SM2_VERIFY 108
/*
* SM2 reason codes.
*/
# define SM2_R_ASN1_ERROR 100
# define SM2_R_BAD_SIGNATURE 101
# define SM2_R_BUFFER_TOO_SMALL 107
# define SM2_R_DIST_ID_TOO_LARGE 110
# define SM2_R_ID_NOT_SET 112
# define SM2_R_ID_TOO_LARGE 111
# define SM2_R_INVALID_CURVE 108
# define SM2_R_INVALID_DIGEST 102
# define SM2_R_INVALID_DIGEST_TYPE 103
# define SM2_R_INVALID_ENCODING 104
# define SM2_R_INVALID_FIELD 105
# define SM2_R_NO_PARAMETERS_SET 109
# define SM2_R_USER_ID_TOO_LARGE 106
# endif
#endif

View File

@ -0,0 +1,39 @@
/*
* Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 2017 Ribose Inc. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_SM3_H
# define OSSL_CRYPTO_SM3_H
# include <openssl/opensslconf.h>
# ifdef OPENSSL_NO_SM3
# error SM3 is disabled.
# endif
# define SM3_DIGEST_LENGTH 32
# define SM3_WORD unsigned int
# define SM3_CBLOCK 64
# define SM3_LBLOCK (SM3_CBLOCK/4)
typedef struct SM3state_st {
SM3_WORD A, B, C, D, E, F, G, H;
SM3_WORD Nl, Nh;
SM3_WORD data[SM3_LBLOCK];
unsigned int num;
} SM3_CTX;
int sm3_init(SM3_CTX *c);
int sm3_update(SM3_CTX *c, const void *data, size_t len);
int sm3_final(unsigned char *md, SM3_CTX *c);
void sm3_block_data_order(SM3_CTX *c, const void *p, size_t num);
#endif

View File

@ -0,0 +1,37 @@
/*
* Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 2017 Ribose Inc. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_SM4_H
# define OSSL_CRYPTO_SM4_H
# include <openssl/opensslconf.h>
# include <openssl/e_os2.h>
# ifdef OPENSSL_NO_SM4
# error SM4 is disabled.
# endif
# define SM4_ENCRYPT 1
# define SM4_DECRYPT 0
# define SM4_BLOCK_SIZE 16
# define SM4_KEY_SCHEDULE 32
typedef struct SM4_KEY_st {
uint32_t rk[SM4_KEY_SCHEDULE];
} SM4_KEY;
int SM4_set_key(const uint8_t *key, SM4_KEY *ks);
void SM4_encrypt(const uint8_t *in, uint8_t *out, const SM4_KEY *ks);
void SM4_decrypt(const uint8_t *in, uint8_t *out, const SM4_KEY *ks);
#endif

View File

@ -0,0 +1,28 @@
/*
* Copyright 2016-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef OSSL_CRYPTO_STORE_H
# define OSSL_CRYPTO_STORE_H
# include <openssl/bio.h>
# include <openssl/store.h>
# include <openssl/ui.h>
/*
* Two functions to read PEM data off an already opened BIO. To be used
* instead of OSSLSTORE_open() and OSSLSTORE_close(). Everything is done
* as usual with OSSLSTORE_load() and OSSLSTORE_eof().
*/
OSSL_STORE_CTX *ossl_store_attach_pem_bio(BIO *bp, const UI_METHOD *ui_method,
void *ui_data);
int ossl_store_detach_pem_bio(OSSL_STORE_CTX *ctx);
void ossl_store_cleanup_int(void);
#endif

View File

@ -0,0 +1,291 @@
/*
* Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/refcount.h"
#include <openssl/x509.h>
#include <openssl/conf.h>
/* Internal X509 structures and functions: not for application use */
/* Note: unless otherwise stated a field pointer is mandatory and should
* never be set to NULL: the ASN.1 code and accessors rely on mandatory
* fields never being NULL.
*/
/*
* name entry structure, equivalent to AttributeTypeAndValue defined
* in RFC5280 et al.
*/
struct X509_name_entry_st {
ASN1_OBJECT *object; /* AttributeType */
ASN1_STRING *value; /* AttributeValue */
int set; /* index of RDNSequence for this entry */
int size; /* temp variable */
};
/* Name from RFC 5280. */
struct X509_name_st {
STACK_OF(X509_NAME_ENTRY) *entries; /* DN components */
int modified; /* true if 'bytes' needs to be built */
BUF_MEM *bytes; /* cached encoding: cannot be NULL */
/* canonical encoding used for rapid Name comparison */
unsigned char *canon_enc;
int canon_enclen;
} /* X509_NAME */ ;
/* Signature info structure */
struct x509_sig_info_st {
/* NID of message digest */
int mdnid;
/* NID of public key algorithm */
int pknid;
/* Security bits */
int secbits;
/* Various flags */
uint32_t flags;
};
/* PKCS#10 certificate request */
struct X509_req_info_st {
ASN1_ENCODING enc; /* cached encoding of signed part */
ASN1_INTEGER *version; /* version, defaults to v1(0) so can be NULL */
X509_NAME *subject; /* certificate request DN */
X509_PUBKEY *pubkey; /* public key of request */
/*
* Zero or more attributes.
* NB: although attributes is a mandatory field some broken
* encodings omit it so this may be NULL in that case.
*/
STACK_OF(X509_ATTRIBUTE) *attributes;
};
struct X509_req_st {
X509_REQ_INFO req_info; /* signed certificate request data */
X509_ALGOR sig_alg; /* signature algorithm */
ASN1_BIT_STRING *signature; /* signature */
CRYPTO_REF_COUNT references;
CRYPTO_RWLOCK *lock;
};
struct X509_crl_info_st {
ASN1_INTEGER *version; /* version: defaults to v1(0) so may be NULL */
X509_ALGOR sig_alg; /* signature algorithm */
X509_NAME *issuer; /* CRL issuer name */
ASN1_TIME *lastUpdate; /* lastUpdate field */
ASN1_TIME *nextUpdate; /* nextUpdate field: optional */
STACK_OF(X509_REVOKED) *revoked; /* revoked entries: optional */
STACK_OF(X509_EXTENSION) *extensions; /* extensions: optional */
ASN1_ENCODING enc; /* encoding of signed portion of CRL */
};
struct X509_crl_st {
X509_CRL_INFO crl; /* signed CRL data */
X509_ALGOR sig_alg; /* CRL signature algorithm */
ASN1_BIT_STRING signature; /* CRL signature */
CRYPTO_REF_COUNT references;
int flags;
/*
* Cached copies of decoded extension values, since extensions
* are optional any of these can be NULL.
*/
AUTHORITY_KEYID *akid;
ISSUING_DIST_POINT *idp;
/* Convenient breakdown of IDP */
int idp_flags;
int idp_reasons;
/* CRL and base CRL numbers for delta processing */
ASN1_INTEGER *crl_number;
ASN1_INTEGER *base_crl_number;
STACK_OF(GENERAL_NAMES) *issuers;
/* hash of CRL */
unsigned char sha1_hash[SHA_DIGEST_LENGTH];
/* alternative method to handle this CRL */
const X509_CRL_METHOD *meth;
void *meth_data;
CRYPTO_RWLOCK *lock;
};
struct x509_revoked_st {
ASN1_INTEGER serialNumber; /* revoked entry serial number */
ASN1_TIME *revocationDate; /* revocation date */
STACK_OF(X509_EXTENSION) *extensions; /* CRL entry extensions: optional */
/* decoded value of CRLissuer extension: set if indirect CRL */
STACK_OF(GENERAL_NAME) *issuer;
/* revocation reason: set to CRL_REASON_NONE if reason extension absent */
int reason;
/*
* CRL entries are reordered for faster lookup of serial numbers. This
* field contains the original load sequence for this entry.
*/
int sequence;
};
/*
* This stuff is certificate "auxiliary info": it contains details which are
* useful in certificate stores and databases. When used this is tagged onto
* the end of the certificate itself. OpenSSL specific structure not defined
* in any RFC.
*/
struct x509_cert_aux_st {
STACK_OF(ASN1_OBJECT) *trust; /* trusted uses */
STACK_OF(ASN1_OBJECT) *reject; /* rejected uses */
ASN1_UTF8STRING *alias; /* "friendly name" */
ASN1_OCTET_STRING *keyid; /* key id of private key */
STACK_OF(X509_ALGOR) *other; /* other unspecified info */
};
struct x509_cinf_st {
ASN1_INTEGER *version; /* [ 0 ] default of v1 */
ASN1_INTEGER serialNumber;
X509_ALGOR signature;
X509_NAME *issuer;
X509_VAL validity;
X509_NAME *subject;
X509_PUBKEY *key;
ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */
ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */
STACK_OF(X509_EXTENSION) *extensions; /* [ 3 ] optional in v3 */
ASN1_ENCODING enc;
};
struct x509_st {
X509_CINF cert_info;
X509_ALGOR sig_alg;
ASN1_BIT_STRING signature;
X509_SIG_INFO siginf;
CRYPTO_REF_COUNT references;
CRYPTO_EX_DATA ex_data;
/* These contain copies of various extension values */
long ex_pathlen;
long ex_pcpathlen;
uint32_t ex_flags;
uint32_t ex_kusage;
uint32_t ex_xkusage;
uint32_t ex_nscert;
ASN1_OCTET_STRING *skid;
AUTHORITY_KEYID *akid;
X509_POLICY_CACHE *policy_cache;
STACK_OF(DIST_POINT) *crldp;
STACK_OF(GENERAL_NAME) *altname;
NAME_CONSTRAINTS *nc;
#ifndef OPENSSL_NO_RFC3779
STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid;
# endif
unsigned char sha1_hash[SHA_DIGEST_LENGTH];
X509_CERT_AUX *aux;
CRYPTO_RWLOCK *lock;
volatile int ex_cached;
} /* X509 */ ;
/*
* This is a used when verifying cert chains. Since the gathering of the
* cert chain can take some time (and have to be 'retried', this needs to be
* kept and passed around.
*/
struct x509_store_ctx_st { /* X509_STORE_CTX */
X509_STORE *ctx;
/* The following are set by the caller */
/* The cert to check */
X509 *cert;
/* chain of X509s - untrusted - passed in */
STACK_OF(X509) *untrusted;
/* set of CRLs passed in */
STACK_OF(X509_CRL) *crls;
X509_VERIFY_PARAM *param;
/* Other info for use with get_issuer() */
void *other_ctx;
/* Callbacks for various operations */
/* called to verify a certificate */
int (*verify) (X509_STORE_CTX *ctx);
/* error callback */
int (*verify_cb) (int ok, X509_STORE_CTX *ctx);
/* get issuers cert from ctx */
int (*get_issuer) (X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
/* check issued */
int (*check_issued) (X509_STORE_CTX *ctx, X509 *x, X509 *issuer);
/* Check revocation status of chain */
int (*check_revocation) (X509_STORE_CTX *ctx);
/* retrieve CRL */
int (*get_crl) (X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x);
/* Check CRL validity */
int (*check_crl) (X509_STORE_CTX *ctx, X509_CRL *crl);
/* Check certificate against CRL */
int (*cert_crl) (X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x);
/* Check policy status of the chain */
int (*check_policy) (X509_STORE_CTX *ctx);
STACK_OF(X509) *(*lookup_certs) (X509_STORE_CTX *ctx, X509_NAME *nm);
STACK_OF(X509_CRL) *(*lookup_crls) (X509_STORE_CTX *ctx, X509_NAME *nm);
int (*cleanup) (X509_STORE_CTX *ctx);
/* The following is built up */
/* if 0, rebuild chain */
int valid;
/* number of untrusted certs */
int num_untrusted;
/* chain of X509s - built up and trusted */
STACK_OF(X509) *chain;
/* Valid policy tree */
X509_POLICY_TREE *tree;
/* Require explicit policy value */
int explicit_policy;
/* When something goes wrong, this is why */
int error_depth;
int error;
X509 *current_cert;
/* cert currently being tested as valid issuer */
X509 *current_issuer;
/* current CRL */
X509_CRL *current_crl;
/* score of current CRL */
int current_crl_score;
/* Reason mask */
unsigned int current_reasons;
/* For CRL path validation: parent context */
X509_STORE_CTX *parent;
CRYPTO_EX_DATA ex_data;
SSL_DANE *dane;
/* signed via bare TA public key, rather than CA certificate */
int bare_ta_signed;
};
/* PKCS#8 private key info structure */
struct pkcs8_priv_key_info_st {
ASN1_INTEGER *version;
X509_ALGOR *pkeyalg;
ASN1_OCTET_STRING *pkey;
STACK_OF(X509_ATTRIBUTE) *attributes;
};
struct X509_sig_st {
X509_ALGOR *algor;
ASN1_OCTET_STRING *digest;
};
struct x509_object_st {
/* one of the above types */
X509_LOOKUP_TYPE type;
union {
char *ptr;
X509 *x509;
X509_CRL *crl;
EVP_PKEY *pkey;
} data;
};
int a2i_ipadd(unsigned char *ipout, const char *ipasc);
int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm);
void x509_init_sig_info(X509 *x);
int x509v3_add_len_value_uchar(const char *name, const unsigned char *value,
size_t vallen, STACK_OF(CONF_VALUE) **extlist);

Some files were not shown because too many files have changed in this diff Show More