feat: implement web service

Signed-off-by: The one with the braid <info@braid.business>
This commit is contained in:
The one with the braid 2023-11-26 19:42:29 +01:00
parent 44494eaa90
commit 6e7f19a764
26 changed files with 331 additions and 512 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@
# Avoid committing pubspec.lock for library packages; see # Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock. # https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock pubspec.lock
.idea

3
.idea/.gitignore generated vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -1,444 +0,0 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="_fe_analyzer_shared">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-64.0.0/lib" />
</list>
</value>
</entry>
<entry key="analyzer">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/analyzer-6.2.0/lib" />
</list>
</value>
</entry>
<entry key="archive">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/archive-3.3.8/lib" />
</list>
</value>
</entry>
<entry key="args">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/args-2.4.2/lib" />
</list>
</value>
</entry>
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/async-2.11.0/lib" />
</list>
</value>
</entry>
<entry key="barcode">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/barcode-2.2.4/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/clock-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/collection-1.18.0/lib" />
</list>
</value>
</entry>
<entry key="convert">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/convert-3.1.1/lib" />
</list>
</value>
</entry>
<entry key="coverage">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/coverage-1.6.3/lib" />
</list>
</value>
</entry>
<entry key="crypto">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/crypto-3.0.3/lib" />
</list>
</value>
</entry>
<entry key="file">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/file-7.0.0/lib" />
</list>
</value>
</entry>
<entry key="frontend_server_client">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/frontend_server_client-3.2.0/lib" />
</list>
</value>
</entry>
<entry key="glob">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/glob-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="http_multi_server">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/http_multi_server-3.2.1/lib" />
</list>
</value>
</entry>
<entry key="http_parser">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/http_parser-4.0.2/lib" />
</list>
</value>
</entry>
<entry key="import_sorter">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/import_sorter-4.6.0/lib" />
</list>
</value>
</entry>
<entry key="intl">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/intl-0.18.1/lib" />
</list>
</value>
</entry>
<entry key="io">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/io-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="js">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/js-0.6.7/lib" />
</list>
</value>
</entry>
<entry key="lints">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/lints-2.1.1/lib" />
</list>
</value>
</entry>
<entry key="logging">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/logging-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/matcher-0.12.16/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/meta-1.9.1/lib" />
</list>
</value>
</entry>
<entry key="mime">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/mime-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="node_preamble">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/lib" />
</list>
</value>
</entry>
<entry key="package_config">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/package_config-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/path-1.8.3/lib" />
</list>
</value>
</entry>
<entry key="pointycastle">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/pointycastle-3.7.3/lib" />
</list>
</value>
</entry>
<entry key="pool">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/pool-1.5.1/lib" />
</list>
</value>
</entry>
<entry key="pub_semver">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/pub_semver-2.1.4/lib" />
</list>
</value>
</entry>
<entry key="qr">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/qr-3.0.1/lib" />
</list>
</value>
</entry>
<entry key="shelf">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/shelf-1.4.1/lib" />
</list>
</value>
</entry>
<entry key="shelf_packages_handler">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2/lib" />
</list>
</value>
</entry>
<entry key="shelf_static">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_static-1.1.2/lib" />
</list>
</value>
</entry>
<entry key="shelf_web_socket">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_web_socket-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="source_map_stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.1/lib" />
</list>
</value>
</entry>
<entry key="source_maps">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/source_maps-0.10.12/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/source_span-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/stack_trace-1.11.1/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/stream_channel-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/string_scanner-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/term_glyph-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="test">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/test-1.24.6/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/test_api-0.6.1/lib" />
</list>
</value>
</entry>
<entry key="test_core">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/test_core-0.5.6/lib" />
</list>
</value>
</entry>
<entry key="tint">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/tint-2.0.1/lib" />
</list>
</value>
</entry>
<entry key="typed_data">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/typed_data-1.3.2/lib" />
</list>
</value>
</entry>
<entry key="vm_service">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/vm_service-11.10.0/lib" />
</list>
</value>
</entry>
<entry key="watcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/watcher-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="web_socket_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.0/lib" />
</list>
</value>
</entry>
<entry key="webkit_inspection_protocol">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="yaml">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dev/yaml-3.1.2/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-64.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/analyzer-6.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/archive-3.3.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/args-2.4.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/async-2.11.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/barcode-2.2.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/clock-1.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/collection-1.18.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/convert-3.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/coverage-1.6.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/crypto-3.0.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/file-7.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/frontend_server_client-3.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/glob-2.1.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/http_multi_server-3.2.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/http_parser-4.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/import_sorter-4.6.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/intl-0.18.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/io-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/js-0.6.7/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/lints-2.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/logging-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/matcher-0.12.16/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/meta-1.9.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/mime-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/package_config-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/path-1.8.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/pointycastle-3.7.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/pool-1.5.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/pub_semver-2.1.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/qr-3.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/shelf-1.4.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_static-1.1.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/shelf_web_socket-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/source_maps-0.10.12/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/source_span-1.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/stack_trace-1.11.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/stream_channel-2.1.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/string_scanner-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/term_glyph-1.2.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/test-1.24.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/test_api-0.6.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/test_core-0.5.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/tint-2.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/typed_data-1.3.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/vm_service-11.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/watcher-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dev/yaml-3.1.2/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View file

@ -1,29 +0,0 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js_interop" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js_interop_unsafe" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

6
.idea/misc.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated
View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/pkpass.iml" filepath="$PROJECT_DIR$/pkpass.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -22,7 +22,7 @@ Some parts of the PkPass specification are either not yet implemented, or not pl
- `signature`: The detached PKCS #7 signature using Apple certificates of the manifest. Note: Checksums _are_ checked. - - `signature`: The detached PKCS #7 signature using Apple certificates of the manifest. Note: Checksums _are_ checked. -
Not planned, feel free to contribute. Not planned, feel free to contribute.
- `nfc`: Card payment information for Apple Pay. - Not planned, feel free to contribute. - `nfc`: Card payment information for Apple Pay. - Not planned, feel free to contribute.
- `webService`: Information used to update passes using the web service. - Planned, feel free to contribute. - `webService`: Only pull to refresh supported. Push service not implemented yet. - Planned, feel free to contribute.
## Localizations ## Localizations

View file

@ -3,11 +3,11 @@
/// More dartdocs go here. /// More dartdocs go here.
library pkpass; library pkpass;
export 'src/error.dart'; export 'pkpass/error.dart';
export 'src/models/barcode.dart'; export 'pkpass/models/barcode.dart';
export 'src/models/beacon.dart'; export 'pkpass/models/beacon.dart';
export 'src/models/location.dart'; export 'pkpass/models/location.dart';
export 'src/models/pass.dart'; export 'pkpass/models/pass.dart';
export 'src/models/pass_structure_dictionary.dart'; export 'pkpass/models/pass_structure_dictionary.dart';
export 'src/models/pass_web_service.dart'; export 'pkpass/models/pass_web_service.dart';
export 'src/pass_file.dart'; export 'pkpass/pass_file.dart';

View file

@ -1,7 +1,7 @@
import 'package:intl/locale.dart'; import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart'; import 'package:pkpass/pkpass.dart';
import 'package:pkpass/src/utils/mabe_decode.dart'; import 'package:pkpass/pkpass/utils/mabe_decode.dart';
/// Information that is required for all passes. /// Information that is required for all passes.
class PassMetadata { class PassMetadata {

View file

@ -1,7 +1,7 @@
import 'package:intl/locale.dart'; import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart'; import 'package:pkpass/pkpass.dart';
import 'package:pkpass/src/utils/mabe_decode.dart'; import 'package:pkpass/pkpass/utils/mabe_decode.dart';
/// Keys that define the structure of the pass. /// Keys that define the structure of the pass.
/// ///

View file

@ -1,5 +1,3 @@
/// TODO: implement PassKit Web Service Reference
///
/// Metadata required for Pass Web Service /// Metadata required for Pass Web Service
/// ///
/// https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988 /// https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988

View file

@ -6,8 +6,8 @@ import 'package:crypto/crypto.dart';
import 'package:intl/locale.dart'; import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart'; import 'package:pkpass/pkpass.dart';
import 'package:pkpass/src/utils/file_matcher.dart'; import 'package:pkpass/pkpass/utils/file_matcher.dart';
import 'package:pkpass/src/utils/lproj_parser.dart'; import 'package:pkpass/pkpass/utils/lproj_parser.dart';
final _utf8codec = Utf8Codec(); final _utf8codec = Utf8Codec();
final _jsonCodec = JsonCodec(); final _jsonCodec = JsonCodec();

View file

@ -0,0 +1,9 @@
/// The PkPass Web Service.
///
/// So far only supports update check but no push service.
///
/// https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988
library pkpass_web_service;
export 'pkpass_web_wervice/web_service.dart';
export 'pkpass_web_wervice/web_service_error.dart';

View file

@ -0,0 +1,219 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// copied from dart:io
/// Utility functions for working with dates with HTTP specific date
/// formats.
class HttpDate {
// From RFC-2616 section "3.3.1 Full Date",
// http://tools.ietf.org/html/rfc2616#section-3.3.1
//
// HTTP-date = rfc1123-date | rfc850-date | asctime-date
// rfc1123-date = wkday "," SP date1 SP time SP "GMT"
// rfc850-date = weekday "," SP date2 SP time SP "GMT"
// asctime-date = wkday SP date3 SP time SP 4DIGIT
// date1 = 2DIGIT SP month SP 4DIGIT
// ; day month year (e.g., 02 Jun 1982)
// date2 = 2DIGIT "-" month "-" 2DIGIT
// ; day-month-year (e.g., 02-Jun-82)
// date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
// ; month day (e.g., Jun 2)
// time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
// ; 00:00:00 - 23:59:59
// wkday = "Mon" | "Tue" | "Wed"
// | "Thu" | "Fri" | "Sat" | "Sun"
// weekday = "Monday" | "Tuesday" | "Wednesday"
// | "Thursday" | "Friday" | "Saturday" | "Sunday"
// month = "Jan" | "Feb" | "Mar" | "Apr"
// | "May" | "Jun" | "Jul" | "Aug"
// | "Sep" | "Oct" | "Nov" | "Dec"
/// Format a date according to
/// [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
/// e.g. `Thu, 1 Jan 1970 00:00:00 GMT`.
static String format(DateTime date) {
const List wkday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const List month = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
DateTime d = date.toUtc();
StringBuffer sb = StringBuffer()
..write(wkday[d.weekday - 1])
..write(", ")
..write(d.day <= 9 ? "0" : "")
..write(d.day.toString())
..write(" ")
..write(month[d.month - 1])
..write(" ")
..write(d.year.toString())
..write(d.hour <= 9 ? " 0" : " ")
..write(d.hour.toString())
..write(d.minute <= 9 ? ":0" : ":")
..write(d.minute.toString())
..write(d.second <= 9 ? ":0" : ":")
..write(d.second.toString())
..write(" GMT");
return sb.toString();
}
/// Parse a date string in either of the formats
/// [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
/// [RFC-850](http://tools.ietf.org/html/rfc850 "RFC-850") or
/// ANSI C's asctime() format. These formats are listed here.
///
/// Thu, 1 Jan 1970 00:00:00 GMT
/// Thursday, 1-Jan-1970 00:00:00 GMT
/// Thu Jan 1 00:00:00 1970
///
/// For more information see [RFC-2616 section
/// 3.1.1](http://tools.ietf.org/html/rfc2616#section-3.3.1
/// "RFC-2616 section 3.1.1").
static DateTime parse(String date) {
final int sp = 32;
const List wkdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const List weekdays = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
const List months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
final int formatRfc1123 = 0;
final int formatRfc850 = 1;
final int formatAsctime = 2;
int index = 0;
String tmp;
void expect(String s) {
if (date.length - index < s.length) {
throw Exception("Invalid HTTP date $date");
}
String tmp = date.substring(index, index + s.length);
if (tmp != s) {
throw Exception("Invalid HTTP date $date");
}
index += s.length;
}
int expectWeekday() {
int weekday;
// The formatting of the weekday signals the format of the date string.
int pos = date.indexOf(",", index);
if (pos == -1) {
int pos = date.indexOf(" ", index);
if (pos == -1) throw Exception("Invalid HTTP date $date");
tmp = date.substring(index, pos);
index = pos + 1;
weekday = wkdays.indexOf(tmp);
if (weekday != -1) {
return formatAsctime;
}
} else {
tmp = date.substring(index, pos);
index = pos + 1;
weekday = wkdays.indexOf(tmp);
if (weekday != -1) {
return formatRfc1123;
}
weekday = weekdays.indexOf(tmp);
if (weekday != -1) {
return formatRfc850;
}
}
throw Exception("Invalid HTTP date $date");
}
int expectMonth(String separator) {
int pos = date.indexOf(separator, index);
if (pos - index != 3) throw Exception("Invalid HTTP date $date");
tmp = date.substring(index, pos);
index = pos + 1;
int month = months.indexOf(tmp);
if (month != -1) return month;
throw Exception("Invalid HTTP date $date");
}
int expectNum(String separator) {
int pos;
if (separator.isNotEmpty) {
pos = date.indexOf(separator, index);
} else {
pos = date.length;
}
String tmp = date.substring(index, pos);
index = pos + separator.length;
try {
int value = int.parse(tmp);
return value;
} on FormatException {
throw Exception("Invalid HTTP date $date");
}
}
void expectEnd() {
if (index != date.length) {
throw Exception("Invalid HTTP date $date");
}
}
int format = expectWeekday();
int year;
int month;
int day;
int hours;
int minutes;
int seconds;
if (format == formatAsctime) {
month = expectMonth(" ");
if (date.codeUnitAt(index) == sp) index++;
day = expectNum(" ");
hours = expectNum(":");
minutes = expectNum(":");
seconds = expectNum(" ");
year = expectNum("");
} else {
expect(" ");
day = expectNum(format == formatRfc1123 ? " " : "-");
month = expectMonth(format == formatRfc1123 ? " " : "-");
year = expectNum(" ");
hours = expectNum(":");
minutes = expectNum(":");
seconds = expectNum(" ");
expect("GMT");
}
expectEnd();
return DateTime.utc(year, month + 1, day, hours, minutes, seconds, 0);
}
}

View file

@ -0,0 +1,69 @@
import 'dart:typed_data';
import 'package:http/http.dart';
import 'package:pkpass/pkpass.dart';
import 'package:pkpass/pkpass_web_wervice/utils/http_date.dart';
import 'web_service_error.dart';
/// PassKit WebService implementation
///
/// A Representational State Transfer (REST)style web service protocol is used
/// to communicate with your server about changes to passes, and to fetch the
/// latest version of a pass when it has changed.
///
/// https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988
class PkPassWebService {
static const _apiVersion = 'v1';
static Client? _client;
/// The [PassMetadata] to check for updates.
final PassMetadata metadata;
/// An optional [Client] used for any http requests
final Client? client;
const PkPassWebService(this.metadata, {this.client});
PassWebService get webService {
final service = metadata.webService;
if (service == null) noWebServiceProvided();
return service;
}
Client get httpClient {
final client = this.client;
if (client != null) return client;
return _client ??= Client();
}
/// Getting the Latest Version of a Pass
///
/// Requests the latest version of the current PkPass file
///
/// [modifiedSince] should be provided in order to support "304 Not Modified"
Future<Uint8List?> getLatestVersion([DateTime? modifiedSince]) async {
final identifier = metadata.passTypeIdentifier;
final serial = metadata.serialNumber;
final endpoint = '/$_apiVersion/passes/$identifier/$serial';
final response = await httpClient.get(
Uri.parse(webService.webServiceURL.toString() + endpoint),
headers: {
if (modifiedSince != null)
'If-Modified-Since': HttpDate.format(modifiedSince),
'Authorization': 'ApplePass ${webService.authenticationToken}',
},
);
switch (response.statusCode) {
case 200:
return response.bodyBytes;
case 304:
return null;
default:
throw WebServiceResponseError(response);
}
}
Never noWebServiceProvided() => throw WebServiceUnavailable();
}

View file

@ -0,0 +1,19 @@
import 'package:http/http.dart';
import 'package:pkpass/pkpass.dart';
abstract class PkPassWebServiceError extends PKPassError {
PkPassWebServiceError({required super.message});
}
class WebServiceUnavailable extends PkPassWebServiceError {
WebServiceUnavailable()
: super(message: 'The PkPass file does not contain any web service.');
}
class WebServiceResponseError extends PkPassWebServiceError {
final Response response;
WebServiceResponseError(this.response)
: super(message: 'Unexpected response from web service');
}

View file

@ -5,7 +5,7 @@ import 'package:intl/locale.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:pkpass/pkpass.dart'; import 'package:pkpass/pkpass.dart';
import 'package:pkpass/src/utils/file_matcher.dart'; import 'package:pkpass/pkpass/utils/file_matcher.dart';
void main() { void main() {
final archive = [ final archive = [