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