1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@cs.vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (C): 2009-2022, VU University Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 9 10 This program is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public License 12 as published by the Free Software Foundation; either version 2 13 of the License, or (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public 21 License along with this library; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 24 As a special exception, if you link this library with other files, 25 compiled with a Free Software compiler, to produce an executable, this 26 library does not by itself cause the resulting executable to be covered 27 by the GNU General Public License. This exception does not however 28 invalidate any other reasons why the executable file might be covered by 29 the GNU General Public License. 30*/ 31 32:- module(plweb_download, []). 33:- use_module(library(http/html_write)). 34:- use_module(library(http/js_write)). 35:- use_module(library(http/http_dispatch)). 36:- use_module(library(http/http_path)). 37:- use_module(library(http/http_parameters)). 38:- use_module(library(http/http_dirindex)). 39:- use_module(library(http/http_wrapper)). 40:- use_module(library(http/http_cors)). 41:- use_module(library(dcg/basics)). 42:- use_module(library(broadcast)). 43:- use_module(library(pairs)). 44:- use_module(library(lists)). 45:- use_module(library(apply)). 46:- use_module(library(error)). 47:- use_module(library(filesex)). 48:- use_module(library(persistency)). 49:- use_module(library(crypto)). 50:- use_module(library(random)). 51:- use_module(wiki).
57:- http_handler(download(devel), download_table, []). 58:- http_handler(download(stable), download_table, []). 59:- http_handler(download(old), download_table, []). 60:- http_handler(download('daily/bin/'), download_daily, []). 61:- http_handler(download(.), download, 62 [prefix, spawn(download), priority(10)]). 63:- http_handler(root(download), http_redirect(moved, download(.)), 64 [priority(10)]).
71download_table(Request) :-
72 http_parameters(Request,
73 [ show(Show, [oneof([all,latest]), default(latest)])
74 ]),
75 memberchk(path(Path), Request),
76 http_absolute_location(root(download), DownLoadRoot, []),
77 atom_concat(DownLoadRoot, DownLoadDir, Path),
78 absolute_file_name(download(DownLoadDir),
79 Dir,
80 [ file_type(directory),
81 access(read)
82 ]),
83 list_downloads(Dir, [show(Show), request(Request)]).
87list_downloads(Dir, Options) :- 88 ( wiki_file_to_dom(Dir, 'header.txt', Header0) 89 -> ( Header0 = [h1(_, Title)|Header] 90 -> true 91 ; Header = Header0 92 ) 93 ; Header = [] 94 ), 95 ( var(Title) 96 -> Title = 'SWI-Prolog downloads' 97 ; true 98 ), 99 reply_html_page( 100 download(Dir, Title), 101 title('SWI-Prolog downloads'), 102 [ \html(Header), 103 br(clear(all)), 104 table(class(downloads), 105 \download_table(Dir, Options)), 106 \machine_download_text, 107 \wiki(Dir, 'footer.txt') 108 ]). 109 110wiki_file_to_dom(Dir, File, DOM) :- 111 directory_file_path(Dir, File, WikiFile), 112 access_file(WikiFile, read), !, 113 wiki_file_to_dom(WikiFile, DOM). 114 115wiki(Dir, File) --> 116 { wiki_file_to_dom(Dir, File, DOM) }, !, 117 html(DOM). 118wiki(_, _) --> 119 []. 120 121machine_download_text --> 122 html({|html|| 123<div class="machine-download"> 124Install scripts may download the SHA256 checksum by appending 125<code>.sha256</code> to the file name. Scripts can download 126the latest version by replacing the version of the file with 127<code>latest</code>. This causes the server to reply with the 128location of the latest version using an 129<code>HTTP 303 See Other</code> message. 130</div> 131 |}). 132 133 134download_table(Dir, Options) --> 135 list_files(Dir, bin, bin, 'Binaries', Options), 136 list_files(Dir, src, src, 'Sources', Options), 137 list_files(Dir, doc, doc, 'Documentation', Options), 138 toggle_show(Options).
145toggle_show(Options) --> 146 { option(request(Request), Options), 147 memberchk(path(Path), Request), !, 148 file_base_name(Path, MySelf), 149 ( option(show(all), Options) 150 -> NewShow = latest 151 ; NewShow = all 152 ) 153 }, 154 html(tr(td([class(toggle), colspan(3)], 155 a(href(MySelf+'?show='+NewShow), 156 [ 'Show ', NewShow, ' files' ])))). 157toggle_show(_) --> 158 [].
tr
with Label and a tr
row for each each
matching file. Options are:
all
or latest
(default).169list_files(Dir, SubDir, Class, Label, Options) --> 170 { directory_file_path(Dir, SubDir, Directory), 171 download_files(Directory, Class, Files, Options), 172 Files \== [] 173 }, 174 html(tr(th(colspan(3), Label))), 175 list_files(Files). 176list_files(_, _, _, _, _) --> 177 []. 178 179list_files([]) --> []. 180list_files([H|T]) --> 181 list_file(H), 182 list_files(T). 183 184list_file(File) --> 185 html(tr(class(download), 186 [ td(class(dl_icon), \file_icon(File)), 187 td(class(dl_size), \file_size(File)), 188 td(class(dl_file), \file_description(File)) 189 ])). 190 191file_icon(file(Type, PlatForm, _, _, _)) --> 192 { icon_for_file(Type, PlatForm, Icon, Alt), !, 193 http_absolute_location(icons(Icon), HREF, []) 194 }, 195 html(img([src(HREF), alt(Alt)])). 196file_icon(_) --> 197 html(?). % no defined icon 198 199icon_for_file(bin, linux(universal), 200 'linux.png', 'Linux 32/64 intel'). 201icon_for_file(bin, linux(_,_), 202 'linux32.gif', 'Linux RPM'). 203icon_for_file(bin, macos(lion,_), 204 'lion.png', 'Lion'). 205icon_for_file(bin, macos(snow_leopard,_), 206 'snowleopard.gif', 'Snow Leopard'). 207icon_for_file(bin, macos(snow_leopard_and_later,_), 208 'macapp.png', 'Snow Leopard and later'). 209icon_for_file(bin, macos(bundle,_), 210 'macapp.png', 'MacOS bundle'). 211icon_for_file(bin, macos(_,_), 212 'mac.gif', 'MacOSX version'). 213icon_for_file(_, windows(win32), 214 'win32.gif', 'Windows version (32-bits)'). 215icon_for_file(_, windows(win64), 216 'win64.gif', 'Windows version (64-bits)'). 217icon_for_file(src, _, 218 'src.gif', 'Source archive'). 219icon_for_file(_, pdf, 220 'pdf.gif', 'PDF file'). 221 222 223file_size(file(_, _, _, _, Path)) --> 224 { size_file(Path, Bytes) 225 }, 226 html('~D bytes'-[Bytes]). 227 228file_description(file(bin, PlatForm, Version, _, Path)) --> 229 { down_file_href(Path, HREF) 230 }, 231 html([ a(href(HREF), 232 [ 'SWI-Prolog ', \version(Version), ' for ', 233 \platform(PlatForm) 234 ]), 235 \platform_notes(PlatForm, Path), 236 \checksum(Path) 237 ]). 238file_description(file(src, Format, Version, _, Path)) --> 239 { down_file_href(Path, HREF) 240 }, 241 html([ a(href(HREF), 242 [ 'SWI-Prolog source for ', \version(Version) 243 ]), 244 \platform_notes(Format, Path), 245 \checksum(Path) 246 ]). 247file_description(file(doc, Format, Version, _, Path)) --> 248 { down_file_href(Path, HREF) 249 }, 250 html([ a(href(HREF), 251 [ 'SWI-Prolog ', \version(Version), 252 ' reference manual in PDF' 253 ]), 254 \platform_notes(Format, Path) 255 ]). 256file_description(file(pkg(Pkg), PlatForm, Version, _, Path)) --> 257 { down_file_href(Path, HREF) 258 }, 259 html([ a(href(HREF), 260 [ \package(Pkg), ' (version ', \version(Version), ') for ', 261 \platform(PlatForm) 262 ]), 263 \platform_notes(pkg(Pkg), Path) 264 ]). 265 266package(Name) --> 267 html([ 'Package ', Name ]). 268 269version(version(Major, Minor, Patch, '')) --> !, 270 html(b('~w.~w.~w'-[Major, Minor, Patch])). 271version(version(Major, Minor, Patch, Tag)) --> 272 html(b('~w.~w.~w-~w'-[Major, Minor, Patch, Tag])). 273 274checksum(Path) --> 275 { file_checksum(Path, SHA256) }, 276 html(div([ class(checksum), 277 title('You can use the checksum to verify the integrity \c 278 of the downloaded file. It provides some protection \c 279 against deliberate tamporing with the file.') 280 ], 281 [ span(class('checkum-header'), 'SHA256'), :, 282 span(class([checksum,sha256]), SHA256) 283 ])). 284 285down_file_href(Path, HREF) :- 286 absolute_file_name(download(.), 287 Dir, 288 [ file_type(directory), 289 access(read) 290 ]), 291 atom_concat(Dir, SlashLocal, Path), 292 delete_leading_slash(SlashLocal, Local), 293 add_envelope(Local, SafeLocal), 294 http_absolute_location(download(SafeLocal), HREF, []). 295 296delete_leading_slash(SlashPath, Path) :- 297 atom_concat(/, Path, SlashPath), !. 298delete_leading_slash(Path, Path). 299 300platform(linux(universal)) --> 301 html(['Linux 32/64 bits (TAR)']). 302platform(linux(rpm, _)) --> 303 html(['i586/Linux (RPM)']). 304platform(macos(Name, CPU)) --> 305 html(['MacOSX ', \html_macos_version(Name, CPU), ' on ', \cpu(CPU)]). 306platform(windows(win32)) --> 307 html(['Microsoft Windows (32 bit)']). 308platform(windows(win64)) --> 309 html(['Microsoft Windows (64 bit)']). 310 311html_macos_version(tiger, _) --> html('10.4 (Tiger)'). 312html_macos_version(leopard, _) --> html('10.5 (Leopard)'). 313html_macos_version(snow_leopard, _) --> html('10.6 (Snow Leopard)'). 314html_macos_version(lion, _) --> html('10.7 (Lion)'). 315html_macos_version(snow_leopard_and_later, _) 316 --> html('10.6 (Snow Leopard) and later'). 317html_macos_version(bundle, x86_64) --> html('10.12 (Sierra) and later'). 318html_macos_version(bundle, fat) --> html('10.14 (Mojave) and later'). 319html_macos_version(OS, _CPU) --> html(OS). 320 321cpu(fat) --> !, html("x86_64 and arm64"). 322cpu(CPU) --> html(CPU).
330platform_notes(Platform, Path) --> 331 { file_directory_name(Path, Dir), 332 platform_note_file(Platform, File), 333 atomic_list_concat([Dir, /, File], NoteFile), 334 debug(download, 'Trying note-file ~q', [NoteFile]), 335 access_file(NoteFile, read), !, 336 debug(download, 'Found note-file ~q', [NoteFile]), 337 wiki_file_to_dom(NoteFile, DOM) 338 }, 339 html(DOM). 340platform_notes(_, _) --> 341 []. 342 343platform_note_file(linux(rpm,_), 'linux-rpm.txt'). 344platform_note_file(linux(universal), 'linux.txt'). 345platform_note_file(windows(win32), 'win32.txt'). 346platform_note_file(windows(win64), 'win64.txt'). 347platform_note_file(pkg(Pkg), File) :- 348 file_name_extension(Pkg, txt, File). 349platform_note_file(macos(Version,fat), File) :- 350 atomic_list_concat([macosx, -, fat, -, Version, '.txt'], File). 351platform_note_file(macos(Version,_), File) :- 352 atomic_list_concat([macosx, -, Version, '.txt'], File). 353platform_note_file(macos(_,_), 'macosx.txt'). 354platform_note_file(tgz, 'src-tgz.txt'). 355platform_note_file(pdf, 'doc-pdf.txt'). 356 357 358 /******************************* 359 * CLASSIFY FILES * 360 *******************************/
367:- dynamic 368 download_cache/6. % Hash, Dir, Class, Opts, Time, Files 369 370download_files(Dir, Class, Files, Options0) :- 371 exists_directory(Dir), !, 372 include(download_option, Options0, Options), 373 term_hash(ci(Dir,Class,Options), Hash), 374 time_file(Dir, DirTime), 375 ( download_cache(Hash, Dir, Class, Options, Time, Files0), 376 ( DirTime == Time 377 -> true 378 ; retractall(download_cache(Hash, Dir, Class, Options, _, _)), 379 fail 380 ) 381 -> true 382 ; download_files_nc(Dir, Class, Files0, Options), 383 asserta(download_cache(Hash, Dir, Class, Options, DirTime, Files0)) 384 ), 385 Files = Files0. 386download_files(_, _, [], _). 387 388clear_download_cache :- 389 retractall(download_cache(_Hash, _Dir, _Class, _Options, _Time, _Files0)). 390 391download_option(show(_)). 392 393 394download_files_nc(Directory, Class, Sorted, Options) :- 395 atom_concat(Directory, '/*', Pattern), 396 expand_file_name(Pattern, Files), 397 classify_files(Files, Class, Classified, Options), 398 sort_files(Classified, Sorted, Options). 399 400classify_files([], _, [], _). 401classify_files([H0|T0], Class, [H|T], Options) :- 402 classify_file(H0, H, Options), 403 arg(1, H, Classification), 404 subsumes_term(Class, Classification), !, 405 classify_files(T0, Class, T, Options). 406classify_files([_|T0], Class, T, Options) :- 407 classify_files(T0, Class, T, Options).
411classify_file(Path, file(Type, Platform, Version, Name, Path), Options) :- 412 file_base_name(Path, Name), 413 atom_codes(Name, Codes), 414 phrase(file(Type, Platform, Version, Options), Codes). 415 416file(bin, macos(OSVersion, CPU), Version, Options) --> 417 { option(show(all), Options) }, 418 "swi-prolog-", opt_devel, long_version(Version), "-", 419 macos_version(OSVersion), 420 ( "-", 421 macos_cpu(CPU) 422 -> "" 423 ; { macos_def_cpu(OSVersion, CPU) } 424 ), 425 ".mpkg.zip", !. 426% Cmake version 427file(bin, macos(bundle, intel), Version, _) --> 428 "swipl-", long_version(Version), opt_release(_), 429 opt_cpu(_), 430 ".dmg", !. 431file(bin, macos(bundle, fat), Version, _) --> 432 "swipl-", long_version(Version), opt_release(_), 433 ".fat.dmg", !. 434file(bin, macos(snow_leopard_and_later, intel), Version, _) --> 435 "SWI-Prolog-", long_version(Version), 436 ".dmg", !. 437file(bin, windows(WinType), Version, _) --> 438 "swipl-", long_version(Version), opt_release(_), 439 cmake_win_type(WinType), 440 ".exe", !. 441file(bin, windows(WinType), Version, _) --> 442 win_type(WinType), "pl", 443 short_version(Version), 444 ".exe", !. 445file(bin, windows(WinType), Version, _) --> 446 swipl, win_type(WinType), "-", 447 short_version(Version), 448 ".exe", !. 449file(bin, linux(rpm, suse), Version, _) --> 450 swipl, long_version(Version), "-", digits(_Build), ".i586.rpm", !. 451file(bin, linux(universal), Version, _) --> 452 "swipl-", 453 long_version(Version), "-", "linux", 454 ".tar.gz", !. 455file(src, tgz, Version, _) --> 456 swipl, long_version(Version), ".tar.gz", !. 457file(doc, pdf, Version, _) --> 458 "SWI-Prolog-", long_version(Version), ".pdf", !. 459 460swipl --> "swipl-", !. 461swipl --> "pl-". 462 463opt_release(Rel) --> "-", int(Rel, 4), !. 464opt_release(-) --> "". 465 466opt_devel --> "devel-", !. 467opt_devel --> "". 468 469opt_cpu(x86_64) --> ".", "x86_64", !. 470opt_cpu(unknown) --> "". 471 472macos_version(tiger) --> "tiger". 473macos_version(leopard) --> "leopard". 474macos_version(snow_leopard) --> "snow-leopard". 475macos_version(lion) --> "lion". 476 477macos_cpu(ppc) --> "powerpc". 478macos_cpu(intel) --> "intel". 479macos_cpu(x86) --> "32bit". 480 481macos_def_cpu(snow_leopard, intel) :- !. 482macos_def_cpu(lion, intel) :- !. 483macos_def_cpu(_, ppc). 484 485win_type(win32) --> "w32". 486win_type(win64) --> "w64". 487 488cmake_win_type(win64) --> ".", "x64". 489cmake_win_type(win32) --> ".", "x86". 490 491long_version(version(Major, Minor, Patch, Tag)) --> 492 int(Major, 1), ".", int(Minor, 2), ".", int(Patch, 2), !, 493 tag(Tag), !. 494long_version(latest) --> 495 "latest". 496 497tag(Tag) --> 498 "-", alnums(Codes), !, 499 { atom_codes(Tag, Codes) }. 500tag('') --> "". 501 502int(Value, MaxDigits) --> 503 digits(Digits), 504 { length(Digits, Len), 505 Len =< MaxDigits, 506 Len > 0, 507 number_codes(Value, Digits) 508 }. 509 510alnums([H|T]) --> 511 [H], { code_type(H, alnum) }, !, 512 alnums(T). 513alnums([]) --> "". 514 515short_version(version(Major, Minor, Patch, Tag)) --> 516 digits(Digits), 517 { Digits = [D1,D2,D3] 518 -> number_codes(Major, [D1]), 519 number_codes(Minor, [D2]), 520 number_codes(Patch, [D3]) 521 ; Digits = [D1,D2,D3,D4] 522 -> ( number_codes(51, [D1,D2]) % 5.1X.Y 523 -> number_codes(Major, [D1]), 524 number_codes(Minor, [D2,D3]), 525 number_codes(Patch, [D4]) 526 ; number_codes(Major, [D1]), 527 number_codes(Minor, [D2]), 528 number_codes(Patch, [D3,D4]) 529 ) 530 ; Digits = [D1,D2,D3,D4,D5] 531 -> number_codes(Major, [D1]), 532 number_codes(Minor, [D2,D3]), 533 number_codes(Patch, [D4,D5]) 534 }, 535 tag(Tag), !. 536short_version(latest) --> 537 "latest".
Options:
all
or latest
.549sort_files(In, Out, Options) :- 550 map_list_to_pairs(map_type, In, Typed0), 551 ( option(show(all), Options) 552 -> Typed = Typed0 553 ; exclude(old_tagged_file, Typed0, Typed) 554 ), 555 keysort(Typed, TSorted), 556 group_pairs_by_key(TSorted, TGrouped), 557 maplist(sort_group_by_version, TGrouped, TGroupSorted), 558 ( option(show(all), Options) 559 -> pairs_values(TGroupSorted, TValues), 560 flatten(TValues, Out) 561 ; take_latest(TGroupSorted, Out) 562 ). 563 564map_type(File, Tag) :- 565 File = file(Type, Platform, _Version, _Name, _Path), 566 type_tag(Type, Platform, Tag). 567 568type_tag(bin, linux(A), tag(10, linux(A))) :- !. 569type_tag(bin, linux(A,B), tag(11, linux(A,B))) :- !. 570type_tag(bin, windows(A), tag(Tg, windows(A))) :- !, 571 win_tag(A, Tg2), 572 Tg is 20+Tg2. 573type_tag(bin, macos(A,B), tag(Tg, macos(A,B))) :- !, 574 mac_tag(A, Tg2), 575 Tg is 30+Tg2. 576type_tag(src, Format, tag(40, Format)) :- !. 577type_tag(doc, Format, tag(50, Format)) :- !. 578type_tag(X, Y, tag(60, X-Y)). 579 580mac_tag(bundle, 4). 581mac_tag(snow_leopard_and_later, 5). 582mac_tag(lion, 6). 583mac_tag(snow_leopard, 7). 584mac_tag(leopard, 8). 585mac_tag(tiger, 9). 586 587win_tag(win64, 1). 588win_tag(win32, 2). 589 590sort_group_by_version(Tag-Files, Tag-Sorted) :- 591 map_list_to_pairs(tag_version, Files, TFiles), 592 keysort(TFiles, TRevSorted), 593 pairs_values(TRevSorted, RevSorted), 594 reverse(RevSorted, Sorted). 595 596tag_version(File, Tag) :- 597 File = file(_,_,Version,_,_), 598 version_tag(Version, Tag). 599 600version_tag(version(Major, Minor, Patch, Tag), 601 version(Major, Minor, Patch, Order)) :- 602 ( pre_version(Tag, Order) 603 -> true 604 ; print_message(error, 605 error(domain_error(pre_release_version, Tag),_)), 606 Order = pre(-100, 0) 607 ). 608 609pre_version('', pre(0, 0)) :- !. 610pre_version(NrA, pre(0, 0)) :- 611 atom_number(NrA, _Nr), !. 612pre_version(Tag, pre(TagOrder, N)) :- 613 tag(TagPrefix, TagOrder), 614 atom_concat(TagPrefix, NA, Tag), 615 atom_number(NA, N). 616 617tag(rc, -1). 618tag(beta, -2). 619tag(alpha, -3). 620 621take_latest([], []). 622take_latest([_-[H|_]|T0], [H|T]) :- !, 623 take_latest(T0, T). 624take_latest([_-[]|T0], T) :- !, % emty set 625 take_latest(T0, T).
629old_tagged_file(tag(_,Type)-_File) :- 630 old_file_type(Type). 631 632old_file_type(linux(_)). 633old_file_type(linux(_,_)). 634old_file_type(macos(_,ppc)). 635old_file_type(macos(tiger,_)). 636old_file_type(macos(snow_leopard_and_later,_)). 637 638 639 /******************************* 640 * DOWNLOAD * 641 *******************************/
latest
you get an HTTP
303 (See Other) reply pointing at the latest version.652download(Request) :- 653 memberchk(path_info(Download), Request), 654 file_name_extension(File, envelope, Download), !, 655 envelope(File). 656download(Request) :- 657 memberchk(path_info(Download), Request), 658 ( file_name_extension(File, sha256, Download) 659 -> true 660 ; File = Download 661 ), 662 download_file(File, AbsFile), 663 cors_enable, 664 format('Cross-origin-resource-policy: cross-origin\n'), 665 ( File == Download 666 -> http_peer(Request, Remote), 667 broadcast(download(File, Remote)), 668 http_reply_file(AbsFile, [unsafe(true)], Request) 669 ; file_checksum(AbsFile, SHA256), 670 format('Content-type: text/plain~n~n'), 671 format('~w~n', [SHA256]) 672 ). 673download(Request) :- 674 memberchk(path_info(Download), Request), 675 classify_file(Download, file(Class,Platform,latest,_,_), [show(last)]), 676 file_directory_name(Download, Dir), 677 absolute_file_name(download(Dir), 678 AbsDir, 679 [ access(read), 680 file_type(directory), 681 file_errors(fail) 682 ]), 683 download_files(AbsDir, Class, Files, [show(last)]), 684 memberchk(file(Class, Platform, _, File, _), Files), !, 685 directory_file_path(Dir, File, Redirect), 686 http_link_to_id(download, path_postfix(Redirect), URI), 687 http_redirect(see_other, URI, Request). 688download(Request) :- 689 ( memberchk(path_info(Download), Request) 690 -> true 691 ; Download = '.' 692 ), 693 absolute_file_name(download(Download), 694 AbsFile, 695 [ access(read), 696 file_errors(fail), 697 file_type(directory) 698 ]), !, 699 http_reply_dirindex(AbsFile, 700 [ unsafe(true), 701 name(name_cell) 702 ], Request). 703download(Request) :- 704 memberchk(path(Path), Request), 705 existence_error(http_location, Path). 706 707download_file(File, AbsFile) :- 708 absolute_file_name(download(File), 709 AbsFile, 710 [ access(read), 711 file_errors(fail) 712 ]). 713 714:- public 715 name_cell//1. 716 717name_cell(File) --> 718 { needs_envelope(File), 719 file_base_name(File, Name), 720 uri_encoded(path, Name, Ref0), 721 file_name_extension(Ref0, envelope, Ref) 722 }, 723 html(a(href(Ref), Name)). 724name_cell(File) --> 725 { file_base_name(File, Name), 726 uri_encoded(path, Name, Ref) 727 }, 728 html(a(href(Ref), Name)).
734download_daily(_Request) :- 735 absolute_file_name(download('daily/bin'), Dir, 736 [ file_type(directory), 737 access(read) 738 ]), 739 reply_html_page( 740 download(Dir, 'Download daily builds for Windows'), 741 title('Download daily builds for Windows'), 742 [ \explain_win_daily, 743 \directory_index(Dir, 744 [ order_by(time), 745 order(descending), 746 name(name_cell) 747 ]) 748 ]). 749 750 751explain_win_daily --> 752 html({|html|| 753 <p>The table below provides access to the most recent 7 754 daily builds of SWI-Prolog for Windows, both the 32- and 755 64-bit versions. The build is done automatically from the 756 <a href="/git/">GIT sources</a>. The files use the following 757 naming convention: 758 </p> 759 <ul> 760 <li><code>swipl-w</code><var>bits</var><code>-</code><var>date</var><code>.exe</code> 761 </ul> 762 <p> 763 Please note that these versions <b>may be unstable!</b> It is 764 adviced to follow current discussions on the 765 <a href="/Mailinglist.html">mailing 766 list</a> and/or the git commit messages at 767 <a href="https://github.com/SWI-Prolog/swipl-devel">GitHub</a>. 768 The primary purpose of the daily builds is to quickly provide 769 binaries after a bug report. 770 </p> 771 |}). 772 773 774 /******************************* 775 * ENVELOPE * 776 *******************************/ 777 778needs_envelope(File) :- 779 file_name_extension(_, exe, File). 780 781add_envelope(File, Envelope) :- 782 needs_envelope(File), 783 !, 784 file_name_extension(File, envelope, Envelope). 785add_envelope(File, File). 786 787envelope(File) :- 788 maybe(0.1), 789 download_file(File, AbsFile), 790 file_checksum(AbsFile, OkHash), 791 compute_file_checksum(AbsFile, NewHash), 792 NewHash \== OkHash, 793 !, 794 reply_html_page( 795 download(File, 'Possibly tampered binary'), 796 title('Possibly tampered binary'), 797 \tampered(File, OkHash, NewHash)). 798envelope(File) :- 799 file_base_name(File, Base), 800 reply_html_page( 801 download(Base, 'Download binary'), 802 title('Download a binary file'), 803 \envelope(File)). 804 805envelope(File) --> 806 { http_absolute_location(icons('alert.gif'), Alert, []), 807 http_absolute_location(icons('vt_logo.png'), VTLogo, []), 808 download_file(File, AbsFile), 809 file_checksum(AbsFile, Hash), 810 file_base_name(File, Base), 811 format(atom(VTHREF), 'https://www.virustotal.com/file/~w/analysis/', Hash) 812 }, 813 html({|html(Base, Hash, VTHREF, VTLogo, Alert)|| 814<p><img src=Alert style="float:left"> 815Windows antivirus software works using <i>signatures</i> and <i>heuristics</i>. 816Using the huge amount of virusses and malware known today, arbitrary executables 817are often <a href="https://en.wikipedia.org/wiki/Antivirus_software#Problems_caused_by_false_positives">falsily classified as malicious</a>. 818<a href="https://safebrowsing.google.com/">Google Safe Browsing</a>, used by 819most modern browsers, therefore often classifies our Windows binaries as 820malware. You can use e.g., <a href="https://www.virustotal.com/gui/home/url">virustotal</a> to verify files with a large number of antivirus programs. 821</p> 822 823<p> 824Our Windows binaries are cross-compiled on an isolated Linux container. The 825integrity of the binaries on the server is regularly verified by validating its 826SHA256 fingerprint. 827</p> 828 829<p> 830Please select the checkbox below to enable the actual download link. 831</p> 832 833<table> 834<tr><td><input type="checkbox" id="understand"><td>I understand</tr> 835<tr><td><td><a id="download">Download <code>Base</code></a> 836<span style="color:#888; font-size:small;">(SHA256: <code>Hash</code>)</span></tr> 837<tr><td style="text-align:right"><img src=VTLogo style="width:1.5ex"><td><a href=VTHREF>VIRUSTOTAL Scan Result</a></tr> 838</table> 839 |}), 840 js_script({|javascript(Base)|| 841$(function() { 842 $("#understand").prop("checked", false) 843 .on("click", function() { 844 $("#download").attr("href", Base); 845 }); 846}); 847 848 |}). 849 850tampered(File, OkHash, NewHash) --> 851 { http_absolute_location(icons('alert.gif'), Alert, []) 852 }, 853 html({|html(File, Alert, OkHash, NewHash)|| 854<p><img src=Alert style="float:left"> 855The file <code>File</code> SHA256 signature has changed. Please 856report this at <a href="mailto:bugs@swi-prolog.org">bugs@swi-prolog.org</a> 857 |}). 858 859 860 /******************************* 861 * CHECKSUMS * 862 *******************************/ 863 864:- persistent 865 sha256(path:atom, 866 sha256:atom). 867 868attach_db :- 869 db_attached('checksum.db'), !. 870attach_db :- 871 db_attach('checksum.db', []).
884file_checksum(Path, Sum) :- 885 attach_db, 886 sha256(Path, Sum0), !, 887 Sum = Sum0. 888file_checksum(Path, Sum) :- 889 compute_file_checksum(Path, Sum). 890 891compute_file_checksum(Path, Sum) :- 892 crypto_file_hash(Path, Sum, 893 [ encoding(octet), 894 algorithm(sha256) 895 ]), 896 assert_sha256(Path, Sum)