diff --git a/.gitignore b/.gitignore index 50371eb..93cd494 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,27 @@ Makefile.in +Makefile aclocal.m4 -autom4te.cache/ +autom4te.cache compile config.guess config.sub +config.log +config.h +config.status configure +stamp-h1 depcomp install-sh missing *.o *~ +.deps +.dirstamp +dot_cardpeek.tar.gz +cardpeek +cardpeek.exe +cardpeek_resources.c +win32/libwinscard.a +deps +build +.vscode diff --git a/.whitesource b/.whitesource new file mode 100644 index 0000000..f340c5d --- /dev/null +++ b/.whitesource @@ -0,0 +1,8 @@ +########################################################## +#### WhiteSource Integration configuration file #### +########################################################## + +# Configuration # +#---------------# +ws.repo.scan=true +vulnerable.check.run.conclusion.level=failure diff --git a/INSTALL b/INSTALL index 2099840..115a9aa 100644 --- a/INSTALL +++ b/INSTALL @@ -1,3 +1,6 @@ +(Note: this is the standard GNU documentation for installs. For more specific +documentation, please refer to README.md) + Installation Instructions ************************* diff --git a/Makefile.am b/Makefile.am index 7e8dd06..b882a16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -76,7 +76,7 @@ OSX_EXTRA=osx/cardpeek-launcher.sh osx/cardpeek.bundle osx/generate_bundle.sh os CLEANFILES=cardpeek_resources.c dot_cardpeek.tar.gz osx/cardpeek.icns -EXTRA_DIST=cardpeek_resources.gresource.xml cardpeek_resources.gresource $(DRIVERS) $(ICONS) dot_cardpeek_dir doc/cardpeek_ref.en.pdf doc/cardpeek_ref.en.odt update_dot_cardpeek_dir.sh script_version.h cardpeek.desktop cardpeek.appdata.xml $(OSX_ICONS) INSTALL.FreeBSD +EXTRA_DIST=cardpeek_resources.gresource.xml cardpeek_resources.gresource $(DRIVERS) $(ICONS) dot_cardpeek_dir doc/cardpeek_ref.en.pdf doc/cardpeek_ref.en.odt update_dot_cardpeek_dir.sh script_version.h cardpeek.desktop cardpeek.appdata.xml $(OSX_ICONS) GLIB_COMPILE_RESOURCES=@GLIB_COMPILE_RESOURCES@ @@ -97,7 +97,7 @@ dist-hook: dot_cardpeek.tar.gz: dot_cardpeek_dir @echo " TAR $<" - $(AM_V_at)tar -c -z -f dot_cardpeek.tar.gz --directory $(srcdir)/dot_cardpeek_dir --exclude=.svn --exclude='\._*' . + $(AM_V_at)tar --help|grep -q sort= && taropts="--sort=name --clamp-mtime --format=gnu --owner=0 --group=0" ; tar -c $$taropts --directory $(srcdir)/dot_cardpeek_dir --exclude=.svn --exclude='\._*' . | gzip -cn9 > dot_cardpeek.tar.gz cardpeek_resources.$(OBJEXT): dot_cardpeek.tar.gz $(ICONS) AUTHORS COPYING cardpeek_resources.gresource.xml @echo " GLIB_COMPILE_RESOURCES cardpeek_resources.gresource.xml" diff --git a/Makefile.win32 b/Makefile.win32 index 2ba4b07..5d41b3a 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -1,4 +1,8 @@ -CC = gcc +DEPS = /mingw32 +TARGET = /C/Program Files (x86)/Cardpeek +MPLATFORM = mingw32 + +CC = $(CCPRE)gcc GLIB_COMPILE_RESOURCES = glib-compile-resources OBJECTS = asn1.o bytestring.o ui/gtk/gui_flexi_cell_renderer.o dyntree_model.o \ @@ -18,14 +22,18 @@ ICONS=icons/cardpeek-analyzer.png icons/cardpeek-item.png icons/cardpeek-record. icons/cardpeek-application.png icons/cardpeek-file.png icons/cardpeek-block.png \ icons/cardpeek-atr.png icons/cardpeek-header.png icons/cardpeek-body.png -LIBS = `pkg-config --libs gtk+-3.0` -L/usr/local/lib -lcurl -L/usr/lib -lssl -lcrypto -llua -L./win32/ -lwinscard -liconv -lreadline -lncurses +export PKG_CONFIG_PATH=$(DEPS)/lib/pkgconfig +PKG_LIST = gtk+-3.0 lua openssl iconv libcurl +PKG_CFLAGS = `$(CCPRE)pkg-config --cflags $(PKG_LIST) | sed "s:-I/$(MPLATFORM):-I$(DEPS):g"` +PKG_LIBS = `$(CCPRE)pkg-config --libs $(PKG_LIST) | sed "s:-L/$(MPLATFORM):-L$(DEPS):g"` -CFLAGS = -Wall -pedantic -Wno-overlength-strings -Wno-long-long -DCURL_STATICLIB -D__USE_MINGW_ANSI_STDIO -I . -I /usr/include -I ./win32/ `pkg-config --cflags gtk+-3.0` +CFLAGS = -Wall -pedantic -Wno-overlength-strings -Wno-long-long -DCURL_STATICLIB -D__USE_MINGW_ANSI_STDIO -I . -I $(DEPS)/include -I ./win32/ $(PKG_CFLAGS) +LIBS = $(PKG_LIBS) -L$(DEPS)/bin -L./win32/ -lwinscard -lreadline -lncurses -all: cardpeek +build: cardpeek.exe -cardpeek: $(OBJECTS) win32/libwinscard.a - $(CC) -Wl,-subsystem,windows $(OBJECTS) $(LIBS) -o cardpeek +cardpeek.exe: $(OBJECTS) #win32/libwinscard.a + $(CC) -Wl,-subsystem,windows $(OBJECTS) $(LIBS) -o cardpeek.exe dot_cardpeek.tar.gz: dot_cardpeek_dir @echo " TAR $<" @@ -37,11 +45,71 @@ cardpeek_resources.o: dot_cardpeek.tar.gz $(ICONS) AUTHORS COPYING cardpeek_reso @echo "COMPILED RESOURCES" win32/resource.o: win32/resource.rc - windres win32/resource.rc -O coff -o win32/resource.o + $(CCPRE)windres win32/resource.rc -O coff -o win32/resource.o -win32/libwinscard.a: win32/winscard.def win32/winscard.h - dlltool.exe -k -d win32/winscard.def -l win32/libwinscard.a c:/WINDOWS/system32/winscard.dll +#TODO: shouldn't be needed. To be confirmed. (It seems at least that it's not needed to compile) +#win32/libwinscard.a: win32/winscard.def win32/winscard.h +# $(CCPRE)dlltool -k -d win32/winscard.def -l win32/libwinscard.a win32/winscard.dll clean: rm -f $(OBJECTS) cardpeek.exe win32/libwinscard.a dot_cardpeek.tar.gz *~ cardpeek_resources.c +install: cardpeek.exe + mkdir -p "$(TARGET)" + cp -u cardpeek.exe "$(TARGET)/" +ifeq (mingw64, $(MPLATFORM)) + cp -u $(DEPS)/bin/libgcc_s_seh-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libcrypto-1_1-x64.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libssl-1_1-x64.dll "$(TARGET)/" +else + ifneq (/mingw32, $(DEPS)) + # not needed from msys2 - TODO: eliminate from debian too () + cp -u $(DEPS)/bin/libgcc_s_sjlj-1.dll "$(TARGET)/" + endif + cp -u $(DEPS)/bin/libgcc_s_dw2-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libcrypto-1_1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libssl-1_1.dll "$(TARGET)/" +endif + cp -u $(DEPS)/bin/libstdc++-6.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libatk-1.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libbrotlidec.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libbrotlicommon.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libbz2-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libcairo-2.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libcairo-gobject-2.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libcurl-4.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libdatrie-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libepoxy-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libexpat-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libffi-6.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libfontconfig-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libfreetype-6.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libfribidi-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgdk-3-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgdk_pixbuf-2.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgio-2.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libglib-2.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgmodule-2.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgobject-2.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgraphite2.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libgtk-3-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libharfbuzz-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libiconv-2.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libidn2-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libintl-8.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libnghttp2-14.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpango-1.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpangocairo-1.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpangoft2-1.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpangowin32-1.0-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpcre-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpixman-1-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpng16-16.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libpsl-5.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libreadline7.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libtermcap-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libthai-0.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libunistring-2.dll "$(TARGET)/" + cp -u $(DEPS)/bin/libwinpthread-1.dll "$(TARGET)/" + cp -u $(DEPS)/bin/lua53.dll "$(TARGET)/" + cp -u $(DEPS)/bin/zlib1.dll "$(TARGET)/" diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..42bb147 --- /dev/null +++ b/README.fr.md @@ -0,0 +1,21 @@ +A propos de Cardpeek +==================== + +_([English version](README.md))_ + +Cardpeek est un outil de lecture de carte à puce avec une interface graphique basée sur GTK, fonctionnant sous GNU Linux/Windows/Mac OS X et extensible par le langage de programmation LUA. + +Cardpeek est un outil qui a pour objectif de vous permettre d’accéder aux informations personnelles qui sont stockées dans vos carte à puce. Vous pouvez ainsi être mieux informé des données qui sont collectées sur vous. + +Dans cette version, l’application est capable de lire le contenu des cartes suivantes : + +* Les cartes à puce bancaires [EMV](doc/emv.fr.md), dont les cartes NFC - _[capture d'écran](doc/sample-emv.jpg)_ +* Les cartes de transport de Paris / ÃŽle de France [Navigo](doc/navigo.fr.md), ainsi que MOBIB (Belgique) et RavKav (Israel) - _[capture d'écran](doc/sample-navigo.jpg)_ +* Les cartes Monéo +* Les cartes Vitales 2 +* Les passports électroniques/biométriques avec une sécurité BAC. +* La carte d'identité belge (eID) +* Les cartes SIM GSM (beta) +* Les cartes Mifare (beta) +* Les cartes conducteur tachygraphes +* Les cartes OpenPGP (beta) diff --git a/README.md b/README.md index ceb7d3c..39277ca 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,43 @@ -**The cardpeek homepage is located at http://pannetrat.com/Cardpeek/** +Cardpeek +======== -About Cardpeek -============== +## About -Cardpeek is a Linux/Windows/Mac OS X tool to read the contents of ISO7816 smart cards. It features a GTK GUI to represent card data is a tree view, and is extendable with a scripting language (LUA). +_([Version française](README.fr.md))_ + +Cardpeek is a Linux/Windows/Mac OS X tool to read the contents of ISO7816 smart cards. It features a GTK GUI to represent card data in a tree view, and is extendable with a scripting language (LUA). The goal of this project is to allow smart card owners to be better informed about what type of personal information is stored in these devices. The tool currently reads the contents of : -* EMV Pin and Chip cards, including NFC ones. -* Navigo public transport cards, MOBIB and RavKav? cards. +* [EMV](doc/emv.md) Pin and Chip cards, including NFC ones - _[screenshot](doc/sample-emv.jpg)_ +* [Navigo](doc/navigo.md) (Paris), MOBIB (Belgium), RavKav (Israel) and other public transport cards of the Calypso family - _[screenshot](doc/sample-navigo.jpg)_ * The French health card "Vitale 2" -* Electronic/Biometric passports in BAC security mode. +* Electronic/Biometric passports in BAC security mode * GSM SIM cards (but not USIM data) * The Belgian eID card -* Driver tachograph cards; -* OpenPGP cards (beta); +* Driver tachograph cards +* OpenPGP cards (beta) It can also read the following cards with limited interpretation of data: -* Some Mifare cards (such as the Thalys card); -* Moneo, the French electronic purse; - -More info here: http://pannetrat.com/Cardpeek/ +* Some Mifare cards (such as the Thalys card) +* Moneo, the French electronic purse -A propos de Cardpeek -==================== +## Build -Cardpeek est un outil de lecture de carte à puce avec une interface graphique basée sur GTK, fonctionnant sous GNU Linux/Windows/Mac OS X et extensible par le langage de programmation LUA. +**!!! Produced binaries do not run yet - See [issue #1](https://github.com/ipamo/cardpeek/issues/1) !!!** -Cardpeek est un outil qui a pour objectif de vous permettre d’accéder aux informations personnelles qui sont stockées dans vos carte à puce. Vous pouvez ainsi être mieux informé des données qui sont collectées sur vous. +- [Build instructions for Debian](doc/build-debian.md), either for the local Debian host, or for cross-compilation to Windows using mingw-w64. +- [Build instructions for Windows](doc/build-windows.md), using msys2. +- [Specific instructions for FreeBSD](doc/build-freebsd.md) in case of errors. -Dans cette version, l’application est capable de lire le contenu des cartes suivantes : +## Usage -* Les cartes à puce bancaires EMV, dont les cartes NFC. -* Les cartes de transport d'île de France Navigo, ainsi que MOBIB(Belgique) et RavKav?(Israel). -* Les cartes Monéo -* Les cartes Vitales 2 -* Les passports électroniques/biométriques avec une sécurité BAC. -* La carte d'identité belge (eID). -* Les cartes SIM GSM (beta). -* Les cartes Mifare (beta). -* Les cartes conducteur tachygraphes. -* Les cartes OpenPGP (beta); +The [Reference Manual](doc/cardpeek_ref.en.pdf) provides detailed usage instructions. -Plus de détails ici : http://pannetrat.com/Cardpeek/ +## Authors +Written initially by Alain Pannetrat under the [GNU General Public License, version 3](COPYING), with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. +More info here: http://pannetrat.com/Cardpeek/ diff --git a/a_string.c b/a_string.c index 8ba5dda..02f0432 100644 --- a/a_string.c +++ b/a_string.c @@ -29,7 +29,7 @@ a_string_t* a_strnnew(unsigned n, const char* str) { - a_string_t* cs = (a_string_t *)malloc(sizeof(a_string_t)); + a_string_t* cs = malloc(sizeof(a_string_t)); //(a_string_t *) if (n) { diff --git a/cardpeek.appdata.xml b/cardpeek.appdata.xml index 8d53c40..5ae9d6a 100644 --- a/cardpeek.appdata.xml +++ b/cardpeek.appdata.xml @@ -30,7 +30,7 @@ - http://cardpeek.googlecode.com/files/sample-emv.jpg + http://4.bp.blogspot.com/-iu2fS_ovwj8/UIqPFKy31lI/AAAAAAAAHx0/WM1qPUbUzxo/s1600/cardpeek+record.png Reading the content of a bank card. diff --git a/cardpeek_update.c b/cardpeek_update.c index 3c8fba2..8ef9ba2 100644 --- a/cardpeek_update.c +++ b/cardpeek_update.c @@ -540,7 +540,7 @@ int cardpeek_update_check(void) } switch (ui_question("Cardpeek is configured to check for script updates periodically.\n" - "Do you whish to perform this check now?", + "Do you wish to perform this check now?", "Yes","No, ask me again later","No, always use the local copy",NULL)) { case 0: diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..6b9b02c --- /dev/null +++ b/clean.sh @@ -0,0 +1,32 @@ +#!/bin/bash +[ -f Makefile ] && make clean +make -f Makefile.win32 clean +for p in $(find -type f -name "*.o"); do + rm "$p" +done +for p in $(find -type f -name "*.dirstamp"); do + rm "$p" +done +for p in $(find -type d -name "*.deps"); do + rm -r "$p" +done +rm -f Makefile +rm -f Makefile.in +rm -f aclocal.m4 +rm -f cardpeek +rm -f cardpeek.exe +rm -f cardpeek_resources.c +rm -f compile +rm -f config.guess +rm -f config.h +rm -f config.log +rm -f config.status +rm -f config.sub +rm -f configure +rm -f depcomp +rm -f install-sh +rm -f missing +rm -f stamp-h1 +rm -f dot_cardpeek.tar.gz +rm -f win32/libwinscard.a +rm -rf autom4te.cache diff --git a/compile-cardpeek.sh b/compile-cardpeek.sh new file mode 100644 index 0000000..e633d78 --- /dev/null +++ b/compile-cardpeek.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Created @ 13.01.2015 by Christian Mayer + + +# You also need to install DBus: +# https://gist.github.com/TheFox/dc3f1b88757ba0a8a7a9 +#1. Xcode 4.6.2 Command Line Tools. +#2. Homebrew (http://mxcl.github.io/homebrew/), used to install libgtk+, liblua and libssl. +#3. XQuartz (http://xquartz.macosforge.org/). + +# Install 'gnome-icon-theme' for GTK+ 3. +brew install gnome-icon-theme +brew install openssl +brew link openssl --force +brew install glib +brew install gtk+3 +brew install curl +brew install lua + +# Check out the source. +pwd + +# Create files for compiling. +autoreconf -fi + +# './configure' may fail with +# Package 'xyz', required by 'abc', not found +# So we need to set the path for PkgConfig. +export PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig +./configure + +# Compile. +make + +# Alternatively you can install +# cardpeek into your system. +#make install diff --git a/configure.ac b/configure.ac index d5fa0ea..f89b22e 100644 --- a/configure.ac +++ b/configure.ac @@ -239,7 +239,7 @@ if test "$HOST_TYPE" = "freebsd" -a ! "$ICONV_LIBS"; then AC_MSG_WARN([ ******** -On FreeBSD, you may need to specify the environement variable ICONV_LIBS='-l iconv' before running 'configure', as detailed in INSTALL.FreeBSD. Otherwise 'make' may fail. +On FreeBSD, you may need to specify the environement variable ICONV_LIBS='-l iconv' before running 'configure', as detailed in doc/build-freebsd.md. Otherwise 'make' may fail. ********]) fi diff --git a/deps-win32.sh b/deps-win32.sh new file mode 100755 index 0000000..d5ac950 --- /dev/null +++ b/deps-win32.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# +# Download and extract dependencies for cross-compilation from Debian for Windows. +# See doc/build-debian.md +# +RED='\033[0;31m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +PLATFORM=${PLATFORM=-win32} +DEPS=deps/$PLATFORM +if [[ `uname -s` == MINGW* ]] || [[ `uname -s` == MSYS* ]]; then + echo -e "${RED}This script should not be run from msys${NC}" + exit 1 +fi + +function import_signing_key { + # + # Import Alexey Pavlov (maintener of mingw-w64 repository) signing key, if necessary + # + local keyid=AD351C50AE085775EB59333B5F92EFC1A47D45A1 + gpg --list-keys --keyid-format LONG | grep $keyid > /dev/null + if [ $? -eq 0 ]; then + return 0 + fi + echo -e "${YELLOW}Import signing key${NC}" + gpg --recv-key $keyid +} + +function get_package { + # + # Download (if necessary), verify, and extract a package. + # @param $1: Name of the package + # + local name=$1 + local mplatform=mingw32 + local iplatform=i686 + if [ "$PLATFORM" == "win64" ]; then + mplatform=mingw64 + iplatform=x86_64 + fi + + if [[ $name == mingw-w64-cross-* ]]; then + # special package to retrieve libgcc_s_dw2-1.dll + local archive_src=http://repo.msys2.org/msys/i686/$name.tar.xz + local archive_dst=$DEPS/archives/$name.tar.xz + + local sign_src=http://repo.msys2.org/msys/i686/$name.tar.xz.sig + local sign_dst=$DEPS/archives/$name.tar.xz.sig + else + local archive_src=http://repo.msys2.org/mingw/$iplatform/mingw-w64-$iplatform-$name.pkg.tar.xz + local archive_dst=$DEPS/archives/mingw-w64-$iplatform-$name.pkg.tar.xz + + local sign_src=http://repo.msys2.org/mingw/$iplatform/mingw-w64-$iplatform-$name.pkg.tar.xz.sig + local sign_dst=$DEPS/archives/mingw-w64-$iplatform-$name.pkg.tar.xz.sig + fi + + mkdir -p $DEPS/archives + + # Download + if [ ! -f $archive_dst ]; then + echo -e "${YELLOW}Download $name (package)${NC}" + curl -o $archive_dst $archive_src + fi + if [ ! -f $sign_dst ]; then + echo -e "${YELLOW}Download $name (sig)${NC}" + curl -o $sign_dst $sign_src + fi + + # Verify + echo -e "${YELLOW}Check $name${NC}" + gpg --verify $sign_dst $archive_dst + if [ ! $? -eq 0 ]; then + echo -e "${RED}Signature error for $name${NC}" + exit 1 + fi + + # Extract + echo -e "${YELLOW}Extract $name${NC}" + + if [[ $name == mingw-w64-cross-* ]]; then + tar -C $DEPS -xf $archive_dst + else + tar -C $DEPS -xf $archive_dst $mplatform/ --strip-components=1 + fi + + if [ ! $? -eq 0 ]; then + echo -e "${RED}Cannot extract $name${NC}" + exit 1 + fi +} + +packages=( + atk-2.30.0-1-any + brotli-1.0.7-1-any + bzip2-1.0.6-6-any + cairo-1.16.0-1-any + curl-7.62.0-1-any + expat-2.2.6-1-any + fontconfig-2.13.1-1-any + freetype-2.9.1-1-any + fribidi-1.0.5-1-any + gdk-pixbuf2-2.38.0-2-any + gettext-0.19.8.1-7-any + glib2-2.58.1-1-any + graphite2-1.3.9-1-any + gtk3-3.24.1-1-any + harfbuzz-2.2.0-1-any + libdatrie-0.2.12-1-any + libepoxy-1.5.3-1-any + libffi-3.2.1-4-any + libiconv-1.15-3-any + libidn2-2.0.5-1-any + libpng-1.6.36-1-any + libpsl-0.20.2-1-any + libthai-0.1.28-2-any + libtiff-4.0.10-1-any + libunistring-0.9.10-1-any + libwinpthread-git-7.0.0.5273.3e5acf5d-1-any + lua-5.3.5-1-any + ncurses-6.1.20180908-1-any + nghttp2-1.35.0-1-any + openssl-1.1.1-4-any + pango-1.42.4-2-any + pcre-8.42-1-any + pixman-0.36.0-1-any + readline-7.0.005-1-any + termcap-1.3.1-3-any + zlib-1.2.11-5-any +) + +# ------ Get mingw packages ------ +import_signing_key +for name in "${packages[@]}"; do + get_package $name +done + +# ----- Get local files ------ +if [ "$PLATFORM" == "win32" ]; then + # We're cross-compiling for 32-bit Windows from Linux + get_package mingw-w64-cross-gcc-7.3.0-2-i686.pkg # necessary to get libgcc_s_dw2-1.dll + cp -u $DEPS/opt/lib/gcc/i686-w64-mingw32/7.3.0/libgcc_s_dw2-1.dll $DEPS/bin/ + cp -u /usr/lib/gcc/i686-w64-mingw32/6.3-win32/libgcc_s_sjlj-1.dll $DEPS/bin/ + cp -u /usr/lib/gcc/i686-w64-mingw32/6.3-win32/libstdc++-6.dll $DEPS/bin/ +else + # We're cross-compiling for 64-bit Windows from Linux + cp -u /usr/lib/gcc/x86_64-w64-mingw32/6.3-win32/libgcc_s_seh-1.dll $DEPS/bin/ + cp -u /usr/lib/gcc/x86_64-w64-mingw32/6.3-win32/libstdc++-6.dll $DEPS/bin/ +fi diff --git a/doc/build-debian.md b/doc/build-debian.md new file mode 100644 index 0000000..78d5ad2 --- /dev/null +++ b/doc/build-debian.md @@ -0,0 +1,54 @@ +Build instructions for Debian 9 (Stretch) +========================================= + +**!!! Produced binaries do not run yet - See [issue #1](https://github.com/ipamo/cardpeek/issues/1) !!!** + +## For local Debian host + +Required libraries: + + sudo apt install autoconf make libglib2.0-dev libgtk-3-dev liblua5.2-dev libcurl4-openssl-dev libssl-dev + +Build: + + autoreconf --install + ./configure + make + +Install (use sudo if necessary): + + make install + + +## Cross-compile for Windows (32 bits) + +Requirements: + + sudo apt install libpcsclite1:i386 dirmngr mingw-w64 mingw-w64-tools binutils-mingw-w64 + +Download mingw-W64 package dependencies either from a Windows MSys2 installation, or directly from [MSys2 repository](http://repo.msys2.org/mingw/i686/), +unpack the packages and merge them into a new directory called `deps/win32` (which will then contain subdirectories `bin`, `lib`, `include`, etc): + + ./deps-win32.sh + +Build: + + make -f Makefile.win32 CCPRE=i686-w64-mingw32- DEPS=deps/win32 TARGET=/opt/cardpeek-win32 + +Install: + + make install -f Makefile.win32 CCPRE=i686-w64-mingw32- DEPS=deps/win32 TARGET=/opt/cardpeek-win32 + +Run: + + wine build/win32/cardpeek.exe + + +## Cross-compile for Windows (64 bits) + +Adapt previous commands as follow + + ./clean.sh + PLATFORM=win64 ./deps-win32.sh + make -f Makefile.win32 CCPRE=x86_64-w64-mingw32- DEPS=deps/win64 MPLATFORM=mingw64 TARGET=/opt/cardpeek-win64 + make install -f Makefile.win32 CCPRE=x86_64-w64-mingw32- DEPS=deps/win64 MPLATFORM=mingw64 TARGET=/opt/cardpeek-win64 diff --git a/INSTALL.FreeBSD b/doc/build-freebsd.md similarity index 82% rename from INSTALL.FreeBSD rename to doc/build-freebsd.md index 1b7442c..0958625 100644 --- a/INSTALL.FreeBSD +++ b/doc/build-freebsd.md @@ -1,5 +1,6 @@ Specific installation instructions for FreeBSD ----------------------------------------------- +============================================== + If you get the following error, after using configure and make: invalid DSO for symbol `libiconv_open' definition @@ -8,5 +9,3 @@ Try running the following commands: ICONV_LIBS="-l iconv" ./configure make - --*- diff --git a/doc/build-windows.md b/doc/build-windows.md new file mode 100644 index 0000000..a6c5212 --- /dev/null +++ b/doc/build-windows.md @@ -0,0 +1,42 @@ +Build on Windows +================ + +**!!! Produced binaries do not run yet - See [issue #1](https://github.com/ipamo/cardpeek/issues/1) !!!** + +## Pre-requisites + +Download and install [msys2](https://www.msys2.org/), which provide gcc toolchains for 32-bit and 64-bit Windows (both powered by MinGW-w64). + +## Build for 32-bit Windows + +Open a 32-bit mingw shell (usually `C:\msys64\mingw32.exe`). + +Install dependencies: + + pacman -S tar make pkg-config mingw32/mingw-w64-i686-gcc mingw32/mingw-w64-i686-gtk3 mingw32/mingw-w64-i686-lua mingw32/mingw-w64-i686-curl + +Clean: + + ./clean.sh + +Build: + + make -f Makefile.win32 + make install -f Makefile.win32 + +## Build for 64-bit Windows + +Open a 64-bit mingw shell (usually `C:\msys64\mingw64.exe`). + +Install dependencies: + + pacman -S tar make pkg-config mingw64/mingw-w64-x86_64-gcc mingw64/mingw-w64-x86_64-gtk3 mingw64/mingw-w64-x86_64-lua mingw64/mingw-w64-x86_64-curl + +Clean: + + ./clean.sh + +Build: + + make -f Makefile.win32 DEPS=/mingw64 MPLATFORM=mingw64 TARGET="/C/Program Files/Cardpeek" + make install -f Makefile.win32 DEPS=/mingw64 MPLATFORM=mingw64 TARGET="/C/Program Files/Cardpeek" diff --git a/doc/emv.fr.md b/doc/emv.fr.md new file mode 100644 index 0000000..6faf605 --- /dev/null +++ b/doc/emv.fr.md @@ -0,0 +1,21 @@ +# How can I read the contents of my EMV bank card? # + +![sample-emv.jpg](sample-emv.jpg) + +The "emv" script in **cardpeek** provides an analysis of EMV banking cards used across the +world. + +# Notes # + +This script will ask you if you want to issue a Get Processing Option (GPO) +command for each application on the card. Since some cards have several applications +(e.g. a national and an international application), this question may be asked twice or +more. This command is needed to allow access to some information in the card. Issuing this command will also increase an internal counter inside the card (the ATC). + +# Notes on privacy # + +You will notice that many of these bank cards keep a \transaction log" of the last +transactions you have made with your card. Some banks cards keep way over a hundred +transactions that are freely readable, containing the date, the amount and the country of the transaction, which brings up some privacy issues. Why do banks need to keep so much information in the card? + +The security elements in the card, such as the PIN and the cryptographic keys are fully protected in the card and cannot be read. \ No newline at end of file diff --git a/doc/emv.md b/doc/emv.md new file mode 100644 index 0000000..c649aec --- /dev/null +++ b/doc/emv.md @@ -0,0 +1,21 @@ +# How can I read the contents of my EMV bank card? # + +![sample-emv.jpg](sample-emv.jpg) + +The « emv » script in **cardpeek** provides an analysis of EMV banking cards used across the +world. + +# Notes # + +This script will ask you if you want to issue a Get Processing Option (GPO) +command for each application on the card. Since some cards have several applications +(e.g. a national and an international application), this question may be asked twice or +more. This command is needed to allow access to some information in the card. Issuing this command will also increase an internal counter inside the card (the ATC). + +# Notes on privacy # + +You will notice that many of these bank cards keep a \transaction log" of the last +transactions you have made with your card. Some banks cards keep way over a hundred +transactions that are freely readable, containing the date, the amount and the country of the transaction, which brings up some privacy issues. Why do banks need to keep so much information in the card? + +The security elements in the card, such as the PIN and the cryptographic keys are protected in the card and cannot be read. \ No newline at end of file diff --git a/doc/navigo.fr.md b/doc/navigo.fr.md new file mode 100644 index 0000000..c2b684b --- /dev/null +++ b/doc/navigo.fr.md @@ -0,0 +1,15 @@ +Comment lire le contenu de son Pass Navigo ? +============================================ + +![sample-navigo.jpg](sample-navigo.jpg) + +Le script "calypso" inclu dans **cardpeek** permet de lire le contenu des cartes "Navigo" utilisées en région parisienne. Cet outil offre une analyse améliorée des "journaux d'évènements" de la carte affichant notamment le nom des stations de métro/RER où la carte a été utilisée. Cet outil a été testé avec succès sur les cartes Navigo Découverte, Navigo et Navigo Intégrale. + +## Notes + +Il faut utiliser **cardpeek** avec un lecteur de carte à puce à contact pour lire le contenu d'une carte "navigo" (ou un lecteur sans contact spécialisé). En effet, les cartes "navigo" ne peuvent pas êtres lues avec des lecteurs sans contact _classiques_ (car elles utilisent un protocole de communication radio qui n'est pas totalement compatible avec l'ISO 14443 B). + +## Protection de la vie privée + +Ces cartes de transport conservent un "journal d'évènement" décrivant les 3 dernières validations effectuées par le porteur de la carte. Ce journal, qui peut poser un risque pour la protection de la vie privée, n'est pas protégé en lecture. +Par contre le nom du porteur de la carte n'est pas, selon nos analyses, accessible en lisant la carte. En revanche, c'est le cas pour la carte MOBIB utilsée à Bruxelles. diff --git a/doc/navigo.md b/doc/navigo.md new file mode 100644 index 0000000..279ac6e --- /dev/null +++ b/doc/navigo.md @@ -0,0 +1,15 @@ +How to read the content of your Navigo Pass? +============================================ + +![sample-navigo.jpg](sample-navigo.jpg) + +The "calypso" script included in **cardpeek** can read the content of Navigo cards used in Paris. It provides enhanced "event log" analysis notably with subway/train station names, as illustrated in the screenshot above. It has been successfully tested on Navigo Découverte, Navigo and Navigo Intégrale cards. + +## Notes + +You must use the contact interface to read a Navigo card (or a special contactless reader), because they cannot be read with a normal conctactless card-reader (these cards use a specific protocol that is not fully compatible with ISO 14443 B). + +## Privacy notes + +These transport cards keep an "event log" describing at least 3 of the last stations/stops you have been through. This "event log", which could pose a privacy risk, is not protected by any access control means and is freely readable. +Note however that your name does not appear on the card (to our best knowledge), as opposed to the MOBIB card in Brussels. diff --git a/doc/sample-emv.jpg b/doc/sample-emv.jpg new file mode 100644 index 0000000..1845bd9 Binary files /dev/null and b/doc/sample-emv.jpg differ diff --git a/doc/sample-navigo.jpg b/doc/sample-navigo.jpg new file mode 100644 index 0000000..85d67a3 Binary files /dev/null and b/doc/sample-navigo.jpg differ diff --git a/dot_cardpeek_dir/scripts/calypso.lua b/dot_cardpeek_dir/scripts/calypso.lua index 7bc3b61..eec4cd6 100644 --- a/dot_cardpeek_dir/scripts/calypso.lua +++ b/dot_cardpeek_dir/scripts/calypso.lua @@ -18,16 +18,16 @@ -- -- @name Calypso -- @description Calypso tranport cards: Navigo, MOBIB, Korrigo, RavKav, ... --- @targets 0.8 +-- @targets 0.8 -- ------------------------------------------------------------------------- -- *PLEASE NOTE* -- This work is based on: --- * public information about the calypso card specification, --- * partial information found on the web about the ticketing data +-- * public information about the calypso card specification, +-- * partial information found on the web about the ticketing data -- format, as described in the French "intercode" documentation. --- * experimentation and guesses, --- This information is incomplete. If you have further data, such +-- * experimentation and guesses, +-- This information is incomplete. If you have further data, such -- as details ofd specs, please help send them -- to L1L1@gmx.com -------------------------------------------------------------------------- @@ -58,7 +58,7 @@ LFI_LIST = { { "ID", "/0003", "file" }, { "Ticketing", "/2000", "folder" }, { "Environment", "/2000/2001", "file" }, - { "Holder", "/2000/2002", "file" }, + { "Holder", "/2000/2002", "file" }, { "Event logs", "/2000/2010", "file" }, { "Contracts", "/2000/2020", "file" }, { "Counters", "/2000/202A", "file" }, @@ -70,9 +70,9 @@ LFI_LIST = { { "Counters", "/2000/2060", "file" }, { "Counters", "/2000/2061", "file" }, { "Counters", "/2000/2062", "file" }, + { "Counters", "/2000/2069", "file" }, { "Special events", "/2000/2040", "file" }, { "Contract list", "/2000/2050", "file" }, - { "Counters", "/2000/2069", "file" }, { "Holder Extended", "/3F1C", "file" } } --]] @@ -83,11 +83,11 @@ LFI_LIST = { { "ID", "/0003", "file" }, { "Holder Extended", "/3F1C", "file" }, { "Display / Free", "/2F10", "file" }, - + { "Ticketing", "/2000", "folder" }, { "AID", "/2000/2004", "file" }, { "Environment", "/2000/2001", "file" }, - { "Holder", "/2000/2002", "file" }, + { "Holder", "/2000/2002", "file" }, { "Event logs", "/2000/2010", "file" }, { "Contracts", "/2000/2020", "file" }, { "Contracts", "/2000/2030", "file" }, @@ -105,7 +105,7 @@ LFI_LIST = { { "Special events", "/2000/2040", "file" }, { "Contract list", "/2000/2050", "file" }, { "Free", "/2000/20F0", "file" }, - + { "MPP", "/3100", "folder" }, { "AID", "/3100/3104", "file" }, { "Public Param.", "/3100/3102", "file" }, @@ -117,7 +117,7 @@ LFI_LIST = { { "Counters", "/3100/3169", "file" }, { "Miscellaneous", "/3100/3150", "file" }, { "Free", "/3100/31F0", "file" }, - + { "RT2", "/2100", "folder" }, { "AID", "/2100/2104", "file" }, { "Environment", "/2100/2101", "file" }, @@ -127,12 +127,12 @@ LFI_LIST = { { "Counters", "/2100/2169", "file" }, { "Special events", "/2100/2140", "file" }, { "Free", "/2100/21F0", "file" }, - + { "EP", "/1000", "folder" }, { "AID", "/1000/1004", "file" }, { "Load Log", "/1000/1014", "file" }, { "Purchase Log", "/1000/1015", "file" }, - + { "eTicket", "/8000", "folder" }, { "AID", "/8000/8004", "file" }, { "Preselection", "/8000/8030", "file" }, @@ -156,7 +156,7 @@ function calypso_select(ctx,desc,path,klass) if sw==0x9000 then for r=0,(#path_parsed/2)-1 do item = bytes.format(bytes.sub(path_parsed,r*2,r*2+1),"%D") - + file_node = parent_node:find_first({id=item}) if file_node==nil then file_node = parent_node:append{ classname = klass, @@ -195,7 +195,7 @@ function calypso_guess_network(cardenv) if country_num==250 or country_num==56 or country_num==131 then return country_num, network_num end - + country_bin = data:sub(3,14) network_bin = data:sub(15,22) country_num = tonumber(country_bin:convert(4):format("%D")) @@ -205,7 +205,7 @@ function calypso_guess_network(cardenv) end log.print(log.WARNING,"Unknown Calypso card.") - + else log.print(log.WARNING,"Could not find enough data in 'Environement/record#1'") end @@ -230,7 +230,7 @@ function calypso_process(cardenv) for lfi_index,lfi_desc in ipairs(LFI_LIST) do lfi_node = calypso_select(cardenv,lfi_desc[1],lfi_desc[2], lfi_desc[3]) - + if lfi_node and lfi_desc[3]=="file" then local record for record=1,255 do @@ -238,9 +238,9 @@ function calypso_process(cardenv) if sw ~= 0x9000 then break end - rec_node = lfi_node:append{ classname = "record", - label = "record", - size = #resp, + rec_node = lfi_node:append{ classname = "record", + label = "record", + size = #resp, id = record, val = resp } end @@ -250,7 +250,7 @@ function calypso_process(cardenv) country, network = calypso_guess_network(cardenv) filename = "calypso/c"..country..".lua" file = io.open(filename); - if file then + if file then io.close(file) dofile(filename) else @@ -259,7 +259,7 @@ function calypso_process(cardenv) filename = "calypso/c"..country.."n"..network..".lua" file = io.open(filename); - if file then + if file then io.close(file) dofile(filename) else @@ -267,10 +267,10 @@ function calypso_process(cardenv) end end -if card.connect() then +if card.connect() then CARD = card.tree_startup("CALYPSO") - + sw = card.select("#2000") if sw==0x9000 then sel_method = SEL_BY_LFI @@ -282,7 +282,7 @@ if card.connect() then sel_method = SEL_BY_LFI ui.question("This script may not work: this card doesn't seem to react to file selection commands.",{"OK"}) end - end + end if sw~=0x6E00 then calypso_process(CARD) diff --git a/dot_cardpeek_dir/scripts/calypso/c131.lua b/dot_cardpeek_dir/scripts/calypso/c131.lua index ef71cf2..db7f50a 100755 --- a/dot_cardpeek_dir/scripts/calypso/c131.lua +++ b/dot_cardpeek_dir/scripts/calypso/c131.lua @@ -149,6 +149,13 @@ function viva_PERIOD_UNITS(source) return PERIOD_UNITS[units] end +local date_days +-- Same as en1545_DATE, but the number of days is kept and reused in viva_VALIDITY +function viva_DATE(source) + date_days = EPOCH+bytes.tonumber(source)*24*3600 + return os.date("%a %x",date_days) +end + function viva_VALIDITY(source) local validity = source:tonumber() local end_time @@ -256,8 +263,8 @@ viva_Environment = { [0] = { viva_HIDDEN, 30, "unknown" }, [1] = { viva_OPERATOR, 8, "issuer" }, [2] = { viva_CARD_NUM, 24, "LV card number" }, - [3] = { en1545_DATE, 14, "issued" }, - [4] = { en1545_DATE, 14, "valid until" }, + [3] = { viva_DATE, 14, "issued" }, + [4] = { viva_DATE, 14, "valid until" }, [5] = { viva_HIDDEN, 15, "unknown" }, [6] = { en1545_BCD_DATE, 32, "holder birthdate" } } @@ -283,11 +290,11 @@ viva_Contract = { [0] = { viva_OPERATOR, 7, "operator" }, [1] = { viva_PRODUCT, 16, "product" }, [2] = { viva_HIDDEN, 2, "unknown" }, - [3] = { en1545_DATE, 14, "start date" }, + [3] = { viva_DATE, 14, "start date" }, [4] = { viva_SALES_POINT, 5, "sales point" }, [5] = { viva_HIDDEN, 19, "sales information" }, [6] = { viva_PERIOD_UNITS, 16, "period units" }, - [7] = { en1545_DATE, 14, "start/end date" }, + [7] = { viva_DATE, 14, "start/end date" }, [8] = { viva_VALIDITY, 7, "validity period" }, [9] = { viva_HIDDEN, 3, "unknown" }, [10] = { viva_OPERATOR1, 5, "operator 1" }, diff --git a/dot_cardpeek_dir/scripts/calypso/c250.lua b/dot_cardpeek_dir/scripts/calypso/c250.lua index 6ea4edb..9952548 100644 --- a/dot_cardpeek_dir/scripts/calypso/c250.lua +++ b/dot_cardpeek_dir/scripts/calypso/c250.lua @@ -33,182 +33,227 @@ require('lib.strict') require('lib.en1545') require('lib.calypso_card_num') ----------------------------- +intercode_Contract = {} +intercode_Contract_types = {} -- list of contract types (detected by BestContracts structure) +intercode_Contract_pointers = {} -- list of contract pointers (detected by BestContracts structure) -intercode_BestContracts = { - [0] = { en1545_REPEAT, 4, "BestContractCount", { - [0] = { en1545_BITMAP, 3, "BestContract", { - [0] = { en1545_NETWORKID, 24, "BestContractsNetworkId" }, - [1] = { en1545_UNDEFINED, 16, "BestContractsTariff" }, - [2] = { en1545_UNDEFINED, 5, "BestContractsPointer" } - }} - }} -} +function intercode_parse_best_contract_tariff(source) + local sort_key = bytes.sub(source, 0, 3):tonumber() + local contract_type = bytes.sub(source, 4, 11):tonumber() + local priority = bytes.sub(source, 12, 15):tonumber() + table.insert(intercode_Contract_types, contract_type) + return "Sort key: "..sort_key.." - Contract type: "..string.format('%02Xh', contract_type).." - Priority: "..priority +end + +function intercode_parse_best_contract_pointer(source) + local pointer = source:tonumber() + table.insert(intercode_Contract_pointers, pointer) + return tostring(pointer) +end intercode_Env = { - [0] = { en1545_UNDEFINED, 6, "EnvVersionNumber" }, - [1] = { en1545_BITMAP, 7, "Env",{ - [0] = { en1545_NETWORKID, 24, "EnvNetworkId" }, - [1] = { en1545_UNDEFINED, 8, "EnvApplicationIssuerId" }, - [2] = { en1545_DATE, 14, "EnvApplicationValidityEndDate" }, - [3] = { en1545_UNDEFINED, 11, "EnvPayMethod" }, - [4] = { en1545_UNDEFINED, 16, "EnvAuthenticator" }, - [5] = { en1545_UNDEFINED, 32, "EnvSelectList" }, - [6] = { en1545_UNDEFINED, 2, "EnvData", - [0] = { en1545_UNDEFINED, 1, "EnvCardStatus" }, - [1] = { en1545_UNDEFINED, 0, "EnvExtra" }, + [0] = { en1545_UNDEFINED, 6, "EnvApplicationVersionNumber" }, + [1] = { en1545_BITMAP, 7, "Env", { + [0] = { en1545_NETWORKID, 24, "EnvNetworkId" }, + [1] = { en1545_UNDEFINED, 8, "EnvApplicationIssuerId" }, + [2] = { en1545_DATE, 14, "EnvApplicationValidityEndDate" }, + [3] = { en1545_UNDEFINED, 11, "EnvPayMethod" }, + [4] = { en1545_UNDEFINED, 16, "EnvAuthenticator" }, + [5] = { en1545_UNDEFINED, 32, "EnvSelectList" }, + [6] = { en1545_BITMAP, 2, "EnvData", { + [0] = { en1545_UNDEFINED, 1, "EnvCardStatus" }, + [1] = { en1545_UNDEFINED, 0, "EnvData2" }, + }} }} - } } intercode_Holder = { - [0] = { en1545_BITMAP, 8, "Holder", { - [0] = { en1545_BITMAP, 2, "HolderName", { - [0] = { en1545_UNDEFINED, 85, "HolderSurname" }, - [1] = { en1545_UNDEFINED, 85, "HolderForename" } - }}, - [1] = { en1545_BITMAP, 2, "HolderBirth", { - [0] = { en1545_BCD_DATE, 32, "HolderBirthDate" }, - [1] = { en1545_UNDEFINED, 115, "HolderBirthPlace"} - }}, - [2] = { en1545_UNDEFINED, 85, "HolderBirthName" }, - [3] = { en1545_UNDEFINED, 32, "HolderIdNumber" }, - [4] = { en1545_UNDEFINED, 24, "HolderCountryAlpha" }, - [5] = { en1545_UNDEFINED, 32, "HolderCompany" }, - [6] = { en1545_REPEAT, 4, "HolderProfiles(0..4)", { - [0] = { en1545_BITMAP, 3, "Profile", { - [0] = { en1545_UNDEFINED, 24, "NetworkId" }, - [1] = { en1545_UNDEFINED, 8, "ProfileNumber" }, - [2] = { en1545_DATE, 14, "ProfileDate" } - }} - }}, - [7] = { en1545_BITMAP, 12, "HolderData", { - [0] = { en1545_UNDEFINED, 4, "HolderDataCardStatus" }, - [1] = { en1545_UNDEFINED, 4, "HolderDataTeleReglement" }, - [2] = { en1545_UNDEFINED, 17, "HolderDataResidence" }, - [3] = { en1545_UNDEFINED, 6, "HolderDataCommercialID" }, - [4] = { en1545_UNDEFINED, 17, "HolderDataWorkPlace" }, - [5] = { en1545_UNDEFINED, 17, "HolderDataStudyPlace" }, - [6] = { en1545_UNDEFINED, 16, "HolderDataSaleDevice" }, - [7] = { en1545_UNDEFINED, 16, "HolderDataAuthenticator" }, - [8] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate1" }, - [9] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate2" }, - [10] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate3" }, - [11] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate4" } + [0] = { en1545_BITMAP, 8, "Holder", { + [0] = { en1545_BITMAP, 2, "HolderName", { + [0] = { en1545_UNDEFINED, 85, "HolderSurname" }, + [1] = { en1545_UNDEFINED, 85, "HolderForename" } + }}, + [1] = { en1545_BITMAP, 2, "HolderBirth", { + [0] = { en1545_BCD_DATE, 32, "HolderBirthDate" }, + [1] = { en1545_UNDEFINED, 115, "HolderBirthPlace"} + }}, + [2] = { en1545_UNDEFINED, 85, "HolderBirthName" }, + [3] = { en1545_UNDEFINED, 32, "HolderIdNumber" }, + [4] = { en1545_UNDEFINED, 24, "HolderCountryAlpha" }, + [5] = { en1545_UNDEFINED, 32, "HolderCompany" }, + [6] = { en1545_REPEAT, 4, "HolderProfiles", { + [0] = { en1545_BITMAP, 3, "Profile", { + [0] = { en1545_UNDEFINED, 24, "NetworkId" }, + [1] = { en1545_NUMBER, 8, "ProfileNumber" }, + [2] = { en1545_DATE, 14, "ProfileDate" } + }} + }}, + [7] = { en1545_BITMAP, 12, "HolderData", { + [0] = { en1545_UNDEFINED, 4, "HolderDataCardStatus" }, + [1] = { en1545_UNDEFINED, 4, "HolderDataTeleReglement" }, + [2] = { en1545_UNDEFINED, 17, "HolderDataResidence" }, + [3] = { en1545_UNDEFINED, 6, "HolderDataCommercialID" }, + [4] = { en1545_UNDEFINED, 17, "HolderDataWorkPlace" }, + [5] = { en1545_UNDEFINED, 17, "HolderDataStudyPlace" }, + [6] = { en1545_UNDEFINED, 16, "HolderDataSaleDevice" }, + [7] = { en1545_UNDEFINED, 16, "HolderDataAuthenticator" }, + [8] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate1" }, + [9] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate2" }, + [10] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate3" }, + [11] = { en1545_UNDEFINED, 14, "HolderDataProfileStartDate4" } + }} }} - }} } +intercode_BestContracts = { + [0] = { en1545_REPEAT, 4, "BestContracts", { + [0] = { en1545_BITMAP, 3, "BestContract", { + [0] = { en1545_NETWORKID, 24, "BestContractNetworkId" }, + [1] = { intercode_parse_best_contract_tariff, 16, "BestContractTariff" }, + [2] = { intercode_parse_best_contract_pointer, 5, "BestContractPointer" }, + }}, + }}, +} -intercode_Contract = { - [0] = { en1545_BITMAP, 20, "Contract", - { - [0] = { en1545_NETWORKID, 24, "ContractNetworkId" }, - [1] = { en1545_UNDEFINED, 8, "ContractProvider" }, - [2] = { en1545_UNDEFINED, 16, "ContractTariff" }, - [3] = { en1545_UNDEFINED, 32, "ContractSerialNumber" }, - [4] = { en1545_BITMAP, 2, "ContractCustomerInfo", { - [0] = { en1545_UNDEFINED, 6, "ContractCustomerProfile" }, - [1] = { en1545_UNDEFINED, 32, "ContractCustomerNumber" }, - }}, - [5] = { en1545_BITMAP, 2, "ContractPassengerInfo", { - [0] = { en1545_UNDEFINED, 6, "ContractPassengerClass" }, - [1] = { en1545_UNDEFINED, 32, "ContractPassengerTotal" }, - }}, - [6] = { en1545_UNDEFINED, 6, "ContractVehiculeClassAllowed" }, - [7] = { en1545_UNDEFINED, 32, "ContractPaymentPointer" }, - [8] = { en1545_UNDEFINED, 11, "ContractPayMethod" }, - [9] = { en1545_UNDEFINED, 16, "ContractServices" }, - [10] = { en1545_AMOUNT, 16, "ContractPriceAmount" }, - [11] = { en1545_UNDEFINED, 16, "ContractPriceUnit" }, - [12] = { en1545_BITMAP, 7, "ContractContractRestriction", { - [0] = { en1545_TIME, 11, "ContractStartTime" }, - [1] = { en1545_TIME, 11, "ContractEndTime" }, - [2] = { en1545_UNDEFINED, 8, "ContractRestrictDay" }, - [3] = { en1545_UNDEFINED, 8, "ContractRestrictTimeCode" }, - [4] = { en1545_UNDEFINED, 8, "ContractRestrictCode" }, - [5] = { en1545_UNDEFINED, 16, "ContractRestrictProduct" }, - [6] = { en1545_UNDEFINED, 16, "ContractRestrcitLocation" }, - }}, - [13] = { en1545_BITMAP, 9, "ContractContractValidityInfo", { - [0] = { en1545_DATE, 14, "ContractStartDate" }, - [1] = { en1545_TIME, 11, "ContractStartTime" }, - [2] = { en1545_DATE, 14, "ContractEndDate" }, - [3] = { en1545_TIME, 11, "ContractEndTime" }, - [4] = { en1545_UNDEFINED, 8, "ContractDuration" }, - [5] = { en1545_DATE, 14, "ContractLimitDate" }, - [6] = { en1545_ZONES, 8, "ContractZones" }, - [7] = { en1545_UNDEFINED, 16, "ContractJourneys" }, - [8] = { en1545_UNDEFINED, 16, "ContractPeriodJourneys" }, - }}, - [14] = { en1545_BITMAP, 8, "ContractContractJourneyData", { - [0] = { en1545_UNDEFINED, 16, "ContractOrigin" }, - [1] = { en1545_UNDEFINED, 16, "ContractDestination" }, - [2] = { en1545_UNDEFINED, 16, "ContractRouteNumbers" }, - [3] = { en1545_UNDEFINED, 8, "ContractRouteVariants" }, - [4] = { en1545_UNDEFINED, 16, "ContractRun" }, - [5] = { en1545_UNDEFINED, 16, "ContractVia" }, - [6] = { en1545_UNDEFINED, 16, "ContractDistance" }, - [7] = { en1545_UNDEFINED, 8, "ContractInterchange" }, - }}, - [15] = { en1545_BITMAP, 4, "ContractContractSaleData", { - [0] = { en1545_DATE, 14, "ContractDate" }, - [1] = { en1545_TIME, 11, "ContractTime" }, - [2] = { en1545_UNDEFINED, 8, "ContractAgent" }, - [3] = { en1545_UNDEFINED, 16, "ContractDevice" }, - }}, - [16] = { en1545_UNDEFINED, 8, "ContractStatus" }, - [17] = { en1545_UNDEFINED, 16, "ContractLoyalityPoints" }, - [18] = { en1545_UNDEFINED, 16, "ContractAuthenticator" }, - [19] = { en1545_UNDEFINED, 0, "Contract"}, - } - } +intercode_Contract['FFh'] = { + [0] = { en1545_BITMAP, 20, "Contract", { + [0] = { en1545_NETWORKID, 24, "ContractNetworkId" }, + [1] = { en1545_UNDEFINED, 8, "ContractProvider" }, + [2] = { en1545_UNDEFINED, 16, "ContractTariff" }, + [3] = { en1545_UNDEFINED, 32, "ContractSerialNumber" }, + [4] = { en1545_BITMAP, 2, "ContractCustomerInfo", { + [0] = { en1545_UNDEFINED, 6, "ContractCustomerProfile" }, + [1] = { en1545_UNDEFINED, 32, "ContractCustomerNumber" }, + }}, + [5] = { en1545_BITMAP, 2, "ContractPassengerInfo", { + [0] = { en1545_UNDEFINED, 6, "ContractPassengerClass" }, + [1] = { en1545_UNDEFINED, 32, "ContractPassengerTotal" }, + }}, + [6] = { en1545_UNDEFINED, 6, "ContractVehiculeClassAllowed" }, + [7] = { en1545_UNDEFINED, 32, "ContractPaymentPointer" }, + [8] = { en1545_UNDEFINED, 11, "ContractPayMethod" }, + [9] = { en1545_UNDEFINED, 16, "ContractServices" }, + [10] = { en1545_AMOUNT, 16, "ContractPriceAmount" }, + [11] = { en1545_UNDEFINED, 16, "ContractPriceUnit" }, + [12] = { en1545_BITMAP, 7, "ContractRestriction", { + [0] = { en1545_TIME, 11, "ContractStartTime" }, + [1] = { en1545_TIME, 11, "ContractEndTime" }, + [2] = { en1545_UNDEFINED, 8, "ContractRestrictDay" }, + [3] = { en1545_UNDEFINED, 8, "ContractRestrictTimeCode" }, + [4] = { en1545_UNDEFINED, 8, "ContractRestrictCode" }, + [5] = { en1545_UNDEFINED, 16, "ContractRestrictProduct" }, + [6] = { en1545_UNDEFINED, 16, "ContractRestrcitLocation" }, + }}, + [13] = { en1545_BITMAP, 9, "ContractValidityInfo", { + [0] = { en1545_DATE, 14, "ContractStartDate" }, + [1] = { en1545_TIME, 11, "ContractStartTime" }, + [2] = { en1545_DATE, 14, "ContractEndDate" }, + [3] = { en1545_TIME, 11, "ContractEndTime" }, + [4] = { en1545_UNDEFINED, 8, "ContractDuration" }, + [5] = { en1545_DATE, 14, "ContractLimitDate" }, + [6] = { en1545_ZONES, 8, "ContractZones" }, + [7] = { en1545_UNDEFINED, 16, "ContractJourneys" }, + [8] = { en1545_UNDEFINED, 16, "ContractPeriodJourneys" }, + }}, + [14] = { en1545_BITMAP, 8, "ContractJourneyData", { + [0] = { en1545_UNDEFINED, 16, "ContractOrigin" }, + [1] = { en1545_UNDEFINED, 16, "ContractDestination" }, + [2] = { en1545_UNDEFINED, 16, "ContractRouteNumbers" }, + [3] = { en1545_UNDEFINED, 8, "ContractRouteVariants" }, + [4] = { en1545_UNDEFINED, 16, "ContractRun" }, + [5] = { en1545_UNDEFINED, 16, "ContractVia" }, + [6] = { en1545_UNDEFINED, 16, "ContractDistance" }, + [7] = { en1545_UNDEFINED, 8, "ContractInterchange" }, + }}, + [15] = { en1545_BITMAP, 4, "ContractSaleData", { + [0] = { en1545_DATE, 14, "ContractDate" }, + [1] = { en1545_TIME, 11, "ContractTime" }, + [2] = { en1545_UNDEFINED, 8, "ContractAgent" }, + [3] = { en1545_UNDEFINED, 16, "ContractDevice" }, + }}, + [16] = { en1545_UNDEFINED, 8, "ContractStatus" }, + [17] = { en1545_UNDEFINED, 16, "ContractLoyalityPoints" }, + [18] = { en1545_UNDEFINED, 16, "ContractAuthenticator" }, + [19] = { en1545_UNDEFINED, 0, "Contract"}, + }} } intercode_Event = { - [0] = { en1545_DATE, 14, "EventDate" }, - [1] = { en1545_TIME, 11, "EventTime" }, - [2] = { en1545_BITMAP, 28, "Event", - { - [0] = { en1545_UNDEFINED, 8, "EventDisplayData" }, - [1] = { en1545_NETWORKID, 24, "EventNetworkId" }, - [2] = { en1545_UNDEFINED, 8, "EventCode" }, - [3] = { en1545_UNDEFINED, 8, "EventResult" }, - [4] = { en1545_UNDEFINED, 8, "EventServiceProvider" }, - [5] = { en1545_UNDEFINED, 8, "EventNotOkCounter" }, - [6] = { en1545_UNDEFINED, 24, "EventSerialNumber" }, - [7] = { en1545_UNDEFINED, 16, "EventDestination" }, - [8] = { en1545_UNDEFINED, 16, "EventLocationId" }, - [9] = { en1545_UNDEFINED, 8, "EventLocationGate" }, - [10] = { en1545_UNDEFINED, 16, "EventDevice" }, - [11] = { en1545_NUMBER, 16, "EventRouteNumber" }, - [12] = { en1545_UNDEFINED, 8, "EventRouteVariant" }, - [13] = { en1545_UNDEFINED, 16, "EventJourneyRun" }, - [14] = { en1545_UNDEFINED, 16, "EventVehiculeId" }, - [15] = { en1545_UNDEFINED, 8, "EventVehiculeClass" }, - [16] = { en1545_UNDEFINED, 5, "EventLocationType" }, - [17] = { en1545_UNDEFINED,240, "EventEmployee" }, - [18] = { en1545_UNDEFINED, 16, "EventLocationReference" }, - [19] = { en1545_UNDEFINED, 8, "EventJourneyInterchanges" }, - [20] = { en1545_UNDEFINED, 16, "EventPeriodJourneys" }, - [21] = { en1545_UNDEFINED, 16, "EventTotalJourneys" }, - [22] = { en1545_UNDEFINED, 16, "EventJourneyDistance" }, - [23] = { en1545_AMOUNT, 16, "EventPriceAmount" }, - [24] = { en1545_UNDEFINED, 16, "EventPriceUnit" }, - [25] = { en1545_UNDEFINED, 5, "EventContractPointer" }, - [26] = { en1545_UNDEFINED, 16, "EventAuthenticator" }, - [27] = { en1545_UNDEFINED, 5, "EventBitmapExtra" }, - } - } + [0] = { en1545_DATE, 14, "EventDate" }, + [1] = { en1545_TIME, 11, "EventTime" }, + [2] = { en1545_BITMAP, 28, "Event", { + [0] = { en1545_UNDEFINED, 8, "EventDisplayData" }, + [1] = { en1545_NETWORKID, 24, "EventNetworkId" }, + [2] = { en1545_UNDEFINED, 8, "EventCode" }, + [3] = { en1545_UNDEFINED, 8, "EventResult" }, + [4] = { en1545_UNDEFINED, 8, "EventServiceProvider" }, + [5] = { en1545_UNDEFINED, 8, "EventNotOkCounter" }, + [6] = { en1545_UNDEFINED, 24, "EventSerialNumber" }, + [7] = { en1545_UNDEFINED, 16, "EventDestination" }, + [8] = { en1545_UNDEFINED, 16, "EventLocationId" }, + [9] = { en1545_UNDEFINED, 8, "EventLocationGate" }, + [10] = { en1545_UNDEFINED, 16, "EventDevice" }, + [11] = { en1545_NUMBER, 16, "EventRouteNumber" }, + [12] = { en1545_UNDEFINED, 8, "EventRouteVariant" }, + [13] = { en1545_UNDEFINED, 16, "EventJourneyRun" }, + [14] = { en1545_UNDEFINED, 16, "EventVehiculeId" }, + [15] = { en1545_UNDEFINED, 8, "EventVehiculeClass" }, + [16] = { en1545_UNDEFINED, 5, "EventLocationType" }, + [17] = { en1545_UNDEFINED,240, "EventEmployee" }, + [18] = { en1545_UNDEFINED, 16, "EventLocationReference" }, + [19] = { en1545_UNDEFINED, 8, "EventJourneyInterchanges" }, + [20] = { en1545_UNDEFINED, 16, "EventPeriodJourneys" }, + [21] = { en1545_UNDEFINED, 16, "EventTotalJourneys" }, + [22] = { en1545_UNDEFINED, 16, "EventJourneyDistance" }, + [23] = { en1545_AMOUNT, 16, "EventPriceAmount" }, + [24] = { en1545_UNDEFINED, 16, "EventPriceUnit" }, + [25] = { en1545_UNDEFINED, 5, "EventContractPointer" }, + [26] = { en1545_UNDEFINED, 16, "EventAuthenticator" }, + [27] = { en1545_UNDEFINED, 5, "EventBitmapExtra" }, + }} } -en1545_map(CARD,"Environment",intercode_Env,intercode_Holder) -en1545_map(CARD,"Event logs",intercode_Event) -en1545_map(CARD,"Special events",intercode_Event) -en1545_map(CARD,"Contract list",intercode_BestContracts) - -- FIXME: here we should parse the "contracts" according to "Tariff" - -- but for now we will assume that contract format is 'FF' - -- as defined in intercode -en1545_map(CARD,"Contracts",intercode_Contract) +function intercode_map_contracts(contract_mappings) + if #intercode_Contract_types ~= #intercode_Contract_pointers then + log.print(log.WARNING, "Cannot map contracts: #intercode_Contract_types != #intercode_Contract_pointers") + return + end + for i, pointer in ipairs(intercode_Contract_pointers) do + local contract_type = intercode_Contract_types[i] + local contract_type_str = string.format('%02Xh', contract_type) + + if contract_mappings[contract_type_str] == nil then + -- Support for more contract types can be added by defining contract_mappings["XXh"] + log.print(log.INFO, "Cannot map contract "..pointer..": contract type "..contract_type_str.." not implemented") + else + local contract_mapping = contract_mappings[contract_type_str] + log.print(log.INFO, "Map contract "..pointer..": contract type "..contract_type_str) + + -- Determine the record associated to this contract pointer + -- TODO : adapt to other card type (the following mapping is for CD97 structure 2) + local file_id + local rec_id + if pointer >= 5 then + file_id = 2030 + rec_id = pointer - 4 + else + file_id = 2020 + rec_id = pointer + end + + local condition = { + file = { label = "Contracts", id = file_id }, + record = { label = "record", id = rec_id }, + } + + en1545_map(CARD, condition, contract_mapping) + end + end +end +en1545_map(CARD, "Environment", intercode_Env, intercode_Holder) +en1545_map(CARD, "Contract list", intercode_BestContracts) +intercode_map_contracts(intercode_Contract) +en1545_map(CARD, "Event logs", intercode_Event) +en1545_map(CARD, "Special events", intercode_Event) calypso_card_num() diff --git a/dot_cardpeek_dir/scripts/calypso/c250n64.lua b/dot_cardpeek_dir/scripts/calypso/c250n64.lua new file mode 100644 index 0000000..02ac21d --- /dev/null +++ b/dot_cardpeek_dir/scripts/calypso/c250n64.lua @@ -0,0 +1,91 @@ +-- +-- This file is part of Cardpeek, the smartcard reader utility. +-- +-- Copyright 2009-2013 by 'L1L1' +-- +-- Cardpeek is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- Cardpeek is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with Cardpeek. If not, see . +-- + +require('lib.strict') + +SERVICE_PROVIDERS = { + [5] = "TAM" +} + +TRANSPORT_LIST = { + [1] = "Urban Bus", + [2] = "Interurban Bus", + [3] = "Metro", + [4] = "Tram", + [5] = "Train", + [8] = "Parking" +} + +TRANSITION_LIST = { + [1] = "Entry", + [2] = "Exit", + [4] = "Inspection", + [6] = "Interchange (entry)", + [7] = "Interchange (exit)" +} + +function process_events(cardenv,node_label) + local event_node + local record_node + local ref_node + + local code_value + local code_transport + local code_transition + local code_transport_string + local code_transition_string + local code_string + local service_provider_value + local location_id_value + local sector_id + local station_id + local location_string + + event_node = cardenv:find_first({label=node_label, parsed="true"}) + + if event_node==nil then + log.print(log.WARNING,"No " .. node_label .. " found in card") + return 0 + end + + for record_node in event_node:find({label="record"}) do + + -- is it TAM? + ref_node = record_node:find_first({label="EventServiceProvider"}) + service_provider_value = bytes.tonumber(ref_node:get_attribute("val")) + ref_node:set_attribute("alt",SERVICE_PROVIDERS[service_provider_value]) + + ref_node = record_node:find_first({label="EventCode"}) + code_value = bytes.tonumber(ref_node:get_attribute("val")) + + -- is it a bus, a tram, ...? + code_transport = bit.SHR(code_value,4) + code_transport_string = TRANSPORT_LIST[code_transport] + if code_transport_string==nil then code_transport_string = code_transport end + + -- is it an entry, an exit, ...? + code_transition = bit.AND(code_value,0xF) + code_transition_string = TRANSITION_LIST[code_transition] + if (code_transition_string==nil) then code_transition_string = code_transition end + + ref_node:set_attribute("alt",code_transport_string.." - "..code_transition_string) + end +end + +process_events(CARD,"Event logs") diff --git a/dot_cardpeek_dir/scripts/calypso/c376n3.lua b/dot_cardpeek_dir/scripts/calypso/c376.lua old mode 100755 new mode 100644 similarity index 56% rename from dot_cardpeek_dir/scripts/calypso/c376n3.lua rename to dot_cardpeek_dir/scripts/calypso/c376.lua index 1195689..b89fc91 --- a/dot_cardpeek_dir/scripts/calypso/c376n3.lua +++ b/dot_cardpeek_dir/scripts/calypso/c376.lua @@ -18,24 +18,32 @@ -- --********************************************************************-- -- --- This file is based on c376n2.lua, which was authored and contributed +-- This file is based on c376n2.lua, which was authored and contributed -- to cardpeek by Anthony Berkow (C) 2013 --- --- Some minor alterations of the original file were performed for +-- +-- Some minor alterations of the original file were performed for -- integration with calypso.lua --- +-- -- Jan 2015: -- This file c376n3.lua was created to take into account some specific -- isses discovered with a new type of RavKav card with network id = 3 --- and which does not return a FCI. +-- and which does not return a FCI. -- This induced a change in RavKav_getRecordInfo() --- +-- +-- 2017: +-- Updated +-- c376n3.lua moved to c376.lua to improve recognizability. All RavKav +-- cards are interoperable across all c376 networks. Issuers except n2 +-- address file records by path/number/short EF instead of using FCI +-- record identifiers. Per ISO 7816-4 sections 5.1.2, 5.1.3, 5.1.4, 5.1.5 +-- --********************************************************************-- require('lib.apdu') require('lib.tlv') require('lib.en1545') require('lib.country_codes') +require('lib.calypso_card_num') require('etc.ravkav-strings') --Classes @@ -45,19 +53,23 @@ ISO_CLS_PRO9 = 0x90 --As for ISO_CLS_STD but the coding and meaning of comma ISO_CLS_SM_PRO = 0x04 --Proprietary secure messaging format --Application ID -AID_RavKav = "#315449432e494341" --"1TIC.ICA" +AID_RavKav = "#315449432e494341" --"1TIC.ICA" / RID 315449432e49, PIX 4341 --LIDs -CALYPSO_LID_ENVIRONMENT = "2001" --SFI=0x07, linear, 1 record -CALYPSO_LID_EVENTS_LOG = "2010" --SFI=0x08, cyclic, 3 records -CALYPSO_LID_CONTRACTS = "2020" --SFI=0x09, linear, 4 records -CALYPSO_LID_COUNTERS = "2069" --SFI=0x19, counters, 9 counters +CALYPSO_LID_ENVIRONMENT = "2001" --SFI=0x07, linear, 1 record +CALYPSO_LID_EVENTS_LOG = "2010" --SFI=0x08, cyclic, 6 records +CALYPSO_LID_CONTRACTS = "2020" --SFI=0x09, linear, 8 records +CALYPSO_LID_COUNTERS = "2069" --SFI=0x19, counters, 9 counters +CALYPSO_LID_SPECIAL_EVENTS = "2040" --SFI=0x1D, linear, 4 records +CALYPSO_LID_CONTRACT_LIST = "2050" --SFI=0x1E, linear, 1 record LID_LIST = { {"General Information", CALYPSO_LID_ENVIRONMENT, "environment"}, {"Counters", CALYPSO_LID_COUNTERS, "counters"}, {"Latest Travel", CALYPSO_LID_EVENTS_LOG, "event"}, - {"Contracts", CALYPSO_LID_CONTRACTS, "contract"} + {"Contracts", CALYPSO_LID_CONTRACTS, "contract"}, + {"Special events", CALYPSO_LID_SPECIAL_EVENTS, "special event"}, + {"Contract list", CALYPSO_LID_CONTRACT_LIST, "contract list"} } function ravkav_parse_serial_number(node,data) @@ -68,19 +80,19 @@ end function ravkav_parse_ats(node,data) if data and 5 <= #data then local byteOffset = 1 - byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "File type", en1545_NUMBER) - byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "EF type", en1545_NUMBER) - byteOffset, recordLen = RavKav_parseBits(data,byteOffset, 1, node, "Record size", en1545_NUMBER) - byteOffset, numRecords = RavKav_parseBits(data,byteOffset, 1, node, "Record count", en1545_NUMBER) - byteOffset = RavKav_parseBits(data, byteOffset, 4, node, "Access", en1545_UNDEFINED) + byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "File type", en1545_NUMBER) + byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "EF type", en1545_NUMBER) + byteOffset, recordLen = RavKav_parseBits(data, byteOffset, 1, node, "Record size", en1545_NUMBER) + byteOffset, numRecords = RavKav_parseBits(data, byteOffset, 1, node, "Record count", en1545_NUMBER) + byteOffset = RavKav_parseBits(data, byteOffset, 4, node, "Access", en1545_UNDEFINED) end end --Tags RAVKAV_IDO = { - ['A5/BF0C'] = {"Secure messaging data"}, - ['BF0C/C7'] = {"Serial number", ravkav_parse_serial_number}, - ['85'] = {"Record Info", ravkav_parse_ats}, + ['A5/BF0C'] = { "Secure messaging data" }, + ['BF0C/C7'] = { "Serial number", ravkav_parse_serial_number }, + ['85'] = { "Record Info", ravkav_parse_ats }, } ValidUntilOption = { @@ -134,11 +146,11 @@ function RavKav_parseEf(ctx) --recordLen = ri_data[3] --numRecords = ri_data[4] local byteOffset = 1 - byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "File type", en1545_NUMBER) - byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "EF type", en1545_NUMBER) - byteOffset, recordLen = RavKav_parseBits(ri_data,byteOffset, 1, RECINFO_REF, "Record size", en1545_NUMBER) - byteOffset, numRecords = RavKav_parseBits(ri_data,byteOffset, 1, RECINFO_REF, "Record count", en1545_NUMBER) - byteOffset = RavKav_parseBits(ri_data, byteOffset, 4, RECINFO_REF, "Access", en1545_UNDEFINED) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "File type", en1545_NUMBER) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "EF type", en1545_NUMBER) + byteOffset, recordLen = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "Record size", en1545_NUMBER) + byteOffset, numRecords = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "Record count", en1545_NUMBER) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 4, RECINFO_REF, "Access", en1545_UNDEFINED) log.print(log.DBG, string.format("numRecords: %d, recordLen: %d", numRecords, recordLen)) end return numRecords, recordLen @@ -149,8 +161,8 @@ function RavKav_getRecordInfo(ctx) local recordLen = 0 local RECINFO_REF = nodes.find_first(ctx, {label="Record Info"}) if RECINFO_REF then - numRecords = RavKav_getFieldAsNumber(RECINFO_REF, "Record count") - recordLen = RavKav_getFieldAsNumber(RECINFO_REF, "Record size") + numRecords = RavKav_getFieldAsNumber(RECINFO_REF, "Record count" ) + recordLen = RavKav_getFieldAsNumber(RECINFO_REF, "Record size" ) else local iter for iter in nodes.find(ctx,{label="record"}) do @@ -243,6 +255,13 @@ function RavKav_ISSUER(source) return tostring(issuerId) end +function RavKav_COMPANY(source) + local companyId = bytes.tonumber(source) + local company = RAVKAV_COMPANIES[companyId] + if company then return company end + return tostring(companyId) +end + function RavKav_PROFILE(source) local profileId = bytes.tonumber(source) local profile = RAVKAV_PROFILES[profileId] @@ -257,11 +276,32 @@ function RavKav_ROUTE(source) return tostring(routeSystemId) end -function RavKav_LOCATION(source) - local locationId = bytes.tonumber(source) - local location = RAVKAV_LOCATIONS[locationId] - if location then return location end - return tostring(locationId) +function RavKav_LINE_TYPE(source) + local lineTypeId = bytes.tonumber(source) + local lineType = RAVKAV_LINE_TYPES[lineTypeId] + if lineType then return lineType end + return tostring(lineTypeId) +end + +function RavKav_RAIL_LOCATION(source) + local railLocationId = bytes.tonumber(source) + local railLocation = RAVKAV_RAIL_LOCATIONS[railLocationId] + if railLocation then return railLocation end + return tostring(railLocationId) +end + +function RavKav_RAIL_LOCATION2(source) + local railLocation2Id = bytes.tonumber(source) + local railLocation2 = RAVKAV_RAIL_LOCATIONS2[railLocation2Id] + if railLocation2 then return railLocation2 end + return tostring(railLocation2Id) +end + +function RavKav_BUS_LOCATION(source) + local busLocationId = bytes.tonumber(source) + local busLocation = RAVKAV_BUS_LOCATIONS[busLocationId] + if busLocation then return busLocation end + return tostring(busLocationId) end function RavKav_VALIDITY_TYPE(source) @@ -271,6 +311,14 @@ function RavKav_VALIDITY_TYPE(source) return tostring(validityType) end +function RavKav_INTERCHANGE_AREA(source) + local interchangesAreaId = bytes.tonumber(source) + local interchangesArea = RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] + if interchangesArea then return interchangesArea end + return tostring(interchangesAreaId) +end + +--Predefined contract IDs function RavKav_CONTRACT_TYPE(source) local contractType = bytes.tonumber(source) local contract = RAVKAV_CONTRACT_TYPES[contractType] @@ -336,7 +384,6 @@ end function RavKav_getFieldsAsNumberAry(SRC_REF, a_label, a_id) local node local ary = {} - for node in nodes.find(SRC_REF, { label=a_label, id=a_id }) do local data = nodes.get_attribute(node,"val") if data then table.insert(ary, bytes.tonumber(data)) end @@ -373,13 +420,13 @@ function RavKav_parseEnvironment(ENV_REF, nRec) bitOffset = RavKav_parseBits(data, bitOffset, 3, ENV_REC_REF, "Version number", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 12, ENV_REC_REF, "Country", RavKav_COUNTRY) bitOffset = RavKav_parseBits(data, bitOffset, 8, ENV_REC_REF, "Issuer", RavKav_ISSUER) - bitOffset = RavKav_parseBits(data, bitOffset, 26, ENV_REC_REF, "Application number", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 26, ENV_REC_REF, "Issuance number", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Date of issue", en1545_DATE) bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "End date", en1545_DATE) bitOffset = RavKav_parseBits(data, bitOffset, 3, ENV_REC_REF, "Pay method", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 32, ENV_REC_REF, "Date of birth", en1545_BCD_DATE) - bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Company (not set)", en1545_NUMBER) - bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "Company ID (not set)", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Company", RavKav_COMPANY) + bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "Company ID", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "ID number", en1545_NUMBER) local PROFILES_REF = nodes.append(ENV_REC_REF, {classname="item", label="Profiles"}) @@ -460,16 +507,16 @@ function RavKav_parseEvent(EVENTS_REF, nRec) local contractId, eventType bitOffset, contractId = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Contract ID", en1545_NUMBER) - bitOffset = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Area ID", en1545_NUMBER) --1 == urban, 2 == intercity + bitOffset = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Line type", RavKav_LINE_TYPE) bitOffset, eventType = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Event type", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "Event time", en1545_DATE_TIME) - bitOffset = RavKav_parseBits(data, bitOffset, 1, EVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond - bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' + bitOffset = RavKav_parseBits(data, bitOffset, 1, EVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' otherwise aggregate value prevalidation time for Israel Railways local PRIORITIES_REF = nodes.append(EVENT_REC_REF, {classname="item", label="Best contract priorities"}) local nContract - for nContract = 1, 8 do - bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) + for nContract = 1, 8 do + bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) end local locationBitmap @@ -478,10 +525,10 @@ function RavKav_parseEvent(EVENTS_REF, nRec) bitOffset, text, ref, locationBitmap = RavKav_parseBits(data, bitOffset, 7, EVENT_REC_REF, "Location bitmap", en1545_UNDEFINED) --defined per issuer if 0 < bit.AND(locationBitmap, 1) then - if 2 == issuerId then --Israel Rail - bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location", RavKav_LOCATION) --station + if 2 == issuerId then --Israel Railways + bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location", RavKav_RAIL_LOCATION) --station else - bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location ID", en1545_NUMBER) --place + bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location ID (GTFS stops.txt)", RavKav_BUS_LOCATION) --stop_code end end @@ -491,27 +538,24 @@ function RavKav_parseEvent(EVENTS_REF, nRec) end if 0 < bit.AND(locationBitmap, 4) then - bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Stop en route", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Stop en route", RavKav_RAIL_LOCATION2) end if 0 < bit.AND(locationBitmap, 8) then --12 unknown bits - bitOffset = RavKav_parseBits(data, bitOffset, 12, EVENT_REC_REF, "Location[3]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 12, EVENT_REC_REF, "Location[3]", en1545_NUMBER) end if 0 < bit.AND(locationBitmap, 0x10) then - bitOffset = RavKav_parseBits(data, bitOffset, 14, EVENT_REC_REF, "Vehicle", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 14, EVENT_REC_REF, "Vehicle", en1545_NUMBER) end - if 0 < bit.AND(locationBitmap, 0x20) then --how many bits?? - log.print(log.DBG, string.format("Event[%d]: Location[5]: unknown value", nRec)) + if 0 < bit.AND(locationBitmap, 0x20) then + bitOffset = RavKav_parseBits(data, bitOffset, 25, EVENT_REC_REF, "Unknown", en1545_NUMBER) end - if 0 < bit.AND(locationBitmap, 0x40) then --8 unknown bits - if 0 < bit.AND(locationBitmap, 0x20) then -- we are lost due to previous unknown field - log.print(log.DBG, string.format("Event[%d]: Location[6]: unknown value", nRec)) - else - bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Location[6]", en1545_UNDEFINED) - end + if 0 < bit.AND(locationBitmap, 0x40) then + local interchangesAreaId + bitOffset, interchangesAreaId = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Journey interchange area", RavKav_INTERCHANGE_AREA) end if 0 == bit.AND(locationBitmap, 0x20) then @@ -546,11 +590,11 @@ function RavKav_parseEvent(EVENTS_REF, nRec) if 3 == issuerId then --Egged bitOffset = 155 lnBitsize = 10 - if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end - elseif 15 == issuerId then --Metropolis + if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end --if Location[3] is set + elseif 15 == issuerId then --Metropoline bitOffset = 123 lnBitsize = 32 - elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Rail, Nativ Express nor Superbus + elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Railways, Nativ Express nor Superbus bitOffset = 145 lnBitsize = 10 end @@ -572,6 +616,138 @@ function RavKav_parseEventsLog(APP_REF) end end +function RavKav_parseSpecialEvent(SPECIALEVENTS_REF, nRec) + local SPECIALEVENT_REC_REF = nodes.find_first(SPECIALEVENTS_REF, {label="record", id=nRec}) + if nil == SPECIALEVENT_REC_REF then return end + + local record = nodes.get_attribute(SPECIALEVENT_REC_REF,"val") + if nil == record then return end + + local data = bytes.convert(record,1) + + if bytes.is_all(data, 0) then return end + + local bitOffset = 0 + + local text, ref, issuerId + + bitOffset = RavKav_parseBits(data, bitOffset, 3, SPECIALEVENT_REC_REF, "Version number", en1545_NUMBER) + bitOffset, text, ref, issuerId = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Issuer", RavKav_ISSUER) + + if issuerId == 0 then return end + + local contractId, eventType + + bitOffset, contractId = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Contract ID", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Line type", RavKav_LINE_TYPE) + bitOffset, eventType = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Event type", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 30, SPECIALEVENT_REC_REF, "Event time", en1545_DATE_TIME) + bitOffset = RavKav_parseBits(data, bitOffset, 1, SPECIALEVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset = RavKav_parseBits(data, bitOffset, 30, SPECIALEVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' otherwise aggregate value prevalidation time for Israel Railways + + local PRIORITIES_REF = nodes.append(SPECIALEVENT_REC_REF, {classname="item", label="Best contract priorities"}) + local nContract + for nContract = 1, 8 do + bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) + end + + local locationBitmap + local ln_ref = nil + + bitOffset, text, ref, locationBitmap = RavKav_parseBits(data, bitOffset, 7, SPECIALEVENT_REC_REF, "Location bitmap", en1545_UNDEFINED) --defined per issuer + + if 0 < bit.AND(locationBitmap, 1) then + if 2 == issuerId then --Israel Railways + bitOffset = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Location", RavKav_RAIL_LOCATION) --station + else + bitOffset = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)", RavKav_BUS_LOCATION) --stop_code + end + end + + if 0 < bit.AND(locationBitmap, 2) then + local lineNumber + bitOffset, lineNumber, ln_ref = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Line number", en1545_NUMBER) --number of line on route + end + + if 0 < bit.AND(locationBitmap, 4) then + bitOffset = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Stop en route", RavKav_RAIL_LOCATION2) + end + + if 0 < bit.AND(locationBitmap, 8) then --12 unknown bits + bitOffset = RavKav_parseBits(data, bitOffset, 12, SPECIALEVENT_REC_REF, "Location[3]", en1545_UNDEFINED) + end + + if 0 < bit.AND(locationBitmap, 0x10) then + bitOffset = RavKav_parseBits(data, bitOffset, 14, SPECIALEVENT_REC_REF, "Vehicle", en1545_NUMBER) + end + + if 0 < bit.AND(locationBitmap, 0x20) then + bitOffset = RavKav_parseBits(data, bitOffset, 25, SPECIALEVENT_REC_REF, "Unknown", en1545_NUMBER) + end + + if 0 < bit.AND(locationBitmap, 0x40) then + local interchangesAreaId + bitOffset, interchangesAreaId = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Journey interchange area", RavKav_INTERCHANGE_AREA) + end + + if 0 == bit.AND(locationBitmap, 0x20) then + local eventExtension + bitOffset, text, ref, eventExtension = RavKav_parseBits(data, bitOffset, 3, SPECIALEVENT_REC_REF, "Event extension bitmap", en1545_UNDEFINED) + + if 0 < bit.AND(eventExtension, 1) then + bitOffset = RavKav_parseBits(data, bitOffset, 10, SPECIALEVENT_REC_REF, "Route system", RavKav_ROUTE) + bitOffset = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Fare code", en1545_NUMBER) + local debitAmount, dr_ref + bitOffset, debitAmount, dr_ref = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Debit amount", en1545_NUMBER) + if 0 < debitAmount and 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + if 21 > debitAmount then + dr_ref:set_attribute("alt",string.format("%u trip(s)", debitAmount)) + else + dr_ref:set_attribute("alt",string.format("NIS %0.2f", debitAmount / 100.0)) + end + end + end + + if 0 < bit.AND(eventExtension, 2) then + bitOffset = RavKav_parseBits(data, bitOffset, 32, SPECIALEVENT_REC_REF, "Event extension[2]", en1545_UNDEFINED) + end + + if 0 < bit.AND(eventExtension, 4) then + bitOffset = RavKav_parseBits(data, bitOffset, 32, SPECIALEVENT_REC_REF, "Event extension[3]", en1545_UNDEFINED) + end + end + + -- fix 'Line number' field + local lnBitsize = 0 + if 3 == issuerId then --Egged + bitOffset = 155 + lnBitsize = 10 + if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end --if Location[3] is set + elseif 15 == issuerId then --Metropoline + bitOffset = 123 + lnBitsize = 32 + elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Railways, Nativ Express nor Superbus + bitOffset = 145 + lnBitsize = 10 + end + if 0 < lnBitsize then + if ln_ref then ln_ref:remove() end + RavKav_parseBits(data, bitOffset, lnBitsize, SPECIALEVENT_REC_REF, "Line number", en1545_NUMBER) + end +end + +function RavKav_parseSpecialEventsLog(APP_REF) + log.print(log.DBG, "Parsing special events...") + local SPECIALEVENTS_REF = nodes.find_first(APP_REF, {label="Special events"} ) + if SPECIALEVENTS_REF then + local numRecords = RavKav_getRecordInfo(SPECIALEVENTS_REF) + local nRec + for nRec = 1, numRecords do + RavKav_parseSpecialEvent(SPECIALEVENTS_REF, nRec) + end + end +end + function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local CONTRACT_REC_REF = nodes.find_first(CONTRACTS_REF, {label="record", id=nRec}) if nil == CONTRACT_REC_REF then return end @@ -597,17 +773,17 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) bitOffset, text, ref, issuerId = RavKav_parseBits(data, bitOffset, 8, CONTRACT_REC_REF, "Issuer", RavKav_ISSUER) if issuerId == 0 then return end - local ticketType, validityBitmap + local etta, validityBitmap local contractValid = true bitOffset = RavKav_parseBits(data, bitOffset, 2, CONTRACT_REC_REF, "Tariff transport access", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 3, CONTRACT_REC_REF, "Tariff counter use", en1545_NUMBER) --0 == not used, 2 == number of tokens, 3 == monetary amount - bitOffset, ticketType = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Ticket type", en1545_NUMBER) + bitOffset, etta = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Ticket type (ETT_A)", en1545_NUMBER) - if 1 == ticketType or 6 == ticketType or 7 == ticketType then --Single or multiple, Aggregate value or Single or multiple + if 1 == etta or 6 == etta or 7 == etta then --Amount of rides, Aggregate value or Amount of rides contractValid = 0 < counter local balance - if 6 == ticketType then --Aggregate value + if 6 == etta then --Aggregate value balance = string.format("NIS %0.2f", counter / 100.0) --balanceRemaining else balance = string.format("%d trip(s)", counter) --tripsRemaining @@ -620,8 +796,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) bitOffset = RavKav_parseBits(data, bitOffset, 14, CONTRACT_REC_REF, "Date of purchase", en1545_DATE) --sale date bitOffset = RavKav_parseBits(data, bitOffset, 12, CONTRACT_REC_REF, "Sale device", en1545_NUMBER) --serial number of device that made the sale bitOffset = RavKav_parseBits(data, bitOffset, 10, CONTRACT_REC_REF, "Sale number", en1545_NUMBER) --runnning sale number on the day of the sale - bitOffset = RavKav_parseBits(data, bitOffset, 1, CONTRACT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond - bitOffset, text, ref, validityBitmap = RavKav_parseBits(data, bitOffset, 9, CONTRACT_REC_REF, "Validity bitmap", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 1, CONTRACT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset, text, ref, validityBitmap = RavKav_parseBits(data, bitOffset, 9, CONTRACT_REC_REF, "Validity bitmap", en1545_UNDEFINED) if 0 < bit.AND(validityBitmap, 1) then bitOffset = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Validity[1]", en1545_UNDEFINED) @@ -629,25 +805,27 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local restrictionCode = 0 if 0 < bit.AND(validityBitmap, 2) then - bitOffset, restrictionCode = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Restriction code", en1545_NUMBER) + bitOffset, restrictionCode = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Restriction code", en1545_NUMBER) --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS end if 0 < bit.AND(validityBitmap, 4) then local iDuration, rd_ref - bitOffset, iDuration, rd_ref = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Restriction duration", en1545_NUMBER) + bitOffset, iDuration, rd_ref = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Restriction duration", en1545_NUMBER) --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS if 16 == restrictionCode then iDuration = iDuration * 5 else iDuration = iDuration * 30 end rd_ref:set_attribute("alt",string.format("%d minute(s)", iDuration)) + + --add if restrictionCode 2 then set attribute %d trips, iTtrips end if 0 < bit.AND(validityBitmap,8) then --validity end date local vtd_ref bitOffset, text, vtd_ref = RavKav_parseBits(data, bitOffset, 14, CONTRACT_REC_REF, "Valid until", en1545_DATE) if contractValid then - contractValid = RavKav_rkDaysToSeconds(vtd_ref:val()) > os.time() + contractValid = nil end end @@ -660,7 +838,7 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) validUntilOption = ValidUntilOption.Date local validUntilSeconds = RavKav_calculateMonthEnd(validFromSeconds, validMonths - 1) --month end date - local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=14}) --month end date + local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=14}) --month end date nodes.set_attribute(NEW_REF,"val", bytes.new(8, string.format("%04X", validUntilSeconds))) nodes.set_attribute(NEW_REF,"alt", os.date("%x", validUntilSeconds)) @@ -673,7 +851,7 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=2}) nodes.set_attribute(NEW_REF,"val", bytes.new(8, string.format("%01X", validUntilOption))) - nodes.set_attribute(NEW_REF,"alt", "The end of the service") + nodes.set_attribute(NEW_REF,"alt", "The end of the service (04:30 of next day)") if contractValid then contractValid = validFromSeconds > os.time() @@ -696,7 +874,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 < bit.AND(validityBitmap, 0x40) then - bitOffset = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Validity[6]", en1545_UNDEFINED) + local profileId + bitOffset, text, ref, profileId = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Contract profile", RavKav_PROFILE) end if 0 < bit.AND(validityBitmap, 0x80) then @@ -704,7 +883,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 < bit.AND(validityBitmap, 0x100) then - bitOffset = RavKav_parseBits(data, bitOffset, 32, CONTRACT_REC_REF, "Validity[8]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 16, CONTRACT_REC_REF, "Validity[8]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 16, CONTRACT_REC_REF, "Validity[9]", en1545_UNDEFINED) end -- read validity locations @@ -723,35 +903,37 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) elseif 1 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) elseif 3 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 32, LOC_REF, "Validity", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 16, LOC_REF, "Validity", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Origin station", RavKav_RAIL_LOCATION2, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Destination station", RavKav_RAIL_LOCATION2, validityType) elseif 7 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) - elseif 8 == validityType then --unknown validity type except for Israel Rail? - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) --? - bitOffset = RavKav_parseBits(data, bitOffset, 14, LOC_REF, "Unknown", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) + elseif 8 == validityType then --unknown validity type except for Israel Railways? + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) --? + bitOffset = RavKav_parseBits(data, bitOffset, 14, LOC_REF, "Unknown", en1545_UNDEFINED, validityType) elseif 9 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 3, LOC_REF, "Extended ticket type (ETT)", en1545_NUMBER, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 11, LOC_REF, "Contract type", RavKav_CONTRACT_TYPE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 3, LOC_REF, "Extended ticket type (ETT_B)", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 11, LOC_REF, "Contract type", RavKav_CONTRACT_TYPE, validityType) elseif 11 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 21, LOC_REF, "Validity", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 21, LOC_REF, "Validity", en1545_UNDEFINED, validityType) elseif 14 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", en1545_NUMBER, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) else log.print(log.DBG, string.format("Contract %d: Validity location[%d]: unrecognised validityType: %d", nRec, nLoc, validityType)) break --since we don't know the next bit position end end - RavKav_parseBits(data, 224, 8,CONTRACT_REC_REF, "Contract authenticator", en1545_UNDEFINED) --checksum? + RavKav_parseBits(data, 224, 8,CONTRACT_REC_REF, "Contract authenticator", en1545_NUMBER) --Green lists reference - remote contract load - upon contact of card ID to green listed SAM local cv_ref = nodes.append(CONTRACT_REC_REF, {classname="item", label="Contract valid", size=1}) if contractValid then @@ -790,9 +972,19 @@ function RavKav_summariseGeneralInfo(APP_REF, SUM_REF) if ENV_REF then local GI_REF = nodes.append(SUM_REF, {classname="file", label="General Information"}) - RavKav_copyField(APP_REF, GI_REF, "Serial number") + RavKav_copyField(APP_REF, GI_REF, "card number") RavKav_copyField(ENV_REF, GI_REF, "Issuer", nil, RAVKAV_ISSUERS) + RavKav_copyField(ENV_REF, GI_REF, "Issuance number") + RavKav_copyField(ENV_REF, GI_REF, "Date of issue") + RavKav_copyField(ENV_REF, GI_REF, "End date") + RavKav_copyField(ENV_REF, GI_REF, "Company", nil, RAVKAV_COMPANIES) + + local companyIdNumber = RavKav_getFieldAsNumber(ENV_REF, "Company ID") + if 0 == companyIdNumber then + local REF = nodes.append(GI_REF, {classname="item", label="Company ID", size=0}) + nodes.set_attribute(REF,"alt", "Not set") + end local idNumber = RavKav_getFieldAsNumber(ENV_REF, "ID number") if 0 == idNumber then local REF = nodes.append(GI_REF, {classname="item", label="Identity number", size=0}) @@ -800,6 +992,7 @@ function RavKav_summariseGeneralInfo(APP_REF, SUM_REF) return end + RavKav_copyField(ENV_REF,GI_REF, "Company ID") RavKav_copyField(ENV_REF,GI_REF, "ID number") RavKav_copyField(ENV_REF,GI_REF, "Date of birth") @@ -815,7 +1008,7 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) local EVENT_REC_REF = nodes.find_first(EVENTS_REF, {label="record", id=nRec}) if nil == EVENT_REC_REF then return end - local issuerId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Issuer") + local issuerId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Issuer", nil, RAVKAV_ISSUERS) if 0 == issuerId then return end local EVSUM_REF = nodes.append(LT_REF, {classname="record", label="Event", id=nRec}) @@ -829,10 +1022,10 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) local lineNumber = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line number") if 0 < lineNumber then description = description.." line " - local routeSystemId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Route system") + local routeSystemId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Route system", nil, RAVKAV_ROUTES) local sLineNum = tostring(lineNumber) if RAVKAV_ROUTES[routeSystemId] then - if 15 == issuerId then --Metropolis + if 15 == issuerId then --Metropoline local sRouteSystemId = tostring(routeSystemId) local posS, posE = string.find(sLineNum, sRouteSystemId) if 1 == posS then --Does "1234567..." start with "12"? @@ -844,6 +1037,23 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) description = description..sLineNum end end + + local lineType = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line type") + if 0 < lineType then + description = description.."" + local lineTypeId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line type", nil, RAVKAV_LINE_TYPES) + local sLineType = tostring(lineType) + if RAVKAV_LINE_TYPES[lineTypeId] then + local sLineTypeId = tostring(lineTypeId) + local posS, posE = string.find(sLineType, sLineTypeId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineType = string.sub(sLineType, posE + 1, posE + 4) --"1234567..." -> "345" + end + description = description..sLineType..", line type "..RAVKAV_LINE_TYPES[lineTypeId] + else + description = description..sLineType + end + end if 0 < #description then RavKav_addTextNode(EVSUM_REF, "Description", description) end ------------------------------------------ @@ -852,10 +1062,31 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) RavKav_copyField(EVENT_REC_REF, EVSUM_REF, "Event time") end - if 2 == issuerId then --Israel Rail - local locationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location") - if RAVKAV_LOCATIONS[locationId] then - RavKav_addTextNode(EVSUM_REF, "Station", RAVKAV_LOCATIONS[locationId]) + local interchangesAreaId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Journey interchange area", nil, RAVKAV_INTERCHANGE_AREAS) + if RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] then RavKav_addTextNode(EVSUM_REF, "Journey interchange area", RAVKAV_INTERCHANGE_AREAS[interchangesAreaId]) end + + if 2 == issuerId then --Israel Railways + local railLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location", nil, RAVKAV_RAIL_LOCATIONS) + if RAVKAV_RAIL_LOCATIONS[railLocationId] then + RavKav_addTextNode(EVSUM_REF, "Station", RAVKAV_RAIL_LOCATIONS[railLocationId]) --station + else + local railLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location") + RavKav_addTextNode(EVSUM_REF, "Station", railLocationId) + end + local railLocation2Id = RavKav_getFieldAsNumber(EVENT_REC_REF, "Stop en route", nil, RAVKAV_RAIL_LOCATIONS2) + if RAVKAV_RAIL_LOCATIONS2[railLocation2Id] then + RavKav_addTextNode(EVSUM_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2[railLocation2Id]) --destination station + else + local railLocation2Id = RavKav_getFieldAsNumber(EVENT_REC_REF, "Stop en route") + RavKav_addTextNode(EVSUM_REF, "Destination station", railLocation2Id) + end + else + local busLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location ID (GTFS stops.txt)", nil, RAVKAV_BUS_LOCATIONS) + if RAVKAV_BUS_LOCATIONS[busLocationId] then + RavKav_addTextNode(EVSUM_REF, "Stop ID (GTFS stops.txt)", RAVKAV_BUS_LOCATIONS[busLocationId]) --stop_code + else + local busLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location ID (GTFS stops.txt)") + RavKav_addTextNode(EVSUM_REF, "Stop ID (GTFS stops.txt)", busLocationId) end end @@ -864,7 +1095,7 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) details = "" local eventType = RavKav_getFieldAsNumber(EVENT_REC_REF, "Event type") local contractId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Contract ID") - if 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + if 4 == eventType and 0 < contractId and 9 > contractId then --is prevalidation local REF, data, debitedText = RavKav_getField(EVENT_REC_REF, "Debit amount") local debitAmount = 0 if data then debitAmount = bytes.tonumber(data) end @@ -872,6 +1103,23 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) if 1 ~= eventType then --not an exit event details = RAVKAV_EVENT_TYPES[eventType] end + details = details.." contract "..tostring(contractId).." prevalidated "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(EVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + elseif 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + local REF, data, debitedText = RavKav_getField(EVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end details = details.." contract "..tostring(contractId).." charged "..debitedText else details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) @@ -901,6 +1149,151 @@ function RavKav_summariseLatestTravel(APP_REF, SUM_REF) end end +function RavKav_summariseSpecialEvent(SPECIALEVENTS_REF, nRec, LT_REF) + local SPECIALEVENT_REC_REF = nodes.find_first(SPECIALEVENTS_REF, {label="record", id=nRec}) + if nil == SPECIALEVENT_REC_REF then return end + + local issuerId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Issuer", nil, RAVKAV_ISSUERS) + if 0 == issuerId then return end + + local SP_EVSUM_REF = nodes.append(LT_REF, {classname="record", label="Special Event", id=nRec}) + + + -- Description --------------------------- + local description = "" + + if RAVKAV_ISSUERS[issuerId] then description = RAVKAV_ISSUERS[issuerId] end + + local lineNumber = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line number") + if 0 < lineNumber then + description = description.." line " + local routeSystemId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Route system", nil, RAVKAV_ROUTES) + local sLineNum = tostring(lineNumber) + if RAVKAV_ROUTES[routeSystemId] then + if 15 == issuerId then --Metropoline + local sRouteSystemId = tostring(routeSystemId) + local posS, posE = string.find(sLineNum, sRouteSystemId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineNum = string.sub(sLineNum, posE + 1, posE + 4) --"1234567..." -> "345" + end + end + description = description..sLineNum..", cluster "..RAVKAV_ROUTES[routeSystemId] + else + description = description..sLineNum + end + end + + local lineType = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line type") + if 0 < lineType then + description = description.."" + local lineTypeId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line type", nil, RAVKAV_LINE_TYPES) + local sLineType = tostring(lineType) + if RAVKAV_LINE_TYPES[lineTypeId] then + local sLineTypeId = tostring(lineTypeId) + local posS, posE = string.find(sLineType, sLineTypeId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineType = string.sub(sLineType, posE + 1, posE + 4) --"1234567..." -> "345" + end + description = description..sLineType..", line type "..RAVKAV_LINE_TYPES[lineTypeId] + else + description = description..sLineType + end + end + if 0 < #description then RavKav_addTextNode(SP_EVSUM_REF, "Description", description) end + ------------------------------------------ + + + if 0 < RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Event time") then + RavKav_copyField(SPECIALEVENT_REC_REF, SP_EVSUM_REF, "Event time") + end + + local interchangesAreaId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Journey interchange area", nil, RAVKAV_INTERCHANGE_AREAS) + if RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] then RavKav_addTextNode(SP_EVSUM_REF, "Journey interchange area", RAVKAV_INTERCHANGE_AREAS[interchangesAreaId]) end + + if 2 == issuerId then --Israel Railways + local railLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location", nil, RAVKAV_RAIL_LOCATIONS) + if RAVKAV_RAIL_LOCATIONS[railLocationId] then + RavKav_addTextNode(SP_EVSUM_REF, "Station", RAVKAV_RAIL_LOCATIONS[railLocationId]) --station + else + local railLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location") + RavKav_addTextNode(SP_EVSUM_REF, "Station", railLocationId) + end + local railLocation2Id = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Stop en route", nil, RAVKAV_RAIL_LOCATIONS2) + if RAVKAV_RAIL_LOCATIONS2[railLocation2Id] then + RavKav_addTextNode(SP_EVSUM_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2[railLocation2Id]) --destination station + else + local railLocation2Id = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Stop en route") + RavKav_addTextNode(SP_EVSUM_REF, "Destination station", railLocation2Id) + end + else + local busLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)", nil, RAVKAV_BUS_LOCATIONS) + if RAVKAV_BUS_LOCATIONS[busLocationId] then + RavKav_addTextNode(SP_EVSUM_REF, "Stop ID (GTFS stops.txt)", RAVKAV_BUS_LOCATIONS[busLocationId]) --stop_code + else + local busLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)") + RavKav_addTextNode(SP_EVSUM_REF, "Stop ID (GTFS stops.txt)", busLocationId) + end + end + + + -- Details ------------------------------- + details = "" + local eventType = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Event type") + local contractId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Contract ID") + if 4 == eventType and 0 < contractId and 9 > contractId then --is prevalidation + local REF, data, debitedText = RavKav_getField(SPECIALEVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end + details = details.." contract "..tostring(contractId).." prevalidated "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + elseif 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + local REF, data, debitedText = RavKav_getField(SPECIALEVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end + details = details.." contract "..tostring(contractId).." charged "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + else + details = RAVKAV_EVENT_TYPES[eventType] + end + if 0 < #details then RavKav_addTextNode(SP_EVSUM_REF, "Details", details) end + ------------------------------------------ +end + +function RavKav_summariseSpecialEvents(APP_REF, SUM_REF) + log.print(log.DBG, "Summarising special events...") + local SPECIALEVENTS_REF = nodes.find_first(APP_REF, {label="Special events"}) + if SPECIALEVENTS_REF then + local LT_REF = nodes.append(SUM_REF, {classname="file", label="Special events"}) + local numRecords = RavKav_getRecordInfo(SPECIALEVENTS_REF) + local nRec + for nRec = 1, numRecords do + RavKav_summariseSpecialEvent(SPECIALEVENTS_REF, nRec, LT_REF) + end + end +end + function RavKav_summariseTextArray(REC_REF, itmLabel, lookupAry, SM_REF, sumLabel) local summary = "" local ary = RavKav_getFieldsAsNumberAry(REC_REF, itmLabel) @@ -943,23 +1336,29 @@ function RavKav_summariseContract(CONTRACTS_REF, nRec, VC_REF, IC_REF) -- Description --------------------------- local description = "" - if RAVKAV_ISSUERS[issuerId] then description = RAVKAV_ISSUERS[issuerId] end + local profileId = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Contract profile") + if RAVKAV_PROFILES[profileId] then description = description..""..RAVKAV_PROFILES[profileId] end - local ticketType = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Ticket type") - if RAVKAV_TICKET_TYPES[ticketType] then description = description.." "..RAVKAV_TICKET_TYPES[ticketType] end + local issuerId = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Issuer") + if RAVKAV_ISSUERS[issuerId] then description = description.." / "..RAVKAV_ISSUERS[issuerId] end + + local etta = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Ticket type (ETT_A)") + if RAVKAV_TICKET_TYPES[etta] then description = description.." / "..RAVKAV_TICKET_TYPES[etta] end if 0 < #description then RavKav_addTextNode(CTSUM_REF, "Description", description) end ------------------------------------------ RavKav_summariseTextArray(CONTRACT_REC_REF, "Route ID", RAVKAV_ROUTES, CTSUM_REF, "Clusters") + RavKav_summariseTextArray(CONTRACT_REC_REF, "Origin station", RAVKAV_RAIL_LOCATIONS2, CTSUM_REF, "Origin station") + RavKav_summariseTextArray(CONTRACT_REC_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2, CTSUM_REF, "Destination station") -- Type of contract ---------------------- - if 0 < ticketType then - local REF, data = RavKav_getField(CONTRACT_REC_REF, "Extended ticket type (ETT)") + if 0 < etta then + local REF, data = RavKav_getField(CONTRACT_REC_REF, "Extended ticket type (ETT_B)") if data then - local ett = bytes.tonumber(data) - if RAVKAV_CONTRACT_DESCRIPTIONS[ticketType][ett] then - RavKav_addTextNode(CTSUM_REF, "Type of contract", RAVKAV_CONTRACT_DESCRIPTIONS[ticketType][ett]) + local ettb = bytes.tonumber(data) + if RAVKAV_CONTRACT_DESCRIPTIONS[etta][ettb] then + RavKav_addTextNode(CTSUM_REF, "Type of contract", RAVKAV_CONTRACT_DESCRIPTIONS[etta][ettb]) end end end @@ -968,11 +1367,11 @@ function RavKav_summariseContract(CONTRACTS_REF, nRec, VC_REF, IC_REF) RavKav_summariseIntegerArray(CONTRACT_REC_REF, "Tariff code", CTSUM_REF, "Tariff codes") RavKav_summariseTextArray(CONTRACT_REC_REF, "Contract type", RAVKAV_CONTRACT_TYPES, CTSUM_REF, "Contract types") - local journeyInterchangesFlag = 0 ~= RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Journey interchanges flag") + local journeyInterchangesFlag = 0 ~= RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Journey interchanges flag") --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS if journeyInterchangesFlag then RavKav_addTextNode(CTSUM_REF, "Journey interchanges", "Includes switching/resume") end - if 0 < RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Restriction duration") then - RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Restriction duration") + if 0 < RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Restriction duration") then ----TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Restriction duration") ----TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS end RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Balance") @@ -1008,7 +1407,7 @@ function RavKav_readFiles(APP_REF) local lid_index local lid_desc for lid_index, lid_desc in ipairs(LID_LIST) do - RavKav_readFile(APP_REF, lid_desc[1], lid_desc[2], lid_desc[3]) + RavKav_readFile(APP_REF, lid_desc[1], lid_desc[2], lid_desc[3], lid_desc[4], lid_desc[5], lid_desc[6]) end end @@ -1016,6 +1415,7 @@ function RavKav_parseRecords(APP_REF) RavKav_parseGeneralInfo(APP_REF) local cntrAry = RavKav_parseCounters(APP_REF) RavKav_parseEventsLog(APP_REF) + RavKav_parseSpecialEventsLog(APP_REF) RavKav_parseContracts(APP_REF, cntrAry) end @@ -1023,6 +1423,7 @@ function RavKav_summariseRecords(APP_REF, root) local SUM_REF = nodes.append(root, {classname="block", label="Summary"}) RavKav_summariseGeneralInfo(APP_REF, SUM_REF) RavKav_summariseLatestTravel(APP_REF, SUM_REF) + RavKav_summariseSpecialEvents(APP_REF, SUM_REF) RavKav_summariseContracts(APP_REF, SUM_REF) end @@ -1037,7 +1438,7 @@ function RavKav_parseAnswerToSelect(ctx) end +calypso_card_num() RavKav_parseAnswerToSelect(CARD) RavKav_parseRecords(CARD) -RavKav_summariseRecords(CARD, CARD) - +RavKav_summariseRecords(CARD, CARD) \ No newline at end of file diff --git a/dot_cardpeek_dir/scripts/calypso/c376n2.lua b/dot_cardpeek_dir/scripts/calypso/c376n2.lua index 980d847..79e10bf 100644 --- a/dot_cardpeek_dir/scripts/calypso/c376n2.lua +++ b/dot_cardpeek_dir/scripts/calypso/c376n2.lua @@ -18,18 +18,22 @@ -- --********************************************************************-- -- --- This file, c376n2.lua, was authored and contributed to cardpeek by +-- This file, c376n2.lua, was authored and contributed to cardpeek by -- Anthony Berkow (C) 2013 --- --- Some minor alterations of the original file were performed for +-- +-- Some minor alterations of the original file were performed for -- integration with calypso.lua --- +-- +-- 2017: +-- Updated +-- --********************************************************************-- require('lib.apdu') require('lib.tlv') require('lib.en1545') require('lib.country_codes') +require('lib.calypso_card_num') require('etc.ravkav-strings') --Classes @@ -39,19 +43,23 @@ ISO_CLS_PRO9 = 0x90 --As for ISO_CLS_STD but the coding and meaning of comma ISO_CLS_SM_PRO = 0x04 --Proprietary secure messaging format --Application ID -AID_RavKav = "#315449432e494341" --"1TIC.ICA" +AID_RavKav = "#315449432e494341" --"1TIC.ICA" / RID 315449432e49, PIX 4341 --LIDs -CALYPSO_LID_ENVIRONMENT = "2001" --SFI=0x07, linear, 1 record -CALYPSO_LID_EVENTS_LOG = "2010" --SFI=0x08, cyclic, 3 records -CALYPSO_LID_CONTRACTS = "2020" --SFI=0x09, linear, 4 records -CALYPSO_LID_COUNTERS = "2069" --SFI=0x19, counters, 9 counters +CALYPSO_LID_ENVIRONMENT = "2001" --SFI=0x07, linear, 1 record +CALYPSO_LID_EVENTS_LOG = "2010" --SFI=0x08, cyclic, 6 records +CALYPSO_LID_CONTRACTS = "2020" --SFI=0x09, linear, 8 records +CALYPSO_LID_COUNTERS = "2069" --SFI=0x19, counters, 9 counters +CALYPSO_LID_SPECIAL_EVENTS = "2040" --SFI=0x1D, linear, 4 records +CALYPSO_LID_CONTRACT_LIST = "2050" --SFI=0x1E, linear, 1 record LID_LIST = { {"General Information", CALYPSO_LID_ENVIRONMENT, "environment"}, {"Counters", CALYPSO_LID_COUNTERS, "counters"}, {"Latest Travel", CALYPSO_LID_EVENTS_LOG, "event"}, - {"Contracts", CALYPSO_LID_CONTRACTS, "contract"} + {"Contracts", CALYPSO_LID_CONTRACTS, "contract"}, + {"Special events", CALYPSO_LID_SPECIAL_EVENTS, "special event"}, + {"Contract list", CALYPSO_LID_CONTRACT_LIST, "contract list"} } function ravkav_parse_serial_number(node,data) @@ -62,19 +70,19 @@ end function ravkav_parse_ats(node,data) if data and 5 <= #data then local byteOffset = 1 - byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "File type", en1545_NUMBER) - byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "EF type", en1545_NUMBER) - byteOffset, recordLen = RavKav_parseBits(data,byteOffset, 1, node, "Record size", en1545_NUMBER) - byteOffset, numRecords = RavKav_parseBits(data,byteOffset, 1, node, "Record count", en1545_NUMBER) - byteOffset = RavKav_parseBits(data, byteOffset, 4, node, "Access", en1545_UNDEFINED) + byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "File type", en1545_NUMBER) + byteOffset = RavKav_parseBits(data, byteOffset, 1, node, "EF type", en1545_NUMBER) + byteOffset, recordLen = RavKav_parseBits(data, byteOffset, 1, node, "Record size", en1545_NUMBER) + byteOffset, numRecords = RavKav_parseBits(data, byteOffset, 1, node, "Record count", en1545_NUMBER) + byteOffset = RavKav_parseBits(data, byteOffset, 4, node, "Access", en1545_UNDEFINED) end end --Tags RAVKAV_IDO = { - ['A5/BF0C'] = {"Secure messaging data"}, - ['BF0C/C7'] = {"Serial number", ravkav_parse_serial_number}, - ['85'] = {"Record Info", ravkav_parse_ats}, + ['A5/BF0C'] = { "Secure messaging data" }, + ['BF0C/C7'] = { "Serial number", ravkav_parse_serial_number }, + ['85'] = { "Record Info", ravkav_parse_ats }, } ValidUntilOption = { @@ -128,11 +136,11 @@ function RavKav_parseEf(ctx) --recordLen = ri_data[3] --numRecords = ri_data[4] local byteOffset = 1 - byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "File type", en1545_NUMBER) - byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "EF type", en1545_NUMBER) - byteOffset, recordLen = RavKav_parseBits(ri_data,byteOffset, 1, RECINFO_REF, "Record size", en1545_NUMBER) - byteOffset, numRecords = RavKav_parseBits(ri_data,byteOffset, 1, RECINFO_REF, "Record count", en1545_NUMBER) - byteOffset = RavKav_parseBits(ri_data, byteOffset, 4, RECINFO_REF, "Access", en1545_UNDEFINED) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "File type", en1545_NUMBER) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "EF type", en1545_NUMBER) + byteOffset, recordLen = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "Record size", en1545_NUMBER) + byteOffset, numRecords = RavKav_parseBits(ri_data, byteOffset, 1, RECINFO_REF, "Record count", en1545_NUMBER) + byteOffset = RavKav_parseBits(ri_data, byteOffset, 4, RECINFO_REF, "Access", en1545_UNDEFINED) log.print(log.DBG, string.format("numRecords: %d, recordLen: %d", numRecords, recordLen)) end return numRecords, recordLen @@ -143,8 +151,8 @@ function RavKav_getRecordInfo(ctx) local recordLen = 0 local RECINFO_REF = nodes.find_first(ctx, {label="Record Info"}) if RECINFO_REF then - numRecords = RavKav_getFieldAsNumber(RECINFO_REF, "Record count") - recordLen = RavKav_getFieldAsNumber(RECINFO_REF, "Record size") + numRecords = RavKav_getFieldAsNumber(RECINFO_REF, "Record count" ) + recordLen = RavKav_getFieldAsNumber(RECINFO_REF, "Record size" ) end return numRecords, recordLen end @@ -231,6 +239,13 @@ function RavKav_ISSUER(source) return tostring(issuerId) end +function RavKav_COMPANY(source) + local companyId = bytes.tonumber(source) + local company = RAVKAV_COMPANIES[companyId] + if company then return company end + return tostring(companyId) +end + function RavKav_PROFILE(source) local profileId = bytes.tonumber(source) local profile = RAVKAV_PROFILES[profileId] @@ -245,11 +260,32 @@ function RavKav_ROUTE(source) return tostring(routeSystemId) end -function RavKav_LOCATION(source) - local locationId = bytes.tonumber(source) - local location = RAVKAV_LOCATIONS[locationId] - if location then return location end - return tostring(locationId) +function RavKav_LINE_TYPE(source) + local lineTypeId = bytes.tonumber(source) + local lineType = RAVKAV_LINE_TYPES[lineTypeId] + if lineType then return lineType end + return tostring(lineTypeId) +end + +function RavKav_RAIL_LOCATION(source) + local railLocationId = bytes.tonumber(source) + local railLocation = RAVKAV_RAIL_LOCATIONS[railLocationId] + if railLocation then return railLocation end + return tostring(railLocationId) +end + +function RavKav_RAIL_LOCATION2(source) + local railLocation2Id = bytes.tonumber(source) + local railLocation2 = RAVKAV_RAIL_LOCATIONS2[railLocation2Id] + if railLocation2 then return railLocation2 end + return tostring(railLocation2Id) +end + +function RavKav_BUS_LOCATION(source) + local busLocationId = bytes.tonumber(source) + local busLocation = RAVKAV_BUS_LOCATIONS[busLocationId] + if busLocation then return busLocation end + return tostring(busLocationId) end function RavKav_VALIDITY_TYPE(source) @@ -259,6 +295,14 @@ function RavKav_VALIDITY_TYPE(source) return tostring(validityType) end +function RavKav_INTERCHANGE_AREA(source) + local interchangesAreaId = bytes.tonumber(source) + local interchangesArea = RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] + if interchangesArea then return interchangesArea end + return tostring(interchangesAreaId) +end + +--Predefined contract IDs function RavKav_CONTRACT_TYPE(source) local contractType = bytes.tonumber(source) local contract = RAVKAV_CONTRACT_TYPES[contractType] @@ -324,7 +368,6 @@ end function RavKav_getFieldsAsNumberAry(SRC_REF, a_label, a_id) local node local ary = {} - for node in nodes.find(SRC_REF, { label=a_label, id=a_id }) do local data = nodes.get_attribute(node,"val") if data then table.insert(ary, bytes.tonumber(data)) end @@ -361,13 +404,13 @@ function RavKav_parseEnvironment(ENV_REF, nRec) bitOffset = RavKav_parseBits(data, bitOffset, 3, ENV_REC_REF, "Version number", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 12, ENV_REC_REF, "Country", RavKav_COUNTRY) bitOffset = RavKav_parseBits(data, bitOffset, 8, ENV_REC_REF, "Issuer", RavKav_ISSUER) - bitOffset = RavKav_parseBits(data, bitOffset, 26, ENV_REC_REF, "Application number", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 26, ENV_REC_REF, "Issuance number", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Date of issue", en1545_DATE) bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "End date", en1545_DATE) bitOffset = RavKav_parseBits(data, bitOffset, 3, ENV_REC_REF, "Pay method", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 32, ENV_REC_REF, "Date of birth", en1545_BCD_DATE) - bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Company (not set)", en1545_NUMBER) - bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "Company ID (not set)", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 14, ENV_REC_REF, "Company", RavKav_COMPANY) + bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "Company ID", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 30, ENV_REC_REF, "ID number", en1545_NUMBER) local PROFILES_REF = nodes.append(ENV_REC_REF, {classname="item", label="Profiles"}) @@ -448,16 +491,16 @@ function RavKav_parseEvent(EVENTS_REF, nRec) local contractId, eventType bitOffset, contractId = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Contract ID", en1545_NUMBER) - bitOffset = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Area ID", en1545_NUMBER) --1 == urban, 2 == intercity + bitOffset = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Line type", RavKav_LINE_TYPE) bitOffset, eventType = RavKav_parseBits(data, bitOffset, 4, EVENT_REC_REF, "Event type", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "Event time", en1545_DATE_TIME) - bitOffset = RavKav_parseBits(data, bitOffset, 1, EVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond - bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' + bitOffset = RavKav_parseBits(data, bitOffset, 1, EVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset = RavKav_parseBits(data, bitOffset, 30, EVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' otherwise aggregate value prevalidation time for Israel Railways local PRIORITIES_REF = nodes.append(EVENT_REC_REF, {classname="item", label="Best contract priorities"}) local nContract - for nContract = 1, 8 do - bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) + for nContract = 1, 8 do + bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) end local locationBitmap @@ -466,10 +509,10 @@ function RavKav_parseEvent(EVENTS_REF, nRec) bitOffset, text, ref, locationBitmap = RavKav_parseBits(data, bitOffset, 7, EVENT_REC_REF, "Location bitmap", en1545_UNDEFINED) --defined per issuer if 0 < bit.AND(locationBitmap, 1) then - if 2 == issuerId then --Israel Rail - bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location", RavKav_LOCATION) --station + if 2 == issuerId then --Israel Railways + bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location", RavKav_RAIL_LOCATION) --station else - bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location ID", en1545_NUMBER) --place + bitOffset = RavKav_parseBits(data, bitOffset, 16, EVENT_REC_REF, "Location ID (GTFS stops.txt)", RavKav_BUS_LOCATION) --stop_code end end @@ -479,27 +522,24 @@ function RavKav_parseEvent(EVENTS_REF, nRec) end if 0 < bit.AND(locationBitmap, 4) then - bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Stop en route", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Stop en route", RavKav_RAIL_LOCATION2) end if 0 < bit.AND(locationBitmap, 8) then --12 unknown bits - bitOffset = RavKav_parseBits(data, bitOffset, 12, EVENT_REC_REF, "Location[3]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 12, EVENT_REC_REF, "Location[3]", en1545_NUMBER) end if 0 < bit.AND(locationBitmap, 0x10) then - bitOffset = RavKav_parseBits(data, bitOffset, 14, EVENT_REC_REF, "Vehicle", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 14, EVENT_REC_REF, "Vehicle", en1545_NUMBER) end - if 0 < bit.AND(locationBitmap, 0x20) then --how many bits?? - log.print(log.DBG, string.format("Event[%d]: Location[5]: unknown value", nRec)) + if 0 < bit.AND(locationBitmap, 0x20) then + bitOffset = RavKav_parseBits(data, bitOffset, 25, EVENT_REC_REF, "Unknown", en1545_NUMBER) end - if 0 < bit.AND(locationBitmap, 0x40) then --8 unknown bits - if 0 < bit.AND(locationBitmap, 0x20) then -- we are lost due to previous unknown field - log.print(log.DBG, string.format("Event[%d]: Location[6]: unknown value", nRec)) - else - bitOffset = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Location[6]", en1545_UNDEFINED) - end + if 0 < bit.AND(locationBitmap, 0x40) then + local interchangesAreaId + bitOffset, interchangesAreaId = RavKav_parseBits(data, bitOffset, 8, EVENT_REC_REF, "Journey interchange area", RavKav_INTERCHANGE_AREA) end if 0 == bit.AND(locationBitmap, 0x20) then @@ -534,11 +574,11 @@ function RavKav_parseEvent(EVENTS_REF, nRec) if 3 == issuerId then --Egged bitOffset = 155 lnBitsize = 10 - if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end - elseif 15 == issuerId then --Metropolis + if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end --if Location[3] is set + elseif 15 == issuerId then --Metropoline bitOffset = 123 lnBitsize = 32 - elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Rail, Nativ Express nor Superbus + elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Railways, Nativ Express nor Superbus bitOffset = 145 lnBitsize = 10 end @@ -560,6 +600,138 @@ function RavKav_parseEventsLog(APP_REF) end end +function RavKav_parseSpecialEvent(SPECIALEVENTS_REF, nRec) + local SPECIALEVENT_REC_REF = nodes.find_first(SPECIALEVENTS_REF, {label="record", id=nRec}) + if nil == SPECIALEVENT_REC_REF then return end + + local record = nodes.get_attribute(SPECIALEVENT_REC_REF,"val") + if nil == record then return end + + local data = bytes.convert(record,1) + + if bytes.is_all(data, 0) then return end + + local bitOffset = 0 + + local text, ref, issuerId + + bitOffset = RavKav_parseBits(data, bitOffset, 3, SPECIALEVENT_REC_REF, "Version number", en1545_NUMBER) + bitOffset, text, ref, issuerId = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Issuer", RavKav_ISSUER) + + if issuerId == 0 then return end + + local contractId, eventType + + bitOffset, contractId = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Contract ID", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Line type", RavKav_LINE_TYPE) + bitOffset, eventType = RavKav_parseBits(data, bitOffset, 4, SPECIALEVENT_REC_REF, "Event type", en1545_NUMBER) + bitOffset = RavKav_parseBits(data, bitOffset, 30, SPECIALEVENT_REC_REF, "Event time", en1545_DATE_TIME) + bitOffset = RavKav_parseBits(data, bitOffset, 1, SPECIALEVENT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset = RavKav_parseBits(data, bitOffset, 30, SPECIALEVENT_REC_REF, "First event time", en1545_DATE_TIME) --identical to 'Event time' otherwise aggregate value prevalidation time for Israel Railways + + local PRIORITIES_REF = nodes.append(SPECIALEVENT_REC_REF, {classname="item", label="Best contract priorities"}) + local nContract + for nContract = 1, 8 do + bitOffset = RavKav_parseBits(data, bitOffset, 4, PRIORITIES_REF, "Contract", RavKav_PERCENT, nContract) + end + + local locationBitmap + local ln_ref = nil + + bitOffset, text, ref, locationBitmap = RavKav_parseBits(data, bitOffset, 7, SPECIALEVENT_REC_REF, "Location bitmap", en1545_UNDEFINED) --defined per issuer + + if 0 < bit.AND(locationBitmap, 1) then + if 2 == issuerId then --Israel Railways + bitOffset = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Location", RavKav_RAIL_LOCATION) --station + else + bitOffset = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)", RavKav_BUS_LOCATION) --stop_code + end + end + + if 0 < bit.AND(locationBitmap, 2) then + local lineNumber + bitOffset, lineNumber, ln_ref = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Line number", en1545_NUMBER) --number of line on route + end + + if 0 < bit.AND(locationBitmap, 4) then + bitOffset = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Stop en route", RavKav_RAIL_LOCATION2) + end + + if 0 < bit.AND(locationBitmap, 8) then --12 unknown bits + bitOffset = RavKav_parseBits(data, bitOffset, 12, SPECIALEVENT_REC_REF, "Location[3]", en1545_UNDEFINED) + end + + if 0 < bit.AND(locationBitmap, 0x10) then + bitOffset = RavKav_parseBits(data, bitOffset, 14, SPECIALEVENT_REC_REF, "Vehicle", en1545_NUMBER) + end + + if 0 < bit.AND(locationBitmap, 0x20) then + bitOffset = RavKav_parseBits(data, bitOffset, 25, SPECIALEVENT_REC_REF, "Unknown", en1545_NUMBER) + end + + if 0 < bit.AND(locationBitmap, 0x40) then + local interchangesAreaId + bitOffset, interchangesAreaId = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Journey interchange area", RavKav_INTERCHANGE_AREA) + end + + if 0 == bit.AND(locationBitmap, 0x20) then + local eventExtension + bitOffset, text, ref, eventExtension = RavKav_parseBits(data, bitOffset, 3, SPECIALEVENT_REC_REF, "Event extension bitmap", en1545_UNDEFINED) + + if 0 < bit.AND(eventExtension, 1) then + bitOffset = RavKav_parseBits(data, bitOffset, 10, SPECIALEVENT_REC_REF, "Route system", RavKav_ROUTE) + bitOffset = RavKav_parseBits(data, bitOffset, 8, SPECIALEVENT_REC_REF, "Fare code", en1545_NUMBER) + local debitAmount, dr_ref + bitOffset, debitAmount, dr_ref = RavKav_parseBits(data, bitOffset, 16, SPECIALEVENT_REC_REF, "Debit amount", en1545_NUMBER) + if 0 < debitAmount and 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + if 21 > debitAmount then + dr_ref:set_attribute("alt",string.format("%u trip(s)", debitAmount)) + else + dr_ref:set_attribute("alt",string.format("NIS %0.2f", debitAmount / 100.0)) + end + end + end + + if 0 < bit.AND(eventExtension, 2) then + bitOffset = RavKav_parseBits(data, bitOffset, 32, SPECIALEVENT_REC_REF, "Event extension[2]", en1545_UNDEFINED) + end + + if 0 < bit.AND(eventExtension, 4) then + bitOffset = RavKav_parseBits(data, bitOffset, 32, SPECIALEVENT_REC_REF, "Event extension[3]", en1545_UNDEFINED) + end + end + + -- fix 'Line number' field + local lnBitsize = 0 + if 3 == issuerId then --Egged + bitOffset = 155 + lnBitsize = 10 + if 0 < bit.AND(locationBitmap, 8) then bitOffset = bitOffset + 12 end --if Location[3] is set + elseif 15 == issuerId then --Metropoline + bitOffset = 123 + lnBitsize = 32 + elseif 2 ~= issuerId and 14 ~= issuerId and 16 ~= issuerId then --not Israel Railways, Nativ Express nor Superbus + bitOffset = 145 + lnBitsize = 10 + end + if 0 < lnBitsize then + if ln_ref then ln_ref:remove() end + RavKav_parseBits(data, bitOffset, lnBitsize, SPECIALEVENT_REC_REF, "Line number", en1545_NUMBER) + end +end + +function RavKav_parseSpecialEventsLog(APP_REF) + log.print(log.DBG, "Parsing special events...") + local SPECIALEVENTS_REF = nodes.find_first(APP_REF, {label="Special events"} ) + if SPECIALEVENTS_REF then + local numRecords = RavKav_getRecordInfo(SPECIALEVENTS_REF) + local nRec + for nRec = 1, numRecords do + RavKav_parseSpecialEvent(SPECIALEVENTS_REF, nRec) + end + end +end + function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local CONTRACT_REC_REF = nodes.find_first(CONTRACTS_REF, {label="record", id=nRec}) if nil == CONTRACT_REC_REF then return end @@ -585,17 +757,17 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) bitOffset, text, ref, issuerId = RavKav_parseBits(data, bitOffset, 8, CONTRACT_REC_REF, "Issuer", RavKav_ISSUER) if issuerId == 0 then return end - local ticketType, validityBitmap + local etta, validityBitmap local contractValid = true bitOffset = RavKav_parseBits(data, bitOffset, 2, CONTRACT_REC_REF, "Tariff transport access", en1545_NUMBER) bitOffset = RavKav_parseBits(data, bitOffset, 3, CONTRACT_REC_REF, "Tariff counter use", en1545_NUMBER) --0 == not used, 2 == number of tokens, 3 == monetary amount - bitOffset, ticketType = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Ticket type", en1545_NUMBER) + bitOffset, etta = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Ticket type (ETT_A)", en1545_NUMBER) - if 1 == ticketType or 6 == ticketType or 7 == ticketType then --Single or multiple, Aggregate value or Single or multiple + if 1 == etta or 6 == etta or 7 == etta then --Amount of rides, Aggregate value or Amount of rides contractValid = 0 < counter local balance - if 6 == ticketType then --Aggregate value + if 6 == etta then --Aggregate value balance = string.format("NIS %0.2f", counter / 100.0) --balanceRemaining else balance = string.format("%d trip(s)", counter) --tripsRemaining @@ -608,8 +780,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) bitOffset = RavKav_parseBits(data, bitOffset, 14, CONTRACT_REC_REF, "Date of purchase", en1545_DATE) --sale date bitOffset = RavKav_parseBits(data, bitOffset, 12, CONTRACT_REC_REF, "Sale device", en1545_NUMBER) --serial number of device that made the sale bitOffset = RavKav_parseBits(data, bitOffset, 10, CONTRACT_REC_REF, "Sale number", en1545_NUMBER) --runnning sale number on the day of the sale - bitOffset = RavKav_parseBits(data, bitOffset, 1, CONTRACT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond - bitOffset, text, ref, validityBitmap = RavKav_parseBits(data, bitOffset, 9, CONTRACT_REC_REF, "Validity bitmap", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 1, CONTRACT_REC_REF, "Journey interchanges flag", en1545_NUMBER) --includes switching/continuing beyond, TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + bitOffset, text, ref, validityBitmap = RavKav_parseBits(data, bitOffset, 9, CONTRACT_REC_REF, "Validity bitmap", en1545_UNDEFINED) if 0 < bit.AND(validityBitmap, 1) then bitOffset = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Validity[1]", en1545_UNDEFINED) @@ -617,25 +789,27 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local restrictionCode = 0 if 0 < bit.AND(validityBitmap, 2) then - bitOffset, restrictionCode = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Restriction code", en1545_NUMBER) + bitOffset, restrictionCode = RavKav_parseBits(data, bitOffset, 5, CONTRACT_REC_REF, "Restriction code", en1545_NUMBER) --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS end if 0 < bit.AND(validityBitmap, 4) then local iDuration, rd_ref - bitOffset, iDuration, rd_ref = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Restriction duration", en1545_NUMBER) + bitOffset, iDuration, rd_ref = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Restriction duration", en1545_NUMBER) --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS if 16 == restrictionCode then iDuration = iDuration * 5 else iDuration = iDuration * 30 end rd_ref:set_attribute("alt",string.format("%d minute(s)", iDuration)) + + --add if restrictionCode 2 then set attribute %d trips, iTtrips end if 0 < bit.AND(validityBitmap,8) then --validity end date local vtd_ref bitOffset, text, vtd_ref = RavKav_parseBits(data, bitOffset, 14, CONTRACT_REC_REF, "Valid until", en1545_DATE) if contractValid then - contractValid = RavKav_rkDaysToSeconds(vtd_ref:val()) > os.time() + contractValid = nil end end @@ -648,7 +822,7 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) validUntilOption = ValidUntilOption.Date local validUntilSeconds = RavKav_calculateMonthEnd(validFromSeconds, validMonths - 1) --month end date - local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=14}) --month end date + local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=14}) --month end date nodes.set_attribute(NEW_REF,"val", bytes.new(8, string.format("%04X", validUntilSeconds))) nodes.set_attribute(NEW_REF,"alt", os.date("%x", validUntilSeconds)) @@ -661,7 +835,7 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) local NEW_REF = nodes.append(CONTRACT_REC_REF, {classname="item", label="Valid to", size=2}) nodes.set_attribute(NEW_REF,"val", bytes.new(8, string.format("%01X", validUntilOption))) - nodes.set_attribute(NEW_REF,"alt", "The end of the service") + nodes.set_attribute(NEW_REF,"alt", "The end of the service (04:30 of next day)") if contractValid then contractValid = validFromSeconds > os.time() @@ -684,7 +858,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 < bit.AND(validityBitmap, 0x40) then - bitOffset = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Validity[6]", en1545_UNDEFINED) + local profileId + bitOffset, text, ref, profileId = RavKav_parseBits(data, bitOffset, 6, CONTRACT_REC_REF, "Contract profile", RavKav_PROFILE) end if 0 < bit.AND(validityBitmap, 0x80) then @@ -692,7 +867,8 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 < bit.AND(validityBitmap, 0x100) then - bitOffset = RavKav_parseBits(data, bitOffset, 32, CONTRACT_REC_REF, "Validity[8]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 16, CONTRACT_REC_REF, "Validity[8]", en1545_UNDEFINED) + bitOffset = RavKav_parseBits(data, bitOffset, 16, CONTRACT_REC_REF, "Validity[9]", en1545_UNDEFINED) end -- read validity locations @@ -711,35 +887,37 @@ function RavKav_parseContract(CONTRACTS_REF, nRec, counter) end if 0 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) elseif 1 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) elseif 3 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 32, LOC_REF, "Validity", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 16, LOC_REF, "Validity", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Origin station", RavKav_RAIL_LOCATION2, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Destination station", RavKav_RAIL_LOCATION2, validityType) elseif 7 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) - elseif 8 == validityType then --unknown validity type except for Israel Rail? - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) --? - bitOffset = RavKav_parseBits(data, bitOffset, 14, LOC_REF, "Unknown", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) + elseif 8 == validityType then --unknown validity type except for Israel Railways? + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", RavKav_ROUTE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 8, LOC_REF, "Tariff code", en1545_NUMBER, validityType) --? + bitOffset = RavKav_parseBits(data, bitOffset, 14, LOC_REF, "Unknown", en1545_UNDEFINED, validityType) elseif 9 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 3, LOC_REF, "Extended ticket type (ETT)", en1545_NUMBER, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 11, LOC_REF, "Contract type", RavKav_CONTRACT_TYPE, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 3, LOC_REF, "Extended ticket type (ETT_B)", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 11, LOC_REF, "Contract type", RavKav_CONTRACT_TYPE, validityType) elseif 11 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 21, LOC_REF, "Validity", en1545_UNDEFINED, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 21, LOC_REF, "Validity", en1545_UNDEFINED, validityType) elseif 14 == validityType then - bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", en1545_NUMBER, validityType) - bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 10, LOC_REF, "Route ID", en1545_NUMBER, validityType) + bitOffset = RavKav_parseBits(data, bitOffset, 12, LOC_REF, "Spatial zones", RavKav_ZONES, validityType) else log.print(log.DBG, string.format("Contract %d: Validity location[%d]: unrecognised validityType: %d", nRec, nLoc, validityType)) break --since we don't know the next bit position end end - RavKav_parseBits(data, 224, 8,CONTRACT_REC_REF, "Contract authenticator", en1545_UNDEFINED) --checksum? + RavKav_parseBits(data, 224, 8,CONTRACT_REC_REF, "Contract authenticator", en1545_NUMBER) --Green lists reference - remote contract load - upon contact of card ID to green listed SAM local cv_ref = nodes.append(CONTRACT_REC_REF, {classname="item", label="Contract valid", size=1}) if contractValid then @@ -778,9 +956,19 @@ function RavKav_summariseGeneralInfo(APP_REF, SUM_REF) if ENV_REF then local GI_REF = nodes.append(SUM_REF, {classname="file", label="General Information"}) - RavKav_copyField(APP_REF, GI_REF, "Serial number") + RavKav_copyField(APP_REF, GI_REF, "card number") RavKav_copyField(ENV_REF, GI_REF, "Issuer", nil, RAVKAV_ISSUERS) + RavKav_copyField(ENV_REF, GI_REF, "Issuance number") + RavKav_copyField(ENV_REF, GI_REF, "Date of issue") + RavKav_copyField(ENV_REF, GI_REF, "End date") + + RavKav_copyField(ENV_REF, GI_REF, "Company", nil, RAVKAV_COMPANIES) + local companyIdNumber = RavKav_getFieldAsNumber(ENV_REF, "Company ID") + if 0 == companyIdNumber then + local REF = nodes.append(GI_REF, {classname="item", label="Company ID", size=0}) + nodes.set_attribute(REF,"alt", "Not set") + end local idNumber = RavKav_getFieldAsNumber(ENV_REF, "ID number") if 0 == idNumber then local REF = nodes.append(GI_REF, {classname="item", label="Identity number", size=0}) @@ -788,6 +976,7 @@ function RavKav_summariseGeneralInfo(APP_REF, SUM_REF) return end + RavKav_copyField(ENV_REF,GI_REF, "Company ID") RavKav_copyField(ENV_REF,GI_REF, "ID number") RavKav_copyField(ENV_REF,GI_REF, "Date of birth") @@ -803,7 +992,7 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) local EVENT_REC_REF = nodes.find_first(EVENTS_REF, {label="record", id=nRec}) if nil == EVENT_REC_REF then return end - local issuerId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Issuer") + local issuerId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Issuer", nil, RAVKAV_ISSUERS) if 0 == issuerId then return end local EVSUM_REF = nodes.append(LT_REF, {classname="record", label="Event", id=nRec}) @@ -817,10 +1006,10 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) local lineNumber = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line number") if 0 < lineNumber then description = description.." line " - local routeSystemId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Route system") + local routeSystemId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Route system", nil, RAVKAV_ROUTES) local sLineNum = tostring(lineNumber) if RAVKAV_ROUTES[routeSystemId] then - if 15 == issuerId then --Metropolis + if 15 == issuerId then --Metropoline local sRouteSystemId = tostring(routeSystemId) local posS, posE = string.find(sLineNum, sRouteSystemId) if 1 == posS then --Does "1234567..." start with "12"? @@ -832,6 +1021,23 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) description = description..sLineNum end end + + local lineType = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line type") + if 0 < lineType then + description = description.."" + local lineTypeId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Line type", nil, RAVKAV_LINE_TYPES) + local sLineType = tostring(lineType) + if RAVKAV_LINE_TYPES[lineTypeId] then + local sLineTypeId = tostring(lineTypeId) + local posS, posE = string.find(sLineType, sLineTypeId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineType = string.sub(sLineType, posE + 1, posE + 4) --"1234567..." -> "345" + end + description = description..sLineType..", line type "..RAVKAV_LINE_TYPES[lineTypeId] + else + description = description..sLineType + end + end if 0 < #description then RavKav_addTextNode(EVSUM_REF, "Description", description) end ------------------------------------------ @@ -840,10 +1046,31 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) RavKav_copyField(EVENT_REC_REF, EVSUM_REF, "Event time") end - if 2 == issuerId then --Israel Rail - local locationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location") - if RAVKAV_LOCATIONS[locationId] then - RavKav_addTextNode(EVSUM_REF, "Station", RAVKAV_LOCATIONS[locationId]) + local interchangesAreaId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Journey interchange area", nil, RAVKAV_INTERCHANGE_AREAS) + if RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] then RavKav_addTextNode(EVSUM_REF, "Journey interchange area", RAVKAV_INTERCHANGE_AREAS[interchangesAreaId]) end + + if 2 == issuerId then --Israel Railways + local railLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location", nil, RAVKAV_RAIL_LOCATIONS) + if RAVKAV_RAIL_LOCATIONS[railLocationId] then + RavKav_addTextNode(EVSUM_REF, "Station", RAVKAV_RAIL_LOCATIONS[railLocationId]) --station + else + local railLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location") + RavKav_addTextNode(EVSUM_REF, "Station", railLocationId) + end + local railLocation2Id = RavKav_getFieldAsNumber(EVENT_REC_REF, "Stop en route", nil, RAVKAV_RAIL_LOCATIONS2) + if RAVKAV_RAIL_LOCATIONS2[railLocation2Id] then + RavKav_addTextNode(EVSUM_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2[railLocation2Id]) --destination station + else + local railLocation2Id = RavKav_getFieldAsNumber(EVENT_REC_REF, "Stop en route") + RavKav_addTextNode(EVSUM_REF, "Destination station", railLocation2Id) + end + else + local busLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location ID (GTFS stops.txt)", nil, RAVKAV_BUS_LOCATIONS) + if RAVKAV_BUS_LOCATIONS[busLocationId] then + RavKav_addTextNode(EVSUM_REF, "Stop ID (GTFS stops.txt)", RAVKAV_BUS_LOCATIONS[busLocationId]) --stop_code + else + local busLocationId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Location ID (GTFS stops.txt)") + RavKav_addTextNode(EVSUM_REF, "Stop ID (GTFS stops.txt)", busLocationId) end end @@ -852,7 +1079,7 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) details = "" local eventType = RavKav_getFieldAsNumber(EVENT_REC_REF, "Event type") local contractId = RavKav_getFieldAsNumber(EVENT_REC_REF, "Contract ID") - if 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + if 4 == eventType and 0 < contractId and 9 > contractId then --is prevalidation local REF, data, debitedText = RavKav_getField(EVENT_REC_REF, "Debit amount") local debitAmount = 0 if data then debitAmount = bytes.tonumber(data) end @@ -860,6 +1087,23 @@ function RavKav_summariseEvent(EVENTS_REF, nRec, LT_REF) if 1 ~= eventType then --not an exit event details = RAVKAV_EVENT_TYPES[eventType] end + details = details.." contract "..tostring(contractId).." prevalidated "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(EVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + elseif 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + local REF, data, debitedText = RavKav_getField(EVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end details = details.." contract "..tostring(contractId).." charged "..debitedText else details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) @@ -889,6 +1133,151 @@ function RavKav_summariseLatestTravel(APP_REF, SUM_REF) end end +function RavKav_summariseSpecialEvent(SPECIALEVENTS_REF, nRec, LT_REF) + local SPECIALEVENT_REC_REF = nodes.find_first(SPECIALEVENTS_REF, {label="record", id=nRec}) + if nil == SPECIALEVENT_REC_REF then return end + + local issuerId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Issuer", nil, RAVKAV_ISSUERS) + if 0 == issuerId then return end + + local SP_EVSUM_REF = nodes.append(LT_REF, {classname="record", label="Special Event", id=nRec}) + + + -- Description --------------------------- + local description = "" + + if RAVKAV_ISSUERS[issuerId] then description = RAVKAV_ISSUERS[issuerId] end + + local lineNumber = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line number") + if 0 < lineNumber then + description = description.." line " + local routeSystemId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Route system", nil, RAVKAV_ROUTES) + local sLineNum = tostring(lineNumber) + if RAVKAV_ROUTES[routeSystemId] then + if 15 == issuerId then --Metropoline + local sRouteSystemId = tostring(routeSystemId) + local posS, posE = string.find(sLineNum, sRouteSystemId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineNum = string.sub(sLineNum, posE + 1, posE + 4) --"1234567..." -> "345" + end + end + description = description..sLineNum..", cluster "..RAVKAV_ROUTES[routeSystemId] + else + description = description..sLineNum + end + end + + local lineType = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line type") + if 0 < lineType then + description = description.."" + local lineTypeId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Line type", nil, RAVKAV_LINE_TYPES) + local sLineType = tostring(lineType) + if RAVKAV_LINE_TYPES[lineTypeId] then + local sLineTypeId = tostring(lineTypeId) + local posS, posE = string.find(sLineType, sLineTypeId) + if 1 == posS then --Does "1234567..." start with "12"? + sLineType = string.sub(sLineType, posE + 1, posE + 4) --"1234567..." -> "345" + end + description = description..sLineType..", line type "..RAVKAV_LINE_TYPES[lineTypeId] + else + description = description..sLineType + end + end + if 0 < #description then RavKav_addTextNode(SP_EVSUM_REF, "Description", description) end + ------------------------------------------ + + + if 0 < RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Event time") then + RavKav_copyField(SPECIALEVENT_REC_REF, SP_EVSUM_REF, "Event time") + end + + local interchangesAreaId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Journey interchange area", nil, RAVKAV_INTERCHANGE_AREAS) + if RAVKAV_INTERCHANGE_AREAS[interchangesAreaId] then RavKav_addTextNode(SP_EVSUM_REF, "Journey interchange area", RAVKAV_INTERCHANGE_AREAS[interchangesAreaId]) end + + if 2 == issuerId then --Israel Railways + local railLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location", nil, RAVKAV_RAIL_LOCATIONS) + if RAVKAV_RAIL_LOCATIONS[railLocationId] then + RavKav_addTextNode(SP_EVSUM_REF, "Station", RAVKAV_RAIL_LOCATIONS[railLocationId]) --station + else + local railLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location") + RavKav_addTextNode(SP_EVSUM_REF, "Station", railLocationId) + end + local railLocation2Id = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Stop en route", nil, RAVKAV_RAIL_LOCATIONS2) + if RAVKAV_RAIL_LOCATIONS2[railLocation2Id] then + RavKav_addTextNode(SP_EVSUM_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2[railLocation2Id]) --destination station + else + local railLocation2Id = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Stop en route") + RavKav_addTextNode(SP_EVSUM_REF, "Destination station", railLocation2Id) + end + else + local busLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)", nil, RAVKAV_BUS_LOCATIONS) + if RAVKAV_BUS_LOCATIONS[busLocationId] then + RavKav_addTextNode(SP_EVSUM_REF, "Stop ID (GTFS stops.txt)", RAVKAV_BUS_LOCATIONS[busLocationId]) --stop_code + else + local busLocationId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Location ID (GTFS stops.txt)") + RavKav_addTextNode(SP_EVSUM_REF, "Stop ID (GTFS stops.txt)", busLocationId) + end + end + + + -- Details ------------------------------- + details = "" + local eventType = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Event type") + local contractId = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Contract ID") + if 4 == eventType and 0 < contractId and 9 > contractId then --is prevalidation + local REF, data, debitedText = RavKav_getField(SPECIALEVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end + details = details.." contract "..tostring(contractId).." prevalidated "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + elseif 6 ~= eventType and 0 < contractId and 9 > contractId then --not a transit trip + local REF, data, debitedText = RavKav_getField(SPECIALEVENT_REC_REF, "Debit amount") + local debitAmount = 0 + if data then debitAmount = bytes.tonumber(data) end + if 0 < debitAmount then + if 1 ~= eventType then --not an exit event + details = RAVKAV_EVENT_TYPES[eventType] + end + details = details.." contract "..tostring(contractId).." charged "..debitedText + else + details = RAVKAV_EVENT_TYPES[eventType].." contract "..tostring(contractId) + end + + local fareCode = RavKav_getFieldAsNumber(SPECIALEVENT_REC_REF, "Fare code") + if 0 < fareCode then + details = details.." (code "..tostring(fareCode)..")" + end + else + details = RAVKAV_EVENT_TYPES[eventType] + end + if 0 < #details then RavKav_addTextNode(SP_EVSUM_REF, "Details", details) end + ------------------------------------------ +end + +function RavKav_summariseSpecialEvents(APP_REF, SUM_REF) + log.print(log.DBG, "Summarising special events...") + local SPECIALEVENTS_REF = nodes.find_first(APP_REF, {label="Special events"}) + if SPECIALEVENTS_REF then + local LT_REF = nodes.append(SUM_REF, {classname="file", label="Special events"}) + local numRecords = RavKav_getRecordInfo(SPECIALEVENTS_REF) + local nRec + for nRec = 1, numRecords do + RavKav_summariseSpecialEvent(SPECIALEVENTS_REF, nRec, LT_REF) + end + end +end + function RavKav_summariseTextArray(REC_REF, itmLabel, lookupAry, SM_REF, sumLabel) local summary = "" local ary = RavKav_getFieldsAsNumberAry(REC_REF, itmLabel) @@ -931,23 +1320,29 @@ function RavKav_summariseContract(CONTRACTS_REF, nRec, VC_REF, IC_REF) -- Description --------------------------- local description = "" - if RAVKAV_ISSUERS[issuerId] then description = RAVKAV_ISSUERS[issuerId] end + local profileId = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Contract profile") + if RAVKAV_PROFILES[profileId] then description = description..""..RAVKAV_PROFILES[profileId] end - local ticketType = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Ticket type") - if RAVKAV_TICKET_TYPES[ticketType] then description = description.." "..RAVKAV_TICKET_TYPES[ticketType] end + local issuerId = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Issuer") + if RAVKAV_ISSUERS[issuerId] then description = description.." / "..RAVKAV_ISSUERS[issuerId] end + + local etta = RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Ticket type (ETT_A)") + if RAVKAV_TICKET_TYPES[etta] then description = description.." / "..RAVKAV_TICKET_TYPES[etta] end if 0 < #description then RavKav_addTextNode(CTSUM_REF, "Description", description) end ------------------------------------------ RavKav_summariseTextArray(CONTRACT_REC_REF, "Route ID", RAVKAV_ROUTES, CTSUM_REF, "Clusters") + RavKav_summariseTextArray(CONTRACT_REC_REF, "Origin station", RAVKAV_RAIL_LOCATIONS2, CTSUM_REF, "Origin station") + RavKav_summariseTextArray(CONTRACT_REC_REF, "Destination station", RAVKAV_RAIL_LOCATIONS2, CTSUM_REF, "Destination station") -- Type of contract ---------------------- - if 0 < ticketType then - local REF, data = RavKav_getField(CONTRACT_REC_REF, "Extended ticket type (ETT)") + if 0 < etta then + local REF, data = RavKav_getField(CONTRACT_REC_REF, "Extended ticket type (ETT_B)") if data then - local ett = bytes.tonumber(data) - if RAVKAV_CONTRACT_DESCRIPTIONS[ticketType][ett] then - RavKav_addTextNode(CTSUM_REF, "Type of contract", RAVKAV_CONTRACT_DESCRIPTIONS[ticketType][ett]) + local ettb = bytes.tonumber(data) + if RAVKAV_CONTRACT_DESCRIPTIONS[etta][ettb] then + RavKav_addTextNode(CTSUM_REF, "Type of contract", RAVKAV_CONTRACT_DESCRIPTIONS[etta][ettb]) end end end @@ -956,11 +1351,11 @@ function RavKav_summariseContract(CONTRACTS_REF, nRec, VC_REF, IC_REF) RavKav_summariseIntegerArray(CONTRACT_REC_REF, "Tariff code", CTSUM_REF, "Tariff codes") RavKav_summariseTextArray(CONTRACT_REC_REF, "Contract type", RAVKAV_CONTRACT_TYPES, CTSUM_REF, "Contract types") - local journeyInterchangesFlag = 0 ~= RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Journey interchanges flag") + local journeyInterchangesFlag = 0 ~= RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Journey interchanges flag") --TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS if journeyInterchangesFlag then RavKav_addTextNode(CTSUM_REF, "Journey interchanges", "Includes switching/resume") end - if 0 < RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Restriction duration") then - RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Restriction duration") + if 0 < RavKav_getFieldAsNumber(CONTRACT_REC_REF, "Restriction duration") then ----TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS + RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Restriction duration") ----TODO: migrate to RAVKAV_INTERCHANGE_RIGHTS end RavKav_copyField(CONTRACT_REC_REF, CTSUM_REF, "Balance") @@ -996,7 +1391,7 @@ function RavKav_readFiles(APP_REF) local lid_index local lid_desc for lid_index, lid_desc in ipairs(LID_LIST) do - RavKav_readFile(APP_REF, lid_desc[1], lid_desc[2], lid_desc[3]) + RavKav_readFile(APP_REF, lid_desc[1], lid_desc[2], lid_desc[3], lid_desc[4], lid_desc[5], lid_desc[6]) end end @@ -1004,6 +1399,7 @@ function RavKav_parseRecords(APP_REF) RavKav_parseGeneralInfo(APP_REF) local cntrAry = RavKav_parseCounters(APP_REF) RavKav_parseEventsLog(APP_REF) + RavKav_parseSpecialEventsLog(APP_REF) RavKav_parseContracts(APP_REF, cntrAry) end @@ -1011,6 +1407,7 @@ function RavKav_summariseRecords(APP_REF, root) local SUM_REF = nodes.append(root, {classname="block", label="Summary"}) RavKav_summariseGeneralInfo(APP_REF, SUM_REF) RavKav_summariseLatestTravel(APP_REF, SUM_REF) + RavKav_summariseSpecialEvents(APP_REF, SUM_REF) RavKav_summariseContracts(APP_REF, SUM_REF) end @@ -1025,7 +1422,7 @@ function RavKav_parseAnswerToSelect(ctx) end +calypso_card_num() RavKav_parseAnswerToSelect(CARD) RavKav_parseRecords(CARD) -RavKav_summariseRecords(CARD, CARD) - +RavKav_summariseRecords(CARD, CARD) \ No newline at end of file diff --git a/dot_cardpeek_dir/scripts/e-passport.lua b/dot_cardpeek_dir/scripts/e-passport.lua index 108941e..289f0cd 100644 --- a/dot_cardpeek_dir/scripts/e-passport.lua +++ b/dot_cardpeek_dir/scripts/e-passport.lua @@ -27,6 +27,35 @@ local KS_ENC local KS_MAC local TEST=false +function epass_check_digit(s, n) + --The check digit calculation is as follows: each position is assigned a + --value; for the digits 0 to 9 this is the value of the digits, for the + --letters A to Z this is 10 to 35, for the filler < this is 0. The value of + --each position is then multiplied by its weight; the weight of the first + --position is 7, of the second it is 3, and of the third it is 1, and after + --that the weights repeat 7, 3, 1, and so on. All values are added together + --and the remainder of the final value divided by 10 is the check digit. + local c = string.sub(s, n) + local b = string.byte(c) + local weights = {7, 3, 1} + local weight = weights[(n-1) % 3 + 1] + if (c == "<") then return 0 end + if (b <= 58) then return (b - 48) * weight end + return (b - 65) * weight +end + +function epass_check_digits(s) + local i = #s + local cd = 0 + while i > 0 do + cd = cd + epass_check_digit(s, i) + --log.print(log.INFO,"Check digits after "..s.."["..i.."]: "..cd) + i = i - 1 + end + --log.print(log.INFO,"Check digits for "..s..": "..(cd%10)) + return cd % 10 +end + function epass_inc_SSC() local i=7 local e @@ -53,11 +82,7 @@ function epass_key_derivation(seed) return bytes.sub(Kenc,0,15),bytes.sub(Kmac,0,15) end -function epass_create_master_keys(mrz) - local pass_no = string.sub(mrz,1,10) - local pass_bd = string.sub(mrz,14,20) - local pass_ed = string.sub(mrz,22,28) - local mrz_info = pass_no..pass_bd..pass_ed; +function epass_create_master_keys(mrz_info) local hash local SHA1 @@ -225,6 +250,21 @@ function ui_parse_cstring(node,data) end end +function ui_parse_utf8(node,data) + nodes.set_attribute(node,"val",data) + + if #data==0 then + return false + end + + local alt = bytes.format(data,"%C") + if not bytes.is_printable(data) then + alt = bytes.format(data,"%C (%D)") + end + nodes.set_attribute(node,"alt",alt) + return true +end + BL_FAC_RECORD_HEADER = { { "Format Identifier", 4, ui_parse_cstring }, { "Format Version", 4, ui_parse_cstring }, @@ -511,15 +551,51 @@ function ui_parse_biometry(node,data) return true end +function ui_parse_jpeg(node,data) + nodes.set_attribute(node,"val", data) + if bytes.sub(data,1,2) == bytes.new(8,"D8 FF") then + nodes.set_attribute(node,"mime-type","image/jpeg") + end + return true +end + AID_MRTD = "#A0000002471001" MRP_REFERENCE = { ['5F01'] = { "LDS Version Number", ui_parse_version }, ['5F08'] = { "Date of birth (truncated)" }, + ['5F0E'] = { "Full name, in national characters", ui_parse_utf8 }, + ['5F0F'] = { "Other names", ui_parse_printable }, + ['5F10'] = { "Personal Number", ui_parse_printable }, + ['5F11'] = { "Place of birth", ui_parse_utf8 }, + ['5F12'] = { "Telephone", ui_parse_printable }, + ['5F13'] = { "Profession", ui_parse_printable }, + ['5F14'] = { "Title", ui_parse_printable }, + ['5F15'] = { "Personal Summary", ui_parse_printable }, + ['5F16'] = { "Proof of citizenship (10918 image)", ui_parse_jpeg }, + ['5F17'] = { "Other valid TD Numbers" }, + ['5F18'] = { "Custody information", ui_parse_printable }, + ['5F19'] = { "Issuing Authority", ui_parse_utf8 }, + ['5F1A'] = { "Other people on document" }, + ['5F1B'] = { "Endorsement/Observations", ui_parse_printable }, + ['5F1C'] = { "Tax/Exit requirements", ui_parse_printable }, + ['5F1D'] = { "Image of document front", ui_parse_jpeg }, + ['5F1E'] = { "Image of document rear", ui_parse_jpeg }, ['5F1F'] = { "MRZ data elements", ui_parse_printable }, + ['5F26'] = { "Date of Issue" }, + ['5F2B'] = { "Date of birth (8 digit)" }, ['5F2E'] = { "Biometric data block", ui_parse_biometry }, ['5F36'] = { "Unicode Version number", ui_parse_version }, + ['5F40'] = { "Compressed image template", ui_parse_jpeg }, + ['5F42'] = { "Address", ui_parse_printable }, + ['5F43'] = { "Compressed image template", ui_parse_jpeg }, + ['5F50'] = { "Date data recorded" }, + ['5F51'] = { "Name of person", ui_parse_utf8 }, + ['5F52'] = { "Telephone", ui_parse_printable }, + ['5F53'] = { "Address", ui_parse_printable }, + ['5F55'] = { "Date and time document personalized" }, + ['5F56'] = { "Serial number of personalization system" }, ['60'] = { "Common Data Elements" }, ['61'] = { "Template for MRZ Data Group" }, ['63'] = { "Template for finger biometric Data Group" }, @@ -576,17 +652,19 @@ FILES = { local MRZ_DATA="" -repeat - MRZ_DATA = ui.readline("Enter the code from the lower MRZ data (printed inside the passport)",44,MRZ_DATA) - if MRZ_DATA==nil then - break - end - if #MRZ_DATA<28 then - ui.question("You must enter at least 28 characters",{"OK"}) - end -until #MRZ_DATA>=28 +local pass_no = ui.readline("Enter document id/passport number",10,MRZ_DATA) +pass_no = string.upper(pass_no) +while (#pass_no < 9) do pass_no = pass_no .. "<" end +if (#pass_no == 9) then pass_no = pass_no .. epass_check_digits(pass_no) end +local pass_bd = ui.readline("Enter birth day YYMMDD",6,MRZ_DATA) +pass_bd = pass_bd .. epass_check_digits(pass_bd) +local pass_ed = ui.readline("Enter expiration date YYMMDD",6,MRZ_DATA) +pass_ed = pass_ed .. epass_check_digits(pass_ed) + +MRZ_DATA = pass_no..pass_bd..pass_ed +log.print(log.INFO,"MRZ data: " .. MRZ_DATA) -if MRZ_DATA then +if (#MRZ_DATA == 24) then if card.connect() then local ke,km,i,v,r @@ -637,6 +715,6 @@ if MRZ_DATA then ui.question("No passport detected in proximity of the reader",{"OK"}) end else - log.print(log.ERROR,"Aborted by the user.") + ui.question("Length of supplied data is incorrect.",{"OK"}) end diff --git a/dot_cardpeek_dir/scripts/emv.lua b/dot_cardpeek_dir/scripts/emv.lua index 93c168e..acd5f06 100644 --- a/dot_cardpeek_dir/scripts/emv.lua +++ b/dot_cardpeek_dir/scripts/emv.lua @@ -90,6 +90,19 @@ function card.get_data(data) return card.send(command) end +-- override card.verify() for EMV plaintext PIN +function card.verify(pin) + -- use 4 bit nibbles + local block = bytes.new(4, "00 20 00 80 08") + block = block .. 0x2 .. #pin .. bytes.new(4, pin) + while #block < 26 do + block = block .. 0xf + end + -- convert to 8 bit for APDU processing + local command = bytes.new(8, tostring(block)) + return card.send(command) +end + -- ------------------------------------------------------------------------ -- EMV @@ -313,13 +326,17 @@ CVM_REFERENCE_BYTE2 = { AIP_REFERENCE = { [1] = { -- Byte 1 [1] = "CDA supported", + [2] = "On device cardholder verification is supported", [3] = "Issuer authentication is supported", [4] = "Terminal risk management is to be performed", [5] = "Cardholder verification is supported", [6] = "DDA supported", - [7] = "SDA supported" + [7] = "SDA supported", + [8] = "RFU" }, [2] = { -- Byte 2 + [1] = "Relay resistance protocol is supported", + [8] = "EMV mode is supported" } } @@ -879,6 +896,44 @@ function emv_process_application(cardenv,aid) --extra = nodes.append(APP,{classname="block",label="extra emv data"}) for j=1,#EXTRA_DATA do sw,resp = card.get_data(EXTRA_DATA[j]) + -- if PIN retry count > 0 ask if we want to verify PIN + if EXTRA_DATA[j] == 0x9F17 and string.sub(tostring(resp),1,4) == "9F17" then + local d = tostring(resp) + local pin + local count = tonumber(string.sub(d, #d - 1, #d)) + while count ~= 0 and pin ~= "" do + query = "Verify PIN (" .. count .. " retries left)?" + if ui.question(query,{"Yes","No"})==1 then + log.print(log.INFO,"Attempting PIN verify") + -- must re-issue GPO to ensure we are auth'd + log.print(log.INFO,"Attempting GPO for PIN verify") + sw,resp = card.get_processing_options(pdol) + -- and again to clear error after failed PIN check + if sw == 0x6985 then + card.get_processing_options(pdol) + end + pin = ui.readline("Enter PIN for verification (or keep empty to avoid PIN verification)",8,"") + if pin ~= "" then + sw,resp = card.verify(pin) + if sw == 0x9000 then + ui.question("PIN Verified",{"OK"}) + count = 0 + elseif sw == 0x6983 or sw == 0x6984 then + ui.question("PIN Blocked",{"OK"}) + elseif bit.SHR(sw, 8) == 0x63 then + count= bit.AND(sw, 0x0F) + ui.question("Wrong PIN",{"OK"}) + elseif sw == 0x6985 then + ui.question("Conditions of use not satisfied!",{"OK"}) + else + ui.question("Unknown error! Check log for details",{"OK"}) + end + end + else + count = 0 + end + end + end if sw == 0x9000 then emv_parse(APP,resp):set_attribute("classname","block") end diff --git a/dot_cardpeek_dir/scripts/etc/ravkav-strings.lua b/dot_cardpeek_dir/scripts/etc/ravkav-strings.lua index a6fcfa9..e3fb017 100644 --- a/dot_cardpeek_dir/scripts/etc/ravkav-strings.lua +++ b/dot_cardpeek_dir/scripts/etc/ravkav-strings.lua @@ -1,26 +1,27 @@ RAVKAV_ISSUERS = { + [0] = "undefined", [1] = "Service Center (Postal Bank)", - [2] = "Israel Rail", + [2] = "Israel Railways", [3] = "Egged", [4] = "Egged Transport", [5] = "Dan", - [6] = "NTT (Nazereth Consolidated Bus Services)", - [7] = "NTT (Nazereth Travel & Tourism)", - [8] = "JB Tours", - [9] = "Omni (Nazrin Express)", - [10] = "Ayalot Regional Council", - [11] = "Elite", + [6] = "NTT (United Bus Services Nazareth)", + [7] = "NTT (Nazareth Travel & Tourism)", + [8] = "GB Tours", + [9] = "Omni Nazrin Express (transferred to Nativ Express)", + [10] = "Eilot Regional Council", + [11] = "Illit (merged with Kavim)", [12] = "undefined 12", [13] = "undefined 13", [14] = "Nativ Express", - [15] = "Metropolis", + [15] = "Metropoline", [16] = "Superbus", - [17] = "Connex & Veolia", + [17] = "Connex Veolia (transferred to Afikim)", [18] = "Kavim", - [19] = "Metrodan", + [19] = "Metrodan (defunct, transferred to Dan Be\'er Sheva)", [20] = "Carmelit", [21] = "CityPass", - [22] = "undefined 22", + [22] = "Tel Aviv Light Rail operator", --id is set, to be operated by Egged child company [23] = "Galim (Narkis Gal)", [24] = "Golan Regional Council", [25] = "Afikim", @@ -29,8 +30,8 @@ RAVKAV_ISSUERS = { [28] = "undefined 28", [29] = "undefined 29", [30] = "Dan North", - [31] = "undefined 31", - [32] = "undefined 32", + [31] = "Dan South", + [32] = "Dan Be\'er Sheva", [33] = "undefined 33", [34] = "undefined 34", [35] = "undefined 35", @@ -39,30 +40,79 @@ RAVKAV_ISSUERS = { [38] = "undefined 38", [39] = "undefined 39", [40] = "undefined 40", - [41] = "East Jerusalem operators" + [41] = "East Jerusalem operators", + [42] = "Jerusalem - Ramallah united", + [43] = "(obsolete) Jerusalem - Mahane Shuafat", + [44] = "Jerusalem - Anata united", + [45] = "Jerusalem - Isawiya united", + [46] = "(obsolete) Jabal Al Muhbar Ltd", + [47] = "Jerusalem - Mount of Olives", + [48] = "(obsolete) Abu-Tor Siluan bus company", + [49] = "Jerusalem - Isawiya - Mahane Shuafat united", + [50] = "South Jerusalem united", + [51] = "Jerusalem - Sur Baher united", + [52] = "(obsolete) Ras-al Amud Izriya - Abu-Dis", + [53] = "(obsolete) Jerusalem - A. Swahra united", + + [98] = "Tel Aviv Service Taxi Lines 4, 2 and 5", +-- 101-157 reserved + [240] = "[Technical] Service Center - Contract restores", + [250] = "Israel Defence Forces" +} + +RAVKAV_COMPANIES = { + [0] = "None", + [256] = "[256] - Israel Police", + [257] = "[257] - Prison Services", + [258] = "[258] - Israel Defence Forces", + [260] = "[260] - Extended student citizens of Eilat and Arava", + [261] = "[261] - Standard student citizens of Eilat and Arava", + [269] = "[269] - Parliament Guard", + [270] = "[270] - Identification by means of foreign passport", + [275] = "[275] - School for Special Education: Magen HaLev Ashdod", + [280] = "[280] - Companies partnered with Israel Railways", + [281] = "[281] - Companies partnered with Israel Railways", + [282] = "[282] - Companies partnered with Israel Railways", + [283] = "[283] - Companies partnered with Israel Railways", + [284] = "[284] - Companies partnered with Israel Railways", + [285] = "[285] - Companies partnered with Israel Railways", + [286] = "[286] - Companies partnered with Israel Railways", + [287] = "[287] - Companies partnered with Israel Railways", + [288] = "[288] - Companies partnered with Israel Railways", + [289] = "[289] - Companies partnered with Israel Railways", + [290] = "[290] - Companies partnered with Israel Railways", + [291] = "[291] - Companies partnered with Israel Railways", + [292] = "[292] - Companies partnered with Israel Railways", + [293] = "[293] - Companies partnered with Israel Railways", + [294] = "[294] - Companies partnered with Israel Railways", + [295] = "[295] - Companies partnered with Israel Railways", + [296] = "[296] - Companies partnered with Israel Railways", + [297] = "[297] - Companies partnered with Israel Railways", + [298] = "[298] - Companies partnered with Israel Railways", + [299] = "[299] - Companies partnered with Israel Railways" } RAVKAV_PROFILES = { --- [0] = "Standard", --- [1] = "Standard", - [2] = "2", + [0] = "Standard", + [1] = "Adult", + [2] = "Child", [3] = "Extended Student", [4] = "Senior Citizen", [5] = "Handicapped", - [6] = "Poor vision / blind", - [7] = "7", - [8] = "8", - [9] = "9", - [10] = "Ministry of Defence", - [11] = "11", - [12] = "12", - [13] = "Public Transport Works", - [14] = "14", - [15] = "15", - [16] = "16", - [17] = "17", - [18] = "18", - [19] = "Regular Student", + [6] = "Poor vision / Blind", + [7] = "Deaf / Dumb", + [8] = "Unemployed", + [9] = "Personal", + [10] = "Israel Defence Forces / Ministry of Defence / Security personnel", + [11] = "Resident", + [12] = "Transport", + [13] = "Public Transport Employee", + [14] = "Long-term transport", + [15] = "Local transport", + [16] = "Commuter", + [17] = "Animal", + [18] = "Object", + [19] = "Standard Student", [20] = "20", [21] = "21", [22] = "22", @@ -76,16 +126,33 @@ RAVKAV_PROFILES = { [30] = "30", [31] = "31", [32] = "Child aged 5-10", - [33] = "Youth", + [33] = "Youth aged 5-18", [34] = "National Service", - [35] = "Of \"takad\" zayin", + [35] = "Service without pay (SHALAT) / Pre-Army Course (KADATS)", [36] = "Israel Police", [37] = "Prison Services", [38] = "Member of Parliament", [39] = "Parliament Guard", [40] = "Eligible for Social Security", [41] = "Victim of Hostilities", - [42] = "New Immigrant in Rural Settlement" + [42] = "New Immigrant in Rural Settlement", + [43] = "Person Accompanying Poor vision / Blind", + [44] = "44", + [45] = "45", + [46] = "46", + [47] = "47", + [48] = "48", + [49] = "49", + [50] = "50", + [51] = "51", + [52] = "52", + [53] = "Residents of the South [Israel Railways]", + [54] = "Residents of the Valley [Israel Railways]", + [55] = "Residents of Ahihud [Israel Railways]", + [56] = "56", + [57] = "57", + [58] = "58", + [59] = "Youth Student on Intercity Ride" } --Cluster codes @@ -93,196 +160,470 @@ RAVKAV_PROFILES = { --[code] = description --issuerId RAVKAV_ROUTES = { --18: - [1] = "Ha'Emek", + [1] = "HaE\'mek", --5: - [11] = "Dan Region - Tel-Aviv", - [12] = "Dan Region - Ever Ha'Yarkon", - [13] = "Dan Region East - Bnei Brak", - [14] = "Dan Region East - Ramat Gan", - [15] = "Dan Region South - Bat Yam", - [16] = "Dan Region South - Rishon Le'Tziyon / Holon", - [17] = "Petach Tikva - Tel-Aviv", - [18] = "Students", - [19] = "Bat Yam - Ramat Gan", - --[40] = reserved --- + [11] = "Dan Region - Tel-Aviv", -- from July 1, 2012 + [12] = "(obsolete) Dan Region - Ever Ha\'Yarkon", -- from July 1, 2012 + [13] = "Dan Region East - Bnei Brak", -- from July 1, 2012 + [14] = "Dan Region East - Ramat Gan", -- from July 1, 2012 + [15] = "Dan Region South - Bat Yam", -- from July 1, 2012 + [16] = "Dan Region South - Rishon Le\'Tziyon / Holon", -- from July 1, 2012 + [17] = "Petah-Tikva - Tel-Aviv", -- from July 1, 2012 + [18] = "Youth students", -- from July 1, 2012 + [19] = "Bat Yam - Ramat Gan", -- from July 1, 2012 + [40] = "reserved", --2: - [50] = "Israel Rail primary", - [51] = "Israel Rail - Northern", - [52] = "Israel Rail - Central", - [53] = "Israel Rail - Southern", - --[54] = reserved --- - --[55] = reserved --- + [50] = "Israel Railways - Primary", + [51] = "Israel Railways - Northern", + [52] = "Israel Railways - Central", + [53] = "Israel Railways - Southern", + [54] = "Israel Railways - Palesht", + [55] = "reserved", --3: - [71] = "Holon urban + metropolis + free", - [72] = "Rishon Le'Tziyon suburban", - [73] = "Rishon Le'Tziyon urban", - [74] = "Rechovot urban", - [75] = "Rechovot suburban", - [76] = "Tel-Aviv - Ashkalon", + [71] = "Holon urban + metropolis + competition", + [72] = "Rishon Le\'Tziyon suburban", + [73] = "Rishon Le\'Tziyon urban", + [74] = "Rehovot urban", + [75] = "Rehovot suburban", + [76] = "Tel-Aviv - Ashkelon", [77] = "Tel-Aviv - Galilee - Amekim", - [79] = "Ashdod Students", + [79] = "Ashdod youth students", [81] = "Haifa urban", [83] = "Haifa suburban", [84] = "Kiryat Shmona - Haifa", - [85] = "Matm'z Karyut", + [85] = "From CBS Krayot", [86] = "Hadera suburban", [87] = "Kiryat Shmona urban", - [89] = "Carmiel urban + Carmiel, Haifa, Tiberias", + [89] = "Carmiel urban, Carmiel - Haifa, Haifa - Tiberias", [91] = "Jerusalem urban", [93] = "Jerusalem - Beit Shemesh", [94] = "Jerusalem - Tel-Aviv", [95] = "Jerusalem - Bnei Brak (402)", - [96] = "Jerusalem - Ha'Shfela", - [97] = "Jerusalem North Axis East", - [98] = "Ashdod - Ashkalon - Jerusalem", + [96] = "Jerusalem - Ha\'Shfela", + [97] = "Jerusalem North - east axis", + [98] = "Ashdod - Ashkelon - Jerusalem", [101] = "Eilat urban and intercity", [102] = "Southern cluster", - [103] = "Haifa - Ha'Sharon - Jerusalem", - [104] = "Jerusalem - Be'er Sheva", + [103] = "Haifa - Ha\'Sharon - Jerusalem", + [104] = "Jerusalem - Be\'er Sheva", [105] = "Jerusalem - Bnei Brak (400)", [106] = "Ashdod - Tel-Aviv (competition)", [107] = "Nahariya - Haifa (competition)", - [108] = "Shfar'am Villages (competition)", - [109] = "Sefad (competition)", - [110] = "Tel-Aviv - Ha'Sharon - Haifa", + [108] = "Shefa\'Amr Villages (competition)", + [109] = "Safed (competition)", + [110] = "Tel-Aviv - HaSharon - Haifa", [111] = "Tel-Aviv - Hadera", - [112] = "Haredi sector (competition)", + [112] = "Haredi lines (competition)", [113] = "Jerusalem suburban", [114] = "Jerusalem protected", - [115] = "Jerusalem - Haredi sector", + [115] = "Jerusalem - Haredi lines", [116] = "Netanya - Hadera (competition)", + [117] = "Sharon - Holon spatial", --3, 15 [118] = "Haifa - Jerusalem - Eilat", - [121] = "Nazareth lines - JB Tours", --8 - [122] = "Nazareth lines - Travel and Tourism", --7 - [123] = "Nazareth Lines - NTT", --6 - [124] = "Nahariya - Sefad", --14 - [125] = "Nazareth-Haifa shared", --7 - [126] = "Nazareth-Haifa shared", --8 - [127] = "Yokneam - Tivon", --9 + [120] = "HaAmakim", --16 + [121] = "(obsolete) Nazareth lines - GB Tours", --8 + [122] = "Nazareth lines - NTT (Nazareth Travel & Tourism)",--7 + [123] = "Nazareth lines - NTT (United Bus Services Nazareth)",--6 + [124] = "Nahariya - Safed", --14 + [125] = "(obsolete) Nazareth-Haifa shared", --7 + [126] = "(obsolete) Nazareth-Haifa shared", --8 + [127] = "Yokneam - Tiv\'on", --9, 14 (merge of Omni with Nativ Express) + [128] = "Galilee", --14 + [129] = "Jerusalem - Yitshak Navon station", --? [130] = "Golan Heights", --24 - [132] = "Nazareth area - JB Tours", --8 - [141] = "Ha'Negev North", --4 - [142] = "Ayalot", --10 + [132] = "Nazareth area - GB Tours", --8 + [140] = "Southern cluster - Negev", --15 + [141] = "Northern Negev", --31 + [142] = "Eilot", --10 [143] = "Rahat", --23 + [150] = "Hashmonaim", --18 [151] = "Elad", --4 - [152] = "Beitar Illit", --18 - [153] = "Beitar Illit", --11 - [154] = "Modi'in", --17 - [155] = "Hadera - Netanya", --14 - [157] = "Modi'in Illit", --16 - [158] = "Yavne - Ashdod - Tel-Aviv", --17 - [160] = "Netanya - Tel-Aviv", --14 - [169] = "Lod - Tel-Aviv", --17 + [152] = "Beitar Illit", --11, 18 (since July 1, 2012, merge of Illit with Kavim) + [153] = "Beitar Illit", --11, 18 (since July 1, 2012, merge of Illit with Kavim) + [154] = "Modi\'in", --17 (defunct since July 2013) + [155] = "Hadera - Netanya", --18 + [156] = "Hadera - Netanya (reserved, service cluster)",--18 + [157] = "(obsolete) Modi\'in Illit", --16 + [158] = "Ashdod - Yavne - Tel-Aviv", --25 + [159] = "Beit Shemesh", --16 + [160] = "(obsolete) Netanya - Tel-Aviv", --14 + [169] = "Lod - Tel-Aviv", --17 (defuncct since July 2013) [170] = "Peruzdor Jerusalem", --16 - [171] = "Road 4 - Jerusalem - Bnei Brak", --17 - [176] = "Jerusalem perimeter", --4 + [171] = "Hwy 4 - Jerusalem - Bnei Brak", --25 + [176] = "Jerusalem outskirts", --4 + [177] = "Shapirim - Jerusalem", --4 + [178] = "Jerusalem outskirts protected", --4 [180] = "East Jerusalem", --41 + [181] = "Jerusalem - Beit Jala", --50 + [182] = "Ramallah", --42 + [183] = "Klendia - Beit Hanina", --42 + [184] = "Abu Tor", --44, 48 + [185] = "Jabel Muhbar - Al Azariya", --46 + [186] = "Jerusalem - Mount of Olives", --47 + [187] = "Isawiya - Shuafat", --49 + [188] = "Beit Tsfafa - Beit Lehem", --50 + [189] = "Sur Baher", --51 [190] = "Samaria", --25 - [195] = "Ha'Sharon", --15 - [200] = "Dan Region", --5 - [201] = "Be'er Sheva urban", --19 - [202] = "Tiberias regional", --17 - [203] = "Ramla - Lod", --16 - [204] = "Ashdod urban", --4 + [195] = "Ha\'Sharon", --15 + [200] = "(obsolete) Dan Region", --5 + [201] = "Be\'er Sheva urban", --32 + [202] = "Tiberias regional", --25 + [203] = "Ramla - Lod", --16 (defunct since end of 2013) + [204] = "Ashdod urban", --25 [205] = "Netanya urban", --4 [206] = "Fast Lane Shuttle", --5 - [208] = "Ono - Petach Tikva", --18 + [207] = "Petah-Tikva - Rosh Ha\'Ayin", --25 + [208] = "Ono - Petah-Tikva", --18 + [209] = "Ono - Elad", --18 [210] = "Jerusalem Light Rail", --21 [214] = "Carmelit", --20 [215] = "Metronit Haifa", --30 [218] = "Fast line Tel-Aviv (BRT)", --5 [220] = "Tel-Aviv Light Rail", --? - [221] = "Beer Sheva direct - Tel-Aviv", --4 - [222] = "Beer Sheva - Tel-Aviv - Negev (including Arad, Yeruham, Mitzpe Ramon and Nitzana)" --15 + [221] = "Direct Be\'er Sheva - Tel-Aviv", --? (defunct) + [222] = "Be\'er Sheva - Tel-Aviv - Negev (including Arad, Yeruham, Mitzpe Ramon and Nitzana)", --15 + +--Sherut Taxis + [249] = "Sherut Taxis", +--IDF + [250] = "IDF", +--Service Center + [251] = "Technical code for contract restores" --240 +} + +-- Commented are the station IDs used in the Israel Railways computer systems +-- However the IDs used by RavKav are not the same that are used in that system +RAVKAV_RAIL_LOCATIONS = { +-- 2: Israel Railways: + [1] = "[Ticketing Machine] Tel Aviv Center - Savidor", --3700 + [2] = "[Ticketing Machine] Herzliya", --3500 + [3] = "[Ticketing Machine] Beit Yehoshua", --3400 + [4] = "[Ticketing Machine] Netanya", --3300 + [5] = "[Ticketing Machine] Hadera West", --3100 + [6] = "[Ticketing Machine] Binyamina", --2800 + [7] = "[Ticketing Machine] Ceasarea - Pardes Hana", --2820 + [8] = "[Ticketing Machine] Atlit", --2500 + [9] = "[Ticketing Machine] Haifa Bat Galim", --2200 + [10] = "[Ticketing Machine] Haifa - Hutsot HaMifrats", --1300 + [11] = "[Ticketing Machine] Kiryat Hayim", --700 + [12] = "[Ticketing Machine] Kiryat Motzkin", --800 + [13] = "[Ticketing Machine] Akko", --1500 + [14] = "[Ticketing Machine] Haifa Hof HaKarmel (Razi\'el)", --2300 + [15] = "[Ticketing Machine] Kfar Saba - Nordau (A. Kostyuk)",--8700 + [16] = "[Ticketing Machine] Nahariya", --1600 + [17] = "[Ticketing Machine] Jerusalem Biblical Zoo", --6500 + [18] = "[Ticketing Machine] Beit Shemesh", --6300 + [19] = "[Ticketing Machine] Kiryat Gat", --7000 + [20] = "[Ticketing Machine] Lod", --5000 + [21] = "[Ticketing Machine] Be\'er Sheva North / University",--7300 + [22] = "[Ticketing Machine] Kfar Habbad", --4800 + [23] = "[Ticketing Machine] Tel Aviv HaShalom", --4600 + [24] = "[Ticketing Machine] Haifa East", -- + [25] = "[Ticketing Machine] Haifa Center HaShmona", --2100 + [26] = "[Ticketing Machine] Ramla", --5010 + [27] = "[Ticketing Machine] Rosh Ha\'Ayin North", --8800 + [28] = "[Ticketing Machine] Be\'er Ya\'akov", --5300 + [29] = "[Ticketing Machine] Rehovot E. Hadar", --5200 + [30] = "[Ticketing Machine] Yavne East", --5410 + [31] = "[Ticketing Machine] Rishon Le\'Tziyon - HaRishonim", --9100 + [32] = "[Ticketing Machine] Ashdod Ad Halom (M. Bar Kochva)",--5800 + [33] = "[Ticketing Machine] Rosh Ha\'Ayin South", -- + [34] = "[Ticketing Machine] Petah-Tikva - Sgula", --4250 + [35] = "[Ticketing Machine] Bnei Brak", --4100 + [36] = "[Ticketing Machine] Tel Aviv - University", --3600 + [37] = "[Ticketing Machine] Be\'er Sheva Center", --7320 + [38] = "[Ticketing Machine] Haifa - HaMifrats Central", --1220 + [39] = "[Ticketing Machine] Tel Aviv HaHagana", --4900 + [40] = "[Ticketing Machine] Ben Gurion Airport", --8600 + [41] = "[Ticketing Machine] Jerusalem Malha", --6700 + [42] = "[Ticketing Machine] Ashkelon", --5900 + [43] = "[Ticketing Machine] Dimona", --7500 + [44] = "[Ticketing Machine] Hod HaSharon - Sokolov", --9200 + [45] = "[Ticketing Machine] Petah-Tikva - Kiryat Arye", --4170 + [46] = "[Ticketing Machine] Lod Ganey Aviv", --5150 + [47] = "[Ticketing Machine] Lehavim - Rahat", --8550 + [48] = "[Ticketing Machine] Modi\'in - Pa\'atei Modi\'in", --300 + [49] = "[Ticketing Machine] Modi\'in Center", --400 + [50] = "[Ticketing Machine] Holon Junction", --4640 + [51] = "[Ticketing Machine] Holon - Wolfson", --4660 + [52] = "[Ticketing Machine] Bat Yam - Yoseftal", --4680 + [53] = "[Ticketing Machine] Bat Yam - Komemiyyut", --4690 + [54] = "[Ticketing Machine] Rishon Le\'Tziyon - Moshe Dayan",--9800 + [55] = "[Ticketing Machine] Yavne West", --9000 + [56] = "[Ticketing Machine] Sderot", --9600 + [57] = "[Ticketing Machine] Netivot", --9650 + [58] = "[Ticketing Machine] Ofakim", --9700 + [59] = "[Ticketing Machine] Netanya Sapir", --3310 + [60] = "[Ticketing Machine] Yokneam - Kfar Yehoshua", --1240 + [61] = "[Ticketing Machine] Migdal HaEmek - Kfar Barukh", --1250 + [62] = "[Ticketing Machine] Afula", --1260 + [63] = "[Ticketing Machine] Beit She\'an", --1280 + [64] = "[Ticketing Machine] Ahihud", --1820 + [65] = "[Ticketing Machine] Carmiel", --1840 + [66] = "[Ticketing Machine] Ra\'anana West", --2940 + [67] = "[Ticketing Machine] Ra\'anana South", --2960 + [68] = "[Ticketing Machine] Kiryat Malakhi - Yoav", --6150 + [69] = "[Ticketing Machine] Jerusalem - Yitshak Navon", --680 + [70] = "[Ticketing Machine] Mazkeret Batya", --6900 + + [7001] = "Tel Aviv Center - Savidor", --3700 + [7002] = "Herzliya", --3500 + [7003] = "Beit Yehoshua", --3400 + [7004] = "Netanya", --3300 + [7005] = "Hadera West", --3100 + [7006] = "Binyamina", --2800 + [7007] = "Ceasarea - Pardes Hana", --2820 + [7008] = "Atlit", --2500 + [7009] = "Haifa Bat Galim", --2200 + [7010] = "Haifa - Hutsot HaMifrats", --1300 + [7011] = "Kiryat Hayim", --700 + [7012] = "Kiryat Motzkin", --800 + [7013] = "Akko", --1500 + [7014] = "Haifa Hof HaKarmel (Razi\'el)", --2300 + [7015] = "Kfar Saba - Nordau (A. Kostyuk)", --8700 + [7016] = "Nahariya", --1600 + [7017] = "Jerusalem Biblical Zoo", --6500 + [7018] = "Beit Shemesh", --6300 + [7019] = "Kiryat Gat", --7000 + [7020] = "Lod", --5000 + [7021] = "Be\'er Sheva North / University", --7300 + [7022] = "Kfar Habbad", --4800 + [7023] = "Tel Aviv HaShalom", --4600 + [7024] = "Haifa East", -- + [7025] = "Haifa Center HaShmona", --2100 + [7026] = "Ramla", --5010 + [7027] = "Rosh Ha\'Ayin North", --8800 + [7028] = "Be\'er Ya\'akov", --5300 + [7029] = "Rehovot E. Hadar", --5200 + [7030] = "Yavne East", --5410 + [7031] = "Rishon Le\'Tziyon - HaRishonim", --9100 + [7032] = "Ashdod Ad Halom (M. Bar Kochva)", --5800 + [7033] = "Rosh Ha\'Ayin South", -- + [7034] = "Petah-Tikva - Sgula", --4250 + [7035] = "Bnei Brak", --4100 + [7036] = "Tel Aviv - University", --3600 + [7037] = "Be\'er Sheva Center", --7320 + [7038] = "Haifa - HaMifrats Central", --1220 + [7039] = "Tel Aviv HaHagana", --4900 + [7040] = "Ben Gurion Airport", --8600 + [7041] = "Jerusalem Malha", --6700 + [7042] = "Ashkelon", --5900 + [7043] = "Dimona", --7500 + [7044] = "Hod HaSharon - Sokolov", --9200 + [7045] = "Petah-Tikva - Kiryat Arye", --4170 + [7046] = "Lod Ganey Aviv", --5150 + [7047] = "Lehavim - Rahat", --8550 + [7048] = "Modi\'in - Pa\'atei Modi\'in", --300 + [7049] = "Modi\'in Center", --400 + [7050] = "Holon Junction", --4640 + [7051] = "Holon - Wolfson", --4660 + [7052] = "Bat Yam - Yoseftal", --4680 + [7053] = "Bat Yam - Komemiyyut", --4690 + [7054] = "Rishon Le\'Tziyon - Moshe Dayan", --9800 + [7055] = "Yavne West", --9000 + [7056] = "Sderot", --9600 + [7057] = "Netivot", --9650 + [7058] = "Ofakim", --9700 + [7059] = "Netanya Sapir", --3310 + [7060] = "Yokneam - Kfar Yehoshua", --1240 + [7061] = "Migdal HaEmek - Kfar Barukh", --1250 + [7062] = "Afula", --1260 + [7063] = "Beit She\'an", --1280 + [7064] = "Ahihud", --1820 + [7065] = "Carmiel", --1840 + [7066] = "Ra\'anana West", --2940 + [7067] = "Ra\'anana South", --2960 + [7068] = "Kiryat Malakhi - Yoav", --6150 + [7069] = "Jerusalem - Yitshak Navon", --680 + [7070] = "Mazkeret Batya" --6900 + + -- [7071] = "7071" -- +} + +RAVKAV_RAIL_LOCATIONS2 = { +-- 2: Israel Railways: + [1] = "Tel Aviv Center - Savidor", --3700 + [2] = "Herzliya", --3500 + [3] = "Beit Yehoshua", --3400 + [4] = "Netanya", --3300 + [5] = "Hadera West", --3100 + [6] = "Binyamina", --2800 + [7] = "Ceasarea - Pardes Hana", --2820 + [8] = "Atlit", --2500 + [9] = "Haifa Bat Galim", --2200 + [10] = "Haifa - Hutsot HaMifrats", --1300 + [11] = "Kiryat Hayim", --700 + [12] = "Kiryat Motzkin", --800 + [13] = "Akko", --1500 + [14] = "Haifa Hof HaKarmel (Razi\'el)", --2300 + [15] = "Kfar Saba - Nordau (A. Kostyuk)", --8700 + [16] = "Nahariya", --1600 + [17] = "Jerusalem Biblical Zoo", --6500 + [18] = "Beit Shemesh", --6300 + [19] = "Kiryat Gat", --7000 + [20] = "Lod", --5000 + [21] = "Be\'er Sheva North / University", --7300 + [22] = "Kfar Habbad", --4800 + [23] = "Tel Aviv HaShalom", --4600 + [24] = "Haifa East", -- + [25] = "Haifa Center HaShmona", --2100 + [26] = "Ramla", --5010 + [27] = "Rosh Ha\'Ayin North", --8800 + [28] = "Be\'er Ya\'akov", --5300 + [29] = "Rehovot E. Hadar", --5200 + [30] = "Yavne East", --5410 + [31] = "Rishon Le\'Tziyon - HaRishonim", --9100 + [32] = "Ashdod Ad Halom (M. Bar Kochva)", --5800 + [33] = "Rosh Ha\'Ayin South", -- + [34] = "Petah-Tikva - Sgula", --4250 + [35] = "Bnei Brak", --4100 + [36] = "Tel Aviv - University", --3600 + [37] = "Be\'er Sheva Center", --7320 + [38] = "Haifa - HaMifrats Central", --1220 + [39] = "Tel Aviv HaHagana", --4900 + [40] = "Ben Gurion Airport", --8600 + [41] = "Jerusalem Malha", --6700 + [42] = "Ashkelon", --5900 + [43] = "Dimona", --7500 + [44] = "Hod HaSharon - Sokolov", --9200 + [45] = "Petah-Tikva - Kiryat Arye", --4170 + [46] = "Lod Ganey Aviv", --5150 + [47] = "Lehavim - Rahat", --8550 + [48] = "Modi\'in - Pa\'atei Modi\'in", --300 + [49] = "Modi\'in Center", --400 + [50] = "Holon Junction", --4640 + [51] = "Holon - Wolfson", --4660 + [52] = "Bat Yam - Yoseftal", --4680 + [53] = "Bat Yam - Komemiyyut", --4690 + [54] = "Rishon Le\'Tziyon - Moshe Dayan", --9800 + [55] = "Yavne West", --9000 + [56] = "Sderot", --9600 + [57] = "Netivot", --9650 + [58] = "Ofakim", --9700 + [59] = "Netanya Sapir", --3310 + [60] = "Yokneam - Kfar Yehoshua", --1240 + [61] = "Migdal HaEmek - Kfar Barukh", --1250 + [62] = "Afula", --1260 + [63] = "Beit She\'an", --1280 + [64] = "Ahihud", --1820 + [65] = "Carmiel", --1840 + [66] = "Ra\'anana West", --2940 + [67] = "Ra\'anana South", --2960 + [68] = "Kiryat Malakhi - Yoav", --6150 + [69] = "Jerusalem - Yitshak Navon", --680 + [70] = "Mazkeret Batya" --6900 +} + +--Bus stops +RAVKAV_BUS_LOCATIONS = { + --adding full stop list here statically, causes + --ERROR backtrace: called at [C] + --ERROR backtrace: called from require() + --ERROR backtrace: called at calypso/c376.lua + --ERROR backtrace: called from dofile() + --ERROR backtrace: called from calypso_process() in [string "calypso.lua"][255] + --ERROR backtrace: called at [string "calypso.lua"] + + --require('etc.ravkav-strings') halts when the file is too big (3.5 MB) + + --for now use GTFS stops.txt + + --[stop_code] = "[stop_code] stop_name / stop_desc" } --- These are the station names from the Israel Rail website. --- However the IDs on the website do NOT match those used by RavKav and are incorrect, --- except for the 3 that have been updated below (with original ID added as comment) -RAVKAV_LOCATIONS = { - [300] = "Modi'in - Pa'ate Modi'in", - [7049] = "Modi'in Center", --400 - [700] = "Kiryat Hayyim", - [800] = "Kiryat Motzkin", - [1220] = "Lev HaMifrats", - [1300] = "Hutsot HaMifrats", - [1500] = "Akko", - [1600] = "Nahariyya", - [2100] = "Haifa Center HaShmona", - [2200] = "Haifa Bat Gallim", - [2300] = "Haifa Hof HaKarmel (Razi'el)", - [2500] = "Atlit", - [2800] = "Binyamina", - [2820] = "Ceasarea - Pardes Hanna", - [3100] = "Hadera West", - [3300] = "Natanya", - [3400] = "Bet Yehoshua", - [7002] = "Herzliya", --3500 - [3600] = "Tel Aviv - University", - [3700] = "Tel Aviv Center - Savidor", - [4100] = "Bnei Brak", - [4170] = "Petah Tikva Kiryat Arye", - [4250] = "Petah Tikva Sgulla", - [7023] = "Tel Aviv HaShalom", --4600 - [4640] = "Holon Junction", - [4660] = "Holon - Wolfson", - [4680] = "Bat Yam - Yoseftal", - [4690] = "Bat Yam - Komemiyyut", - [4800] = "Kfar Habbad", - [4900] = "Tel Aviv HaHagana", - [5000] = "Lod", - [5010] = "Ramla", - [5150] = "Lod Ganey Aviv", - [5200] = "Rehovot E. Hadar", - [5300] = "Be'er Ya'akov", - [5410] = "Yavne", - [5800] = "Ashdod Ad Halom (M.Bar Kochva)", - [5900] = "Ashkelon", - [6300] = "Bet Shemesh", - [6500] = "Jerusalem Biblical Zoo", - [6700] = "Jerusalem Malha", - [7000] = "Kiryat Gat", - [7300] = "Be'er Sheva North University", - [7320] = "Be'er Sheva Center", - [7500] = "Dimona", - [8550] = "Lehavim - Rahat", - [8600] = "Ben Gurion Airport", - [8700] = "Kefar Sava - Nordau (A.Kostyuk)", - [8800] = "Rosh Ha'Ayin North", - [9000] = "Yavne West", - [9100] = "Rishon LeTsiyyon HaRishonim", - [9200] = "Hod HaSharon - Sokolov", - [9800] = "Rishon LeTsiyyon - Moshe Dayan" +RAVKAV_LINE_TYPES = { + [0] = "Not set or generic public transport", + [1] = "Urban", + [2] = "Regional", + [3] = "Intercity", + [4] = "Light Rail", + [5] = "Intercity Rail", + [15] = "Transport method" } RAVKAV_EVENT_TYPES = { + [0] = "Action -", [1] = "Entry -", --Used for [2] = "Exit -", + [3] = "Transit -", + [4] = "Prevalidate -", + [5] = "Inspection -", [6] = "Transit trip", - [9] = "Cancel -", - [12] = "Loaded card and used immediately for", - [13] = "Loaded", - [14] = "Personalization" + [7] = "Exit from station -", + [8] = "Other usage -", + [9] = "Contract cancel -", + [12] = "Contract load and used immediately for -", + [13] = "Contract load -", + [14] = "Personalization / Card issuance", + [15] = "Card invalidation" +} + +RAVKAV_LOAD_EVENTS = { + [0] = "Contract load failure", + [10] = "Card is full", + [11] = "Card is inactive", + [12] = "Better contract for passenger profile exists", + [13] = "Contract does not match passenger profile", + [14] = "Max amount of aggregate value reached", + [15] = "Contract invalid, please contact support", + [16] = "Card is faulty", + [17] = { [0] = "Conflicting contract found on card" + }, + [18] = "Passenger profile is invalid. Please contact support", + [20] = "Contract load action can not be cancelled. Load cancellation requirements not met", + [21] = "Contract load action can not be cancelled. Load cancellation requirements not met", + [22] = "Contract load action can not be cancelled. Contract was used after loading", + [23] = "Passenger profile load action can not be cancelled. Passenger profile was used after loading", + [30] = "The card is not the same card scanned at the beginning of the operation", + [40] = "Card unsupported", + [41] = "Connection to card abnormally disconnected", + [43] = "Incomplete operation detected. Please wait several minutes and try again", + [44] = "Request timed out. Please retry the operation", + [50] = "Service temporarily unavailable. Please try again later", + [70] = "You moved the card too fast", + [71] = "Cannot create conection to card", + [72] = "You moved the card too fast", + [100] = "Not possible to load more than two special passenger profiles to card. Valid special passenger profile exist, that needs to be cancelled first to be able to load more profiles", + [101] = "Selected passenger profile can not be loaded. You are not eligible for the selected passenger profile", + [102] = "Card is not prepersonalized. Prepersonalize the card at a service point", + [103] = "Unsupported card. Please contact support", + [104] = "Unsupported contract. The card contains a contract that is no longer supported. Please contact support", + [105] = "Contract validity time will be longer than passenger profile or card validity. Please issue a new card at a service point or renew the passenger profile", + [106] = "Passenger profile will be longer than card validity. Please issue a new card with longer validity time at a service point", + [800] = "Can not convert stored value. No convertable stored value found", + [801] = "Can not convert stored value. A better eligible passenger profile exist than the selected profile", + [1011] = "Unable to connect to the server. Please check your network connection, firewall or content blocking service settings and refresh the page", + [1012] = "Connection blocked by the user terminal. Please check your firewall or content blocking service settings and refresh the page", + [1013] = "Connection abnormally disconnected. Please check your network connection", + [1014] = "App version is too old. Please update the app and try again", + [1060] = "You moved the card too fast", + [1061] = "You moved the card too fast", + [1062] = "Card unsupported", + [1070] = "Connection abnormally disconnected. Please check your internet connection" } +--ETT_A RAVKAV_TICKET_TYPES = { - [1] = "Single or multiple", - [2] = "Season pass", --free period + [1] = "Amount of rides", + [2] = "Periodic", --unlimited travel [3] = "3", [4] = "Free of charge", [5] = "5", [6] = "Aggregate value", - [7] = "Single or multiple", - [8] = "8" + [7] = "Amount of rides", + [8] = "8", + [9] = "Paper ticket" --Paper ticket only, reserved for reporting purposes to Service Center } RAVKAV_VALIDITY_TYPES = { [0] = "Area", [1] = "Tariff", [2] = "2", - [3] = "3", + [3] = "Rail origin-destination", [4] = "4", + [5] = "5", + [6] = "6", [7] = "Rail tariff 1", [8] = "Rail tariff 2", [9] = "Predefined", @@ -294,265 +635,898 @@ RAVKAV_VALIDITY_TYPES = { [15] = "End of validity locations" } --- ticketType, ETT +--TODO: +RAVKAV_INTERCHANGE_RIGHTS = { +---- validityBitmap 2 +---- restrictionCode +---- when 1 == right to interchange until eventTime + iDuration (validityBitmap 4) (Restriction duration) in RAVKAV_INTERCHANGE_AREAS (locationBitmap 0x40 interchangesAreaId) +---- when 2 == right to continue until eventTime + iDuration == 360 (6 hours) (Restriction duration), iTrips remaining + [1] = "Right to do transit trip until {eventTime + iDuration} in area {interchangesAreaId}", + [2] = "Right to continue trip until {eventTime + iDuration == 360 (6 hours)}, {iTrips} remaining" +} + +RAVKAV_INTERCHANGE_AREAS = { + [0] = "Area 0", + [1] = "Area 1 - Intercity and most areas", + [2] = "Area 2 - Bnei Brak", + [3] = "Area 3", + [4] = "Area 4", + [5] = "Area 5", + [6] = "Area 6", + [7] = "Area 7", + [8] = "Area 8", + [9] = "Area 9", + [10] = "Area 10", + [11] = "Area 11", + [12] = "Area 12", + [13] = "Area 13", + [14] = "Area 14", + [15] = "Area 15", + [16] = "Area 16", + [17] = "Area 17", + [18] = "Area 18", + [19] = "Area 19", + [20] = "Area 20", + [21] = "Area 21", + [22] = "Area 22", + [23] = "Area 23", + [24] = "Area 24", + [25] = "Area 25", + [26] = "Area 26", + [27] = "Area 27", + [28] = "Area 28", + [29] = "Area 29", + [30] = "Area 30", + [31] = "Area 31 - Jerusalem - Area 1 (city center + suburbs (Maale Adumim, Mevaseret Zion, Giv\'at Ze\'ev, etc.))", + [32] = "Area 32", + [33] = "Area 33 - Jerusalem - Area 3 (Beit Shemesh, Peruzdor Jerusalem)", + [34] = "Area 34 - Jerusalem - Area 4 (Mod\'in, external ring)", + [35] = "Area 35", + [36] = "(obsolete) Area 36 - Maale Adumim (part of Area 31)", + [37] = "(obsolete) Area 37 - Mevaseret Zion (part of Area 31)", + [38] = "(obsolete) Area 38 - Giv\'at Ze\'ev (part of Area 31)", + [39] = "Area 39", + [40] = "Area 40", + [41] = "Area 41 - Netanya - Tayibe", + [42] = "Area 42 - Dan Region - Ring 1 (Tel Aviv-Yafo, Ramat Gan, Giv\'atayim, Bnei Brak, Petah-Tikva, Bat Yam, Holon, Rishon Le\'Tziyon)", + [43] = "Area 43 - Dan Region - Area 21 (Sharon, part of Ring 2)", + [44] = "Area 44 - Ashdod - Gedera", + [45] = "Area 45 - Dan Region - Petah-Tikva (part of Ring 1)", + [46] = "Area 46 - Dan Region - Area 22 (part of Ring 2 (Rosh-Ha-Ayin)", + [47] = "Area 47 - Hashmonaim", + [48] = "Area 48 - Modi\'in", + [49] = "Area 49 - Highway 5", + [50] = "Area 50", + [51] = "Area 51 - Haifa - Center (including Isfiya and Dalit-al-Karmel)", + [52] = "(obsolete) Area 52 - Krayot", + [53] = "Area 53 - Yokneam - Tiv\'on", + [54] = "(obsolete) Area 54 - Rechasim", + [55] = "(obsolete) Area 55 - Atlit", + [56] = "Area 56 - HaMifrats CBS", + [57] = "Area 57 - Hof Ha-Karmel CBS", + [58] = "Area 58 - Nesher", + [59] = "Area 59 - Tirat Ha-Karmel", + [60] = "Area 60 - Akko", + [61] = "Area 61 - Nahariya", + [62] = "Area 62 - Carmiel", + [63] = "Area 63 - Nazareth", + [64] = "Area 64 - Zichron Ya\'akov", + [65] = "Area 65", + [66] = "Area 66", + [67] = "Area 67", + [68] = "Area 68", + [69] = "Area 69", + [70] = "Area 70", + [71] = "Area 71 - Be\'er Sheva Center", + [72] = "Area 72 - Lehavim - Rahat", + [73] = "Area 73 - Ofakim - Hatzerim", + [74] = "Area 74 - Meitar - Omer", + [75] = "Area 75 - Sderot - Sha\'ar HaNegev Regional Council", + [76] = "Area 76 - Yeruham - Ramat Negev Regional Council", + [77] = "Area 77 - Dimona - Arad", + [78] = "Area 78 - South Mount Hevron", + [79] = "Area 79", + [80] = "Area 80", + [81] = "Area 81", + [82] = "Area 82", + [83] = "Area 83", + [84] = "Area 84", + [85] = "Area 85", + [86] = "Area 86", + [87] = "Area 87", + [88] = "Area 88", + [89] = "Area 89", + [90] = "Area 90", + [91] = "Area 91", + [92] = "Area 92", + [93] = "Area 93", + [94] = "Area 94", + [95] = "Area 95", + [96] = "Area 96", + [97] = "Area 97", + [98] = "Area 98", + [99] = "Area 99", + [100] = "Area 100", + [101] = "Area 101", + [102] = "Area 102", + [103] = "Area 103", + [104] = "Area 104", + [105] = "Area 105", + [106] = "Area 106", + [107] = "Area 107", + [108] = "Area 108", + [109] = "Area 109", + [110] = "Area 110", + [111] = "Area 111", + [112] = "Area 112", + [113] = "Area 113", + [114] = "Area 114", + [115] = "Area 115", + [116] = "Area 116", + [117] = "Area 117", + [118] = "Area 118", + [119] = "Area 119", + [120] = "Area 120", + [121] = "Area 121", + [122] = "Area 122", + [123] = "Area 123", + [124] = "Area 124", + [125] = "Area 125", + [126] = "Area 126", + [127] = "Area 127", + [128] = "Area 128", + [129] = "Area 129", + [130] = "Area 130", + [131] = "Area 131", + [132] = "Area 132", + [133] = "Area 133", + [134] = "Area 134", + [135] = "Area 135", + [136] = "Area 136", + [137] = "Area 137", + [138] = "Area 138", + [139] = "Area 139", + [140] = "Area 140", + [141] = "Area 141", + [142] = "Area 142", + [143] = "Area 143", + [144] = "Area 144", + [145] = "Area 145", + [146] = "Area 146", + [147] = "Area 147", + [148] = "Area 148", + [149] = "Area 149", + [150] = "Area 150", + [151] = "Area 151", + [152] = "Area 152", + [153] = "Area 153", + [154] = "Area 154", + [155] = "Area 155", + [156] = "Area 156", + [157] = "Area 157", + [158] = "Area 158", + [159] = "Area 159", + [160] = "Area 160", + [161] = "Area 161", + [162] = "Area 162", + [163] = "Area 163", + [164] = "Area 164", + [165] = "Area 165", + [166] = "Area 166", + [167] = "Area 167", + [168] = "Area 168", + [169] = "Area 169", + [170] = "Area 170", + [171] = "Area 171", + [172] = "Area 172", + [173] = "Area 173", + [174] = "Area 174", + [175] = "Area 175", + [176] = "Area 176", + [177] = "Area 177", + [178] = "Area 178", + [179] = "Area 179", + [180] = "Area 180", + [181] = "Area 181", + [182] = "Area 182", + [183] = "Area 183", + [184] = "Area 184", + [185] = "Area 185", + [186] = "Area 186", + [187] = "Area 187", + [188] = "Area 188", + [189] = "Area 189", + [190] = "Area 190", + [191] = "Area 191", + [192] = "Area 192", + [193] = "Area 193", + [194] = "Area 194", + [195] = "Area 195", + [196] = "Area 196", + [197] = "Area 197", + [198] = "Area 198", + [199] = "Area 199", + [200] = "Area 200", + [201] = "Area 201", + [202] = "Area 202", + [203] = "Area 203", + [204] = "Area 204", + [205] = "Area 205", + [206] = "Area 206", + [207] = "Area 207", + [208] = "Area 208", + [209] = "Area 209", + [210] = "Area 210", + [211] = "Area 211", + [212] = "Area 212", + [213] = "Area 213", + [214] = "Area 214", + [215] = "Area 215", + [216] = "Area 216", + [217] = "Area 217", + [218] = "Area 218", + [219] = "Area 219", + [220] = "Area 220", + [221] = "Area 221", + [222] = "Area 222", + [223] = "Area 223", + [224] = "Area 224", + [225] = "Area 225", + [226] = "Area 226", + [227] = "Area 227", + [228] = "Area 228", + [229] = "Area 229", + [230] = "Area 230", + [231] = "Area 231", + [232] = "Area 232", + [233] = "Area 233", + [234] = "Area 234", + [235] = "Area 235", + [236] = "Area 236", + [237] = "Area 237", + [238] = "Area 238", + [239] = "Area 239", + [240] = "Area 240", + [241] = "Area 241", + [242] = "Area 242", + [243] = "Area 243", + [244] = "Area 244", + [245] = "Area 245", + [246] = "Area 246", + [247] = "Area 247", + [248] = "Area 248", + [249] = "Area 249", + [250] = "Area 250", + [251] = "Area 251", + [252] = "Area 252", + [253] = "Area 253", + [254] = "Area 254", + [255] = "Area 255 - No transfers" +} + +RAVKAV_METROPOLIS = { + [1] = "Dan Region", + [2] = "2", + [3] = "3", + [4] = "Metropolis Be\'er Sheva", + [5] = "Metropolis Haifa", + [6] = "Metropolis Jerusalem", + [7] = "7", + [8] = "8", + [9] = "9", + [10] = "Outside of Metropolis" +} + +-- ExtendedTicketType, ETT +-- [etta] = { [ettb] = "etta description / ettb description / comments" +-- } RAVKAV_CONTRACT_DESCRIPTIONS = { - [1] = { [0] = "Single ticket", - [1] = "Return", - [2] = "Tickets for 2 trips", - [3] = "Tickets for 5 trips", - [4] = "Tickets for 10 trips", - [5] = "Tickets for 12 trips", - [6] = "Tickets for 15 trips", - [7] = "Tickets for 20 trips" + [1] = { [0] = "Amount of rides / Single ride", + [1] = "Amount of rides / Forward and return", + [2] = "Amount of rides / Ticket for 2 trips", + [3] = "Amount of rides / Ticket for 5 trips", + [4] = "Amount of rides / Ticket for 10 trips", + [5] = "Amount of rides / Ticket for 12 trips / Israel Railways only", + [6] = "Amount of rides / Ticket for 15 trips", + [7] = "Amount of rides / Ticket for 20 trips" }, - [2] = { [0] = "Monthly free travel", - [1] = "Weekly free travel", - [2] = "Daily free travel", - [3] = "Monthly free local travel", - [4] = "Free travel for the semester", - [5] = "Annual free travel" + [2] = { [0] = "Periodic / Month unlimited travel", + [1] = "Periodic / Week unlimited travel", + [2] = "Periodic / Day unlimited travel", + [3] = "Periodic / Semester B unlimited travel", + [4] = "Periodic / Semester A unlimited travel", + [5] = "Periodic / Annual unlimited travel", + [6] = "Periodic / Month regional travel", + [7] = "Periodic / Credit" }, - [4] = { [0] = "Reserve soldier coupon", - [1] = "Special travel" + [4] = { [0] = "Free of charge / Reserve soldier coupon", + [1] = "Free of charge / Special travel", + [2] = "Free of charge / Special travel / (soldiers only)", + [3] = "Post pay contract / (ABT (Account based ticketing) compliant)" }, - [6] = { [0] = "Total value of NIS 30", - [1] = "Total value of NIS 50", - [2] = "Total value of NIS 100", - [3] = "Total value of NIS 150", - [4] = "Total value of NIS 200", - [5] = "Total value of NIS 5 + 30", - [6] = "Total value of NIS 5 + 12.80", - [7] = "Total value of NIS 5 + 20" + [6] = { [0] = "Aggregate value / Value 1 / Payment of NIS 30", + [1] = "Aggregate value / Value 2 / Payment of NIS 50", + [2] = "Aggregate value / Value 3 / Payment of NIS 100", + [3] = "Aggregate value / Value 4 / Payment of NIS 150", + [4] = "Aggregate value / Value 5 / Payment of NIS 200", + [5] = "Aggregate value / Flexible aggregate value", + [6] = "Aggregate value / Payment of NIS 11.80", + [7] = "Aggregate value / Payment of NIS 18.60" }, - [7] = { [0] = "Tickets for 4 trips", - [1] = "Tickets for 6 trips", - [2] = "Supplementary ticket / voucher to claim", - [3] = "Special tickets" - } + [7] = { [0] = "Amount of rides / Ticket for 4 trips", + [1] = "Amount of rides / Ticket for 6 trips", + [2] = "Amount of rides / Supplement ticket/voucher to claim / addition to existing ticket", + [3] = "Amount of rides / Ticket for more than 20 rides / Special ride tickets", + [4] = "Amount of rides / Ticket for 12 trips / Israel Railways", + [5] = "Amount of rides / Ticket for 11 trips / East Jerusalem operators" + }, + [9] = { [1] = "Paper ticket / Continuation of trip on paper / reserved for reporting to Service Center", + [2] = "Paper ticket / Interchange of trip on paper / reserved for reporting to Service Center" + } } ---Predefined codes +--Predefined contract IDs -- 0- 999 Reserved by MOT --1000-1999 Egged --2000-2047 Compatibility tests RAVKAV_CONTRACT_TYPES = { --code = description / operators / comments - [1] = "\"Rest of the country\" – entire country except for Arava (soldiers)", --wilderness? - [2] = "Arava [Eilot District, Egged] (soldiers)", - [3] = "Rest of the country - entire country except for Yozei Dofen", --certain exceptions? - [4] = "Dan Region", - [6] = "Entire country except for the Arava from price code 5 and up", - [7] = "Entire country up to price code 7 inclusive", - [8] = "Jerusalem [CityPass] (police)", - [9] = "Free travel Egged + Egged-Transport (company employees and family)", - [10] = "Free travel Dan + Kavim [Dan, Kavim] (company employees and family)", - [11] = "Free travel Dan + Kavim + Egged [Dan, Kavim, Egged] (company employees and family)", - [12] = "Free travel Dan + Egged [Dan, Egged] (company employees and family)", - [13] = "Golan Heights Student Card [Golan Heights Regional Council]", - [14] = "Netanya Municipality Youth Pass [Egged Transport]", - [15] = "Betar Illit <> Beit Shemesh [Kavim]", - [16] = "Betar Illit internal [Kavim]", - [17] = "Betar Illit youth [Kavim] (youth only, discount excepted)", - [18] = "Shoham / Modi'in Illit Student Card [Superbus] (youth only)", - [19] = "Betar Illit <> Beit Shemesh youth [Kavim] (youth only, discount excepted)", - [21] = "Beit Shemesh internal [Superbus]", - [22] = "Modi'in Illit internal [Superbus]", - [23] = "Ramla internal [Superbus]", - [24] = "Modi'in Illit <> Bnei Brak [Superbus]", - [25] = "Shoham <> Tel-Aviv [Superbus]", - [26] = "Shoham <> Ben-Gurion [Superbus]", - [27] = "Modi'in internal [Connex and Veolia]", - [28] = "Modi'in <> Ramla <> Lod [Connex and Veolia]", - [29] = "Modi'in <> Tel-Aviv [Connex and Veolia]", - [30] = "Modi'in <> Jerusalem [Connex and Veolia]", - [31] = "Ashdod zones [Connex and Veolia]", - [32] = "Yavne [Connex and Veolia]", - [33] = "Free weekly Ashdod zones [Connex and Veolia]", - [34] = "Semester Modi'in <> Jerusalem [Connex and Veolia] (students only)", - [35] = "Annual Modi'in <> Jerusalem [Connex and Veolia] (students only)", - [36] = "Free daily Ramla [Superbus]", - [37] = "Free daily Lod [Connex and Veolia]", - [39] = "Free daily Lod <> Tel-Aviv [Connex and Veolia]", - [41] = "Semester Modi'in <> Tel-Aviv [Connex and Veolia] (students only)", - [42] = "Annual Modi'in <> Tel-Aviv [Connex and Veolia] (students only)", - [45] = "Rahat urban [Narcissus-Gal]", - [46] = "Rahat surrounds [Narcissus-Gal]", - [55] = "Free daily Ashdod internal [Egged-Transport]", - [56] = "Ashdod internal [Egged-Transport]", - [57] = "Ashkalon [Egged-Transport]", - [58] = "Kiryat-Gat [Egged-Transport]", - [59] = "Ashkalon <> Kiryat-Gat [Egged-Transport]", - [60] = "Be'er Sheva urban [Metrodan]", - [61] = "Student semester [Egged-Transport] (all clusters, students only)", - [62] = "Student annual [Egged-Transport] (all clusters, students only)", - [63] = "Free weekly Nazareth <> Kfar Kana [Jaybee Tours]", - [64] = "Free weekly Nazareth <> Turan/Baina [Jaybee Tours]", - [68] = "Free weekly Turan <> Haifa [Jaybee Tours]", - [69] = "Yearly subscription for lines 28, 298, 299 [Travel and Tourism] (students only)", - [70] = "Afula [Kavim]", - [71] = "Free daily Afula [Kavim]", - [72] = "Free daily Ha'Emek surrounds [Kavim]", - [73] = "Free daily Mount Tabor [Kavim] (line 350 only)", - [74] = "Free daily Tiberias [Connex and Veolia]", - [75] = "Tiberias [Connex and Veolia]", - [76] = "Free weekly Ma'alot <> Kfar Yasif <> Nahariya [Nativ Express]", - [77] = "Nazareth [M*A*S*H]", - [78] = "Nazareth <> Kfar Kana [M*A*S*H]", - [79] = "Free weekly Peki'in <> Horfeish <> Nahariya [Nativ Express]", - [81] = "Netanya internal [Egged-Transport]", - [82] = "Free daily Netanya internal [Egged-Transport]", - [86] = "Sefad [Nativ Express]", - [87] = "Nahariya [Nativ Express]", - [88] = "Shlomi <> Nahariya [Nativ Express]", - [91] = "Free daily – Sagol [Kavim] (Petach Tikva and Rosh Ha'Ayin)", - [92] = "Elad <> Petach Tikva [Egged-Transport]", - [93] = "Elad <> Bnei Brak [Egged-Transport]", - [94] = "Free daily Elad [Egged-Transport]", - [95] = "Free weekly Or Akiva <> Hadera [Nativ Express]", - [96] = "Free weekly Jat <> Charish <> Hadera [Nativ Express]", - [97] = "Kadima/Zoran <> Netanya [Nativ Express]", - [98] = "Netanya <> Tel-Aviv [Nativ Express]", - [100] = "Dan Region – Free 'white' subscription [Egged, Dan, Kavim, Metropoline, Afikim] (sold through Afikim only, includes subscriptions for students)", - [105] = "Dan Region – Samaria free 'brown' subscription [Egged, Dan, Kavim, Metropoline, Afikim] (sold through Afikim only, includes subscriptions for students)", - [109] = "Dan Region – Sagol [Kavim] (Petach Tikva and Rosh Ha'Ayin)", - [111] = "Dan Region – Free subscription zone 1 [Dan, Egged, Kavim, Metropoline] (including students)", - [112] = "Dan Region – Free subscription zone 2 [Dan, Egged, Kavim, Metropoline] (including students)", - [113] = "Dan Region – Free subscription surrounds [Dan, Egged, Kavim, Metropoline] (including students)", - [120] = "Dan Region - Free daily special [Dan] (in place of damaged smartcard)", - [121] = "Dan Region - cartisiya code 41 [Dan, Egged, Kavim, Metropoline] (ring 2, zone 22 + Petach Tikva only)", - [122] = "Dan Region - cartisiya code 42 [Dan, Egged, Kavim, Metropoline] (ring 1 only)", - [123] = "Dan Region - cartisiya code 43 [Dan, Egged, Kavim, Metropoline] (ring 2, zone 21 only)", - [124] = "Dan Region - cartisiya code 44 [Dan, Egged, Kavim, Metropoline] (surrounds)", - [135] = "Dan Region - Free daily zone 1 [Dan, Egged, Kavim, Metropoline] (including Petach Tikva area)", - [137] = "Dan Region - Afur, Bnei Brak [Dan]", - [200] = "Nationwide aggregate value [Egged, Dan, Kavim, Metropoline] (enabled temporarily on specific clusters)", - [210] = "Elad aggregate value [Egged-Transport] (Elad cluster only)", - [300] = "Sharing agreement lines 331, 332 [Travel and Tourism, Jaybee Tours]", - [303] = "Sharing agreement line 333 [Travel and Tourism, Jaybee Tours]", - [365] = "Free weekly Migdal Ha'Emek <> Haifa [Travel and Tourism, Jaybee Tours]", - [366] = "Free weekly Nazareth / Nazereth Illit <> Haifa [Travel and Tourism, Jaybee Tours]", - [367] = "Free weekly Nazareth <> University [Travel and Tourism, Jaybee Tours]", - [510] = "Netanya [Egged Transport, Nativ Express]", - [511] = "Netanya <> Kfar Yona [Egged Transport, Nativ Express]", - [512] = "Netanya <> Tel-Aviv [Egged Transport, Nativ Express]", - [515] = "Hadera [Egged, Nativ Express]", - [516] = "Hadera <> Or Akiva [Egged, Nativ Express]", - [517] = "Hadera <> Pardes Hanna [Egged, Nativ Express]", - [518] = "Netanya <> Hadera [Egged, Nativ Express]", - [520] = "Metropoline Haifa [Egged, Nazrin Express]", - [521] = "Single ticket code 10 [Egged, Nazrin Express]", - [522] = "Single ticket code 2 [Egged, Nazrin Express]", - [524] = "Single ticket code 4 [Egged, Nazrin Express]", - [526] = "Single ticket code 6 [Egged, Nazrin Express]", - [527] = "Single ticket code 7 [Egged, Nazrin Express]", - [528] = "Single ticket code 8 [Egged, Nazrin Express]", - [529] = "Single ticket code 9 [Egged, Nazrin Express]", - [530] = "Haifa zone [Egged, Nazrin Express]", - [532] = "Haifa surround [Egged, Nazrin Express]", - [534] = "Semester Afula <> Haifa [Egged, Nazrin Express] (students only)", - [535] = "Annual Afula <> Haifa [Egged, Nazrin Express] (students only)", - [536] = "Semester Metropoline Haifa [Egged, Nazrin Express] (students only)", - [537] = "Annual Metropoline Haifa [Egged, Nazrin Express] (students only)", - [538] = "Semester Haifa zone [Egged, Nazrin Express] (students only)", - [541] = "Cartisiya code 10 [Egged, Nazrin Express]", - [542] = "Cartisiya code 2 [Egged, Nazrin Express]", - [544] = "Cartisiya code 4 [Egged, Nazrin Express]", - [545] = "Cartisiya code 6 with hourly transition [Egged, Nazrin Express]", - [546] = "Cartisiya code 6 without hourly transition [Egged, Nazrin Express]", - [547] = "Cartisiya code 7 [Egged, Nazrin Express]", - [548] = "Cartisiya code 8 [Egged, Nazrin Express]", - [549] = "Cartisiya code 9 [Egged, Nazrin Express]", - [551] = "Nahariya <> Akko [Egged, Nativ Express]", - [552] = "Nahariya <> Kiryat Ata Junction [Egged, Nativ Express]", - [553] = "Nahariya <> Akko <> Haifa [Egged, Nativ Express]", - [554] = "Carmiel <> Akko [Egged, Nativ Express]", - [558] = "Akko <> Haifa [Egged, Nativ Express]", - [602] = "Single Jerusalem surrounds, code 2 + continuation [Egged, Superbus]", - [603] = "Shared Single Peruzdor <> Jerusalem [Egged, Superbus]", - [604] = "Jerusalem surrounds, shared, cartisiya code 2 [Egged, Superbus]", - [605] = "Peruzdor <> Jerusalem, cartisiya 5 + continuation [Egged, Superbus]", - [606] = "Peruzdor <> Jerusalem, cartisiya 6 + continuation [Egged, Superbus]", - [607] = "Beitar Illit <> Jerusalem return + continuation [Egged, Kavim]", - [608] = "Beitar Illit <> Jerusalem, cartisiya 10 + continuation [Egged, Kavim]", - [609] = "Beitar Illit <> Jerusalem, cartisiya 5 + continuation [Egged, Kavim] (entitled only (not including youth / senior citizens))", - [610] = "Beitar Illit <> Jerusalem, cartisiya 10 + continuation [Egged, Kavim]", - [611] = "Beitar Illit extended annual student [Egged, Kavim] (students only)", - [612] = "Beitar Illit extended semester A student [Egged, Kavim] (students only)", - [613] = "Beitar Illit extended semester B student [Egged, Kavim] (students only)", - [614] = "Beitar Illit standard annual student [Egged, Kavim] (students only)", - [615] = "Beitar Illit standard semester A student [Egged, Kavim] (students only)", - [616] = "Beitar Illit standard semester B student [Egged, Kavim] (students only)", - [617] = "Return Ticket + continuation [Egged, Kavim] (youth / senior citizens only)", - [620] = "Beit Shemesh <> Jerusalem, annual student [Egged, Superbus, CityPass] (students only)", - [621] = "Peruzdor <> Jerusalem, annual student [Egged, Superbus, CityPass] (students only)", - [622] = "Beit Shemesh <> Jerusalem, semester A student [Egged, Superbus, CityPass] (students only)", - [623] = "Peruzdor <> Jerusalem, semester A student [Egged, Superbus, CityPass] (students only)", - [624] = "Beit Shemesh <> Jerusalem, semester B student [Egged, Superbus, CityPass] (students only)", - [625] = "Peruzdor <> Jerusalem, semester B student [Egged, Superbus, CityPass] (students only)", - [626] = "Modi'in Illit <> Jerusalem annual student [Egged, Superbus, CityPass] (students only)", - [627] = "Modi'in Illit <> Jerusalem semester A student [Egged, Superbus, CityPass] (students only)", - [628] = "Modi'in Illit <> Jerusalem semester B student [Egged, Superbus, CityPass] (students only)", - [632] = "Beit Shemesh <> Jerusalem [Egged, Superbus, CityPass] (sold by Superbus)", - [633] = "Peruzdor <> Jerusalem [Egged, Superbus, CityPass] (sold by Superbus)", - [634] = "Modi'in Illit <> Jerusalem [Egged, Superbus, CityPass] (sold by Superbus)", - [636] = "Beitar Illit extended youth [Egged, Kavim] (sold by Kavim)", - [637] = "Extended, Beitar Illit <> Jerusalem [Egged, Kavim] (sold by Kavim)", - [638] = "Standard, Beitar Illit <> Jerusalem [Egged, Kavim] (sold by Kavim)", - [641] = "Jerusalem shared single code 2 [Egged, CityPass, Superbus]", - [642] = "Jerusalem shared single code 3 [Egged, CityPass, Superbus]", - [645] = "Jerusalem shared cartisiyot code 2 [Egged, CityPass, Superbus]", - [646] = "Jerusalem shared cartisiyot code 3 [Egged, CityPass, Superbus]", - [647] = "Jerusalem – cartisiya 2 [Egged, CityPass] (Choham'f - to The Western Wall)", - [651] = "Jerusalem free surrounds semester A [Egged, CityPass, Superbus] (students only)", - [652] = "Jerusalem free surrounds annual [Egged, CityPass, Superbus] (students only)", - [653] = "Jerusalem surrounds [Egged, CityPass, Superbus] (respected by Superbus only)", - [654] = "Jerusalem free surrounds semester B [Egged, CityPass, Superbus] (students only)", - [658] = "Beit Shemesh Jerusalem semester B [Egged, CityPass] (students only)", - [659] = "Beit Shemesh Jerusalem [Egged, CityPass]", - [680] = "Lines 400-402 Bnei Brak <> Jerusalem [Egged, Dan]", - [690] = "East Jerusalem [East Jerusalem operators]", - [692] = "East Jerusalem surrounds [East Jerusalem operators]", - [701] = "Ramla <> Lod [Egged, Superbus, Connex]", - [702] = "Be'er Ya'akov <> Ben-Gurion [Egged, Superbus, Connex]", - [703] = "Ramla <> Lod <> Tel-Aviv [Egged, Superbus, Connex]", - [704] = "Rechovot <> Rishon Le'Tziyon <> Ramla <> Lod [Egged, Superbus, Connex]", - [705] = "Lod [Egged, Superbus, Connex]", - [706] = "Lod <> Petach Tikva [Egged, Connex]", - [707] = "Rechovot <> Rishon Le'Tziyon Be'er Ya'akov junction [Egged, Superbus]", - [708] = "Rechovot <> Tel-Aviv semester B [Egged, Superbus] (students only)", - [709] = "Rechovot <> Tel-Aviv [Egged, Superbus]", - [811] = "Semester South [Egged, Metrodan, Metropoline, Egged-Transport] (students only)", - [812] = "Annual South [Egged, Metrodan, Metropoline, Egged-Transport] (students only)", - [821] = "Cartisiya 2 Tel-Aviv <> Be'er Sheva [Metropoline, Egged-Transport]", - [901] = "Israel Rail free daily + Dan Region [Dan, Egged, Kavim, Metropoline] (senior citizen)", - [902] = "Israel Rail free daily + Dan Region [Dan, Egged, Kavim, Metropoline] (standard passenger)", - [903] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (senior citizen, reform)", - [904] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (senior citizen, reform)", - [905] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (standard passenger, reform)", - [906] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (standard passenger, reform)", - [907] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (senior citizen, special)", - [908] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (senior citizen, standard)", - [909] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (senior citizen, special)", - [910] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (senior citizen, standard)", - [911] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (standard passenger, special)", - [912] = "Israel Rail + Dan Region zone 1 [Dan, Egged, Kavim, Metropoline] (standard passenger, standard)", - [913] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (standard passenger, special)", - [914] = "Israel Rail + Dan Region surrounds [Dan, Egged, Kavim, Metropoline] (standard passenger, standard)", - [925] = "Israel Rail + Haifa zone [Egged] (senior citizen)", - [926] = "Israel Rail + Haifa zone [Egged] (standard passenger)", - [935] = "Israel Rail + Shoham <> Ben-Gurion [Superbus] (senior citizen)", - [936] = "Israel Rail + Shoham <> Ben-Gurion [Superbus] (standard passenger)", - [940] = "Single Ticket Israel Rail + Samaria lines [Afikim]", - [960] = "Israel Rail + Be'er Sheva [Metrodan]", - [962] = "Israel Rail + Netanya [Egged Transport]", - [964] = "Israel Rail + Rechovot [Egged]", - [966] = "Israel Rail + Lod [Superbus, Connex, Egged]", - [968] = "Israel Rail + Ashkalon [Egged Transport]", - [970] = "Israel Rail + Kiryat Gat [Egged Transport]", - [972] = "Israel Rail + Hadera (tentative)", - [974] = "Israel Rail + Beit Yehoshua [Nativ Express]", - [976] = "Israel Rail + Yavne West/East [Connex]", - [978] = "Israel Rail + Ashdod [Egged Transport]", - [980] = "Israel Rail + Kiryat Motzkin [Egged]", - [982] = "Israel Rail + Akko [Egged]" -} + [1] = "[Free travel] \"Entire country\" - except for Eilat and Eilot District / [all]", + [2] = "[Free travel] Eilat and Eilot District / [Eilot Regional Council, Egged]", + [3] = "[Free travel] Unlimited travel by settings / [all, private for each operator] / (operator employees and family)", + [4] = "[Free travel] Dan Region", + [5] = "[Free travel] Modi\'in", + [6] = "[Free travel] Entire country except for Arava starting from fare code 5 and up", + [7] = "[Free travel] Entire country / [all]", + [8] = "[Free travel] Jerusalem Light Rail / [CityPass] / (police, inspectors)", + [9] = "[Free travel] Egged + Egged Transport / [Egged, Egged Transport] / (operator employees and family)", + [10] = "[Free travel] Dan + Kavim / [Dan, Kavim] / (operator employees and family)", + [11] = "[Free travel] Dan + Kavim + Egged / [Dan, Kavim, Egged] / (operator employees and family)", + [12] = "[Free travel] Dan + Egged / [Dan, Egged] / (operator employees and family)", + [13] = "[Free travel] Golan Heights Youth Student Pass / [Golan Heights Regional Council] / (from September 1, 2011)", + [14] = "Amount of rides - Netanya Municipality / [Egged Transport] / (youth)", + [15] = "(obsolete) Month pass: Beitar Illit <> Beit Shemesh / [Kavim]", + [16] = "Month pass: Beitar Illit only / [Kavim]", + [17] = "Month pass: Beitar Illit only youth / [Kavim] / (youth only, discount excepted)", + [18] = "Youth student pass: Shoham / Modi\'in Illit / [Kavim] / (youth only)", + [19] = "Month pass: Beitar Illit <> Beit Shemesh youth / [Kavim] / (youth only, discount excepted)", + [20] = "Annual pass: Shoham Extended / [Kavim, Superbus] / (youth only)", + [21] = "Month pass: Beit Shemesh only / [Superbus] / (standard, standard student, extended student)", + [22] = "Month pass: Modi\'in Illit only / [Kavim] / (standard, standard student, extended student)", + [23] = "Month pass: Ramla only / [Kavim] / (standard, standard student, extended student)", + [24] = "(obsolete) Month pass: Modi\'in Illit <> Bnei Brak / [Kavim]", + [25] = "(obsolete) Month pass: Shoham <> Tel-Aviv / [Kavim]", + [26] = "(obsolete) Month pass: Shoham <> Ben-Gurion Airport / [Kavim]", + [27] = "Month pass: Modi\'in only / [Kavim] / (standard, standard student, extended student)", + [28] = "(obsolete) Month pass: Modi\'in <> Ramla <> Lod / [Kavim] / (standard, standard student, extended student)", + [29] = "(obsolete) Month pass: Modi\'in <> Tel-Aviv / [Kavim] / (standard, standard student, extended student)", + [30] = "(obsolete) Month pass: Modi\'in <> Jerusalem / [Kavim, Egged Transport] / (standard, standard student, extended student)", + [31] = "(obsolete) Month pass: Ashdod regional / [Afikim] / (standard, standard student, extended student)", + [32] = "Month pass: Yavne / [Afikim] / (standard, standard student, extended student)", + [33] = "(obsolete) Week pass: Ashdod regional / [Afikim]", + [34] = "[Free travel] Egged + Superbus / [Egged, Superbus] / (operator employees in the Amakim cluster and Metropolis Haifa only)", + [35] = "[Free travel] Egged + Nativ Express / [Egged, Nativ Express] / (operator employees of Egged and Nativ Express only)", + [36] = "(obsolete) Day pass: Ramla / [Kavim]", + [37] = "Day pass: Lod / [Kavim]", + [38] = "Annual pass: Shoham <> Petah-Tikva / [Kavim] / (youth only)", + [39] = "(obsolete) Day pass: Lod <> Tel-Aviv / [Kavim]", + [40] = "Annual pass: Modi\'in Illit / [Kavim] / (youth only)", + [41] = "Annual pass: Shoham / [Kavim] / (youth only)", + [42] = "(obsolete) Annual pass: Modi\'in <> Tel-Aviv / [Connex Veolia] / (students only)", + [45] = "Month pass: Rahat urban / [Galim (Narkis Gal)]", + [46] = "(obsolete) Month pass: Rahat extended / [Galim (Narkis Gal)]", + [51] = "Semester pass: Kiryat-Gat / [Egged Transport] / (students only)", + [52] = "Annual pass: Kiryat-Gat / [Egged Transport] / (students only)", + [53] = "Semester pass: Ashkelon <> Kiryat-Gat / [Egged Transport] / (students only)", + [54] = "Annual pass: Ashkelon <> Kiryat-Gat / [Egged Transport] / (students only)", + [55] = "Day pass: Ashdod only / [Afikim]", + [56] = "(obsolete) Month pass: Ashdod only / [Afikim] / (standard, standard student, extended student)", + [57] = "Month pass: Ashkelon / [Dan South]", + [58] = "Month pass: Kiryat-Gat / [Dan South]", + [59] = "Month pass: Ashkelon <> Kiryat-Gat / [Dan South]", + [60] = "(obsolete) Month pass: Be\'er Sheva urban / [Metrodan]", + [61] = "Semester pass: Student / [Egged Transport] / (Netanya, Ashdod, Ashkelon, students only)", + [62] = "Annual pass: Student / [Egged Transport] / (Netanya, Ashdod, Ashkelon, students only)", + [63] = "Week pass: Nazareth <> Kfar Kana / [GB Tours]", + [64] = "Week pass: Nazareth <> Turan/Baina / [GB Tours]", + [65] = "Annual pass: lines 33, 335 / [GB Tours] / (students only)", + [66] = "Annual pass: lines 31, 38, 337 / [GB Tours] / (students only)", + [67] = "Annual pass: Tamra/Kabul / [NTT (Nazareth Travel & Tourism)] / (based on fare code 6)", + [68] = "Week pass: Turan <> Haifa / [GB Tours]", + [69] = "Annual pass: lines 28, 298, 299 / [NTT (Nazareth Travel & Tourism)] / (based on fare code 8)", + [70] = "Month pass: Afula / [Kavim] / (standard, standard student, extended student)", + [71] = "Day pass: Afula / [Kavim, Superbus]", + [72] = "Day pass: Ha\'Emek - Extended / [Kavim]", + [73] = "Day pass: Mount Tabor / [Kavim] / (line 350 only)", + [74] = "Day pass: Tiberias / [Superbus, Connex Veolia]", + [75] = "Month pass: Tiberias / [Superbus, Connex Veolia] / (standard, standard student, extended student)", + [76] = "Week pass: Ma\'alot <> Kfar Yasif <> Nahariya / [Nativ Express]", + [77] = "Month pass: Nazareth / [NTT (United Bus Services Nazareth)]", + [78] = "Month pass: Nazareth <> Kfar Kana / [NTT (United Bus Services Nazareth)]", + [79] = "Week pass: Peki\'in <> Horfish <> Nahariya / [Nativ Express]", + [80] = "Annual pass: lines 294, 295 / [Nativ Express] / (students only)", + [81] = "(obsolete) Month pass: Netanya only / [Egged Transport]", + [82] = "Day pass: Netanya only / [Egged Transport]", + [85] = "Month pass: Migdal HaEmek [Superbus] / (standard, standard student included)", + [86] = "Month pass: Safed / [Nativ Express]", + [87] = "Month pass: Nahariya / [Nativ Express]", + [88] = "Month pass: Shlomi <> Nahariya / [Nativ Express]", + [91] = "Day pass: Dan Region: Petah-Tikva and Rosh Ha\'Ayin - Purple / [Kavim, Afikim]", + [92] = "Month pass: Elad <> Petah-Tikva / [Kavim]", + [93] = "Month/Day pass: Elad <> Bnei Brak / [Kavim] / (standard, standard student, extended student)", + [94] = "Day pass: Elad / [Kavim]", + [95] = "Week pass: Or Akiva <> Hadera / [Kavim]", + [96] = "Week pass: Jat <> Harish <> Hadera / [Kavim]", + [97] = "(obsolete) Month pass: Kadima/Zoran <> Netanya / [Kavim]", + [98] = "Month pass: Netanya <> Tel-Aviv / [Nativ Express]", + [100] = "(obsolete) Month pass: Dan Region - White / [Egged, Dan, Kavim, Metropoline, Afikim] / (sold through Afikim/Kavim only, including student contracts)", + [101] = "[Free / Special travel] - Tel Aviv University / [Dan] / (internal)", + [105] = "Month pass: Dan Region - Brown - Gush 5 - Samaria / [Egged, Dan, Kavim, Metropoline, Afikim] / (sold through Afikim only (standard, standard student, extended student) )", + [109] = "Month pass: Dan Region - Purple - Petah-Tikva and Rosh Ha\'Ayin [Kavim, Afikim] / (standard, standard student, extended student) (interchange areas 45, 46)", + [111] = "(obsolete) Month pass: Dan Region - Zone 1 / [Dan, Egged, Kavim, Metropoline] / (including students) (interchange areas 42, 45)", + [112] = "(obsolete) Month pass: Dan Region - Zone 2 / [Dan, Egged, Kavim, Metropoline] / (including students) (interchange areas 43)", + [113] = "(obsolete) Month pass: Dan Region - Extended / [Dan, Egged, Kavim, Metropoline] / (including students) (interchange areas 42, 43, 45, 46)", + [120] = "Day pass: Dan Region - special / [Dan] / (in place of damaged smartcard)", + [121] = "Dan Region - Amount of rides - code 41 / [Dan, Egged, Kavim, Metropoline] / (ring 2, zone 22 + Petah-Tikva only) (interchange areas 45, 46)", + [122] = "Dan Region - Amount of rides - code 42 / [Dan, Egged, Kavim, Metropoline] / (ring 1 only) (interchange areas 42)", + [123] = "Dan Region - Amount of rides - code 43 / [Dan, Egged, Kavim, Metropoline] / (ring 2, zone 21 only) (interchange areas 43)", + [124] = "Dan Region - Amount of rides - code 44 / [Dan, Egged, Kavim, Metropoline] / (extended)", + [135] = "(obsolete) Day pass: Dan Region - Ring 1 / [Dan, Egged, Kavim, Metropoline] / (interchange areas 42)", + [137] = "Month pass: Dan Region - Gray - Bnei Brak / [Dan, Afikim]", + [141] = "Month/Week/Day pass: Dan Region - Ring 1 / [Dan, Egged, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [142] = "Month/Week/Day pass: Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [143] = "Month/Week/Day pass: Dan Region + North - Ring 1, 2.1, 2.2, 3.1, 3.4 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [144] = "Month/Week/Day pass: Dan Region + South - Ring 1, 2.2, 2.3, 3.2, 3.3 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [145] = "Month/Week/Day pass: Dan Region: HaSharon + Netanya - Ring 2.1, 3.1, 3.4 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [146] = "Month/Week/Day pass: Dan Region: Metropolis East - Ring 2.2, 3.2, 3.4 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Israel Railways] / (standard, standard student, extended student)", + [147] = "Month/Week/Day pass: Dan Region: Metropolis South - Ring 2.3, 3.2, 3.3 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Israel Railways] / (standard, standard student, extended student)", + [148] = "(obsolete) Month/Week/Day pass: FULL Dan Region - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [149] = "(obsolete) Month/Week/Day pass: Dan Region - Ring 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [150] = "Month/Week/Day pass: FULL Dan Region + Hadera + Wadi Aara - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, zone 701, 702 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Superbus, Israel Railways] / (standard, standard student, extended student)", + [151] = "Month/Week/Day pass: FULL Dan Region + Ashkelon + Kiryat Gat - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, zone 801, 802 / [Dan, Egged, Egged Transport, Kavim, Metropoline, Afikim, Nativ Express, Dan South, Israel Railways] / (standard, standard student, extended student)", + [152] = "Month/Week/Day pass: Dan Region: HaSharon region only - Ring 2.1 / [Dan, Egged, Kavim, Metropoline, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [153] = "Month/Week/Day pass: Dan Region: Rosh-Ha-Ayin + Shoham region only - Ring 2.2 / [Egged, Kavim, Metropoline, Afikim, Israel Railways] / (standard, standard student, extended student)", + [154] = "Month/Week/Day pass: Dan Region: Rehovot region only - Ring 2.3 / [Egged, Kavim, Metropoline, Afikim, Israel Railways] / (standard, standard student, extended student)", + [155] = "Month/Week/Day pass: Dan Region: Netanya region only - Ring 3.1 / [Egged, Egged Transport, Kavim, Metropoline, Nativ Express, Israel Railways] / (standard, standard student, extended student)", + [156] = "(obsolete) Month/Week/Day pass: Dan Region: Modi\'in region only - Ring 3.2 / [Egged, Egged Transport, Kavim, Metropoline, Nativ Express, Israel Railways]", --merged to 697 + [157] = "Month/Week/Day pass: Dan Region: Ashdod region only - Ring 3.3 / [Dan South, Afikim, Egged, Egged Transport, Kavim, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [171] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Be\'er Sheva - Ring 1 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metrodan, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [172] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Be\'er Sheva - Extended - Ring 1, 2.1, 2.2, 2.3 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metropoline, Metrodan, Israel Railways] / (standard, standard student, extended student)", + [173] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Be\'er Sheva and Western Negev - Ring 1, 2.1, 2.2, 3.1, 3.2 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metropoline, Metrodan, Israel Railways] / (standard, standard student, extended student)", + [174] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Be\'er Sheva + South East - Ring 1, 2.1, 2.3, 3.2, 3.3, 3.4 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metrodan, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [175] = "Month/Week/Day pass: Metropolis Be\'er Sheva: West area + Negev - Ring 2.1, 2.2, 3.1, 3.2 / [Dan South, Egged, Egged Transport, Galim (Narkis Gal), Metropoline, Israel Railways] / (standard, standard student, extended student)", + [176] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Lehavim area + West area + Negev - Ring 2.1, 2.2, 3.2, 3.3, 3.4 / [Dan South, Egged, Egged Transport, Galim (Narkis Gal), Metropoline, Israel Railways] / (standard, standard student, extended student)", + [177] = "Month/Week/Day pass: FULL Metropolis Be\'er Sheva + Mitzpe Ramon area - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + zone 811/901 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metropoline, Metrodan, Israel Railways] / (standard, standard student, extended student)", --Excludes Galim + [178] = "(obsolete) Month pass: Metropolis Be\'er Sheva + Ashkelon area - Ring 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + zone 801, 802 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metropoline, Metrodan, Israel Railways] / (standard, standard student, extended student)", + [179] = "Month/Week/Day pass: FULL Metropolis Be\'er Sheva + Ashkelon area + Kiryat Gat area - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + zone 801, 802 / [Dan Be\'er Sheva, Dan South, Egged, Egged Transport, Metropoline, Metrodan, Israel Railways] / (standard, standard student, extended student)", --Excludes Galim + [180] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Rahat + Lehavim area only - Ring 2.1 / [Dan South, Egged, Egged Transport, Galim (Narkis Gal), Metropoline, Israel Railways] / (standard, standard student, extended student)", + [181] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Ofakim area only - Ring 2.2 / [Dan South, Egged, Egged Transport, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [182] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Omer + Hura area only - Ring 2.3 / [Egged, Egged Transport, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [183] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Sderot + Netivot area only - Ring 3.1 / [Dan South, Egged, Egged Transport, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [184] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Yeruham area only - Ring 3.2 / [Egged, Metropoline] / (standard, standard student, extended student)", + [185] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Arad + Dimona area only - Ring 3.3 / [Egged, Egged Transport, Metropoline, Israel Railways] / (standard, standard student, extended student)", + [186] = "Month/Week/Day pass: Metropolis Be\'er Sheva: Southern Mt Hevron area only - Ring 3.4 / [Egged, Egged Transport, Metropoline] / (standard, standard student, extended student)", + [200] = "Nationwide aggregate value", + [210] = "Elad aggregate value / [Egged Transport] / (Elad cluster only)", + [220] = "Afula aggregate value / [Kavim] / (HaEmek cluster only)", + [221] = "Jerusalem aggregate value / [Kavim]", + [225] = "(obsolete) Hashmonaim aggregate value / [Kavim] / (Hashmonaim cluster only)", + [230] = "Golan Heights aggregate value / [Golan Heights Regional Council]", + [300] = "(obsolete) Sharing agreement - lines 31, 332 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [301] = "Sharing agreement - code 1 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [303] = "Sharing agreement - code 3 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [304] = "Sharing agreement - code 4 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [305] = "Sharing agreement - code 5 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [306] = "Sharing agreement - code 6 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [307] = "Sharing agreement - code 7 (including week pass) / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [308] = "Sharing agreement - code 8 (including week pass and students) / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [309] = "Sharing agreement - code 9 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [311] = "Sharing agreement - code 11 (including week pass and students) / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [312] = "Sharing agreement - code 52 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [313] = "Sharing agreement - code 63 / [NTT (Nazareth Travel & Tourism), GB Tours] / (all shared contracts)", + [326] = "Continuation of trip - code 6 / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [327] = "Continuation of trip - code 7 / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [328] = "Continuation of trip - code 8 / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [329] = "Continuation of trip - code 9 / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [336] = "Continuation of trip - code 6 - Nazareth <> Haifa / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [337] = "Continuation of trip - code 7 - Nazareth <> Haifa / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [338] = "Continuation of trip - code 8 - Nazareth <> Haifa / [NTT (Nazareth Travel & Tourism), GB Tours, Egged, Dan North]", + [339] = "Annual pass: Tamra <> Haifa / [NTT (Nazareth Travel & Tourism), Dan North]", + [340] = "Month pass: Tayibe / [Kavim, Metropoline]", + [341] = "Month pass: Tamra / [Egged, Nativ Express]", + [342] = "Month pass: Tira / [Kavim, Metropoline]", + [343] = "Month pass: Qalansawe / [Kavim, Metropoline]", + [344] = "Month pass: Or Akiva / [Kavim, Egged]", + [345] = "Month pass: Umm al-Fahm / [Kavim]", + [346] = "Month pass: Elad / [Kavim]", + [347] = "Month pass: Shefa-\'Amr / [Egged]", + [348] = "Month pass: Dimona / [Egged]", + [349] = "Month pass: Netivot / [Egged Transport]", + [350] = "Month pass: Sakhnin / [NTT (Nazareth Travel & Tourism)]", + [351] = "Month pass: Baqa al-Gharbiyye / [Kavim]", + [352] = "Month pass: Ofakim / [Egged Transport]", + [353] = "Month pass: Arad / [Metropoline]", + [354] = "Month pass: Sderot / [Egged Transport]", + [355] = "Month pass: Kiryat Malakhi / [Egged Transport]", + [356] = "Month pass: Ariel / [Afikim]", + [357] = "Month pass: Beit She\'an / [Superbus, Kavim]", + [358] = "Month pass: Givat Ada <> Binyamina / [Kavim]", + [365] = "Month/Day pass: Afula, Beit She\'an, Southern Golan Heights and Kineret areas - zone 502, 601, 602 / [Dan, Dan North, Dan South, Dan Be\'er Sheva, Egged, Egged Transport, Kavim, Metropoline, Afikim, Superbus, CityPass, Golan Regional Council, Galim (Narkis Gal), GB Tours, Illit, Nativ Express, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways]", + [366] = "Month/Day pass: Afula, Beit She\'an, Wadi Aara and Hadera areas - zone 601, 602, 701, 702 / [Egged, Kavim, Afikim, Superbus, Golan Regional Council, GB Tours, Nativ Express, NTT (Nazareth Travel & Tourism), Israel Railways]", + [367] = "Month/Day pass: Afula, Zichron Ya\'akov, Carmiel and Nazareth areas - Metropolis Haifa: Ring 3.2, 3.3, 3.4, zone 601, 602 / [Dan North, Egged, Kavim, Superbus, Golan Regional Council, GB Tours, Nativ Express, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways]", + [368] = "Month/Day pass: Nahariya, Carmiel, Nazareth, Western Galilee, Kineret and Golan Heights areas - Metropolis Haifa: Ring 3.1, 3.2, 3.3, zone 501, 502 / [Dan North, Egged, Superbus, Golan Regional Council, GB Tours, Nativ Express, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways]", + [369] = "Month/Day pass: Wadi Aara, Hadera, Nazareth and Zichron Ya\'akov areas - Metropolis Haifa: Ring 3.3, 3.4, zone 701, 702 / [Egged, Kavim, Afikim, Superbus, GB Tours, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways]", + [370] = "Month/Day pass: Ashkelon, Netivot and Sderot areas - Metropolis Be\'er Sheva: Ring 3.1, zone 801 / [Dan South, Egged, Metropoline, Afikim, Superbus, Galim (Narkis Gal), Israel Railways]", + [371] = "Month/Day pass: Kiryat Gat, Netivot, Sderot and Atniel areas - Metropolis Be\'er Sheva: Ring 3.1, 3.4, zone 802 / [Dan South, Egged, Egged Transport, Metropoline, Galim (Narkis Gal), Israel Railways]", + [373] = "Month/Day pass: Ashkelon, Kiryat Gat, Gedera and Ashdod areas - Dan Region: Ring 3.3, zone 801, 802 / [Dan South, Egged, Metropoline, Afikim, Superbus, Galim (Narkis Gal), Israel Railways]", + [374] = "Month/Day pass: Kiryat Gat and Beit Shemesh areas - Metropolis Jerusalem: Ring 3.1, zone 802 / [Dan South, Egged, Egged Transport, Kavim, Metropoline, Superbus, Galim (Narkis Gal), Israel Railways]", + [375] = "Month/Day pass: Be\'er Sheva - Jerusalem - Metropolis Be\'er Sheva: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Jerusalem: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, 3.5 + zone 801, 802 / [Dan South, Dan Be\'er Sheva, Egged, Egged Transport, Kavim, Metropoline, Afikim, Superbus, CityPass, Galim (Narkis Gal), Illit, Israel Railways]", + [380] = "Amount of 2 rides - code 4 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [381] = "Amount of 2 rides - code 5 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [382] = "Amount of 2 rides - code 6 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [383] = "Amount of 2 rides - code 7 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [384] = "Amount of 2 rides - code 8 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [385] = "Amount of 2 rides - code 9 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [386] = "Amount of 2 rides - code 11 / [NTT (Nazareth Travel & Tourism), GB Tours]", + [401] = "Reserved", + [402] = "Reserved", + [410] = "Month pass: Jerusalem - Ramallah Extended / [Jerusalem - Ramallah united]", + [411] = "Month pass: Jerusalem - Ramallah / [Jerusalem - Ramallah united]", + [412] = "Month pass: Jerusalem - Beit Jala Extended / [South Jerusalem united]", + [413] = "Month pass: Jerusalem - Beit Jala / [South Jerusalem united]", + [414] = "Reserved for East Jerusalem operators", + [415] = "Reserved for East Jerusalem operators", + [416] = "Reserved for East Jerusalem operators", + [417] = "Reserved for East Jerusalem operators", + [418] = "Reserved for East Jerusalem operators", + [419] = "[Free travel] / [Jerusalem - Ramallah united] / (operator employees and family)", + [420] = "[Free travel] / [South Jerusalem united] / (operator employees and family)", + [421] = "Reserved for East Jerusalem operators", + [422] = "Reserved for East Jerusalem operators", + [423] = "Reserved for East Jerusalem operators", + [424] = "Reserved for East Jerusalem operators", + [425] = "Reserved for East Jerusalem operators", + [426] = "Reserved for East Jerusalem operators", + [427] = "Reserved for East Jerusalem operators", + [428] = "Reserved for East Jerusalem operators", + [429] = "Reserved for East Jerusalem operators", + [430] = "Reserved for East Jerusalem operators", + [499] = "Month/Week/Day pass: Metropolis Haifa: Hadera <> Haifa (including Wadi Ara, Afula, Beit She\'an areas) - Ring 1, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4, zone 601, 602, 701, 702 / [Egged, Dan North, Carmelit, Kavim, Superbus, Nativ Express, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [500] = "Month/Week/Day pass: Metropolis Haifa: Circular Haifa - Ring 1 / [Egged, Dan North, Carmelit, Nativ Express, Kavim, Superbus, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [501] = "Month/Week/Day pass: Metropolis Haifa: Circular Haifa - Extended - Ring 1, 2.1, 2.2 / [Egged, Dan North, Carmelit, Nativ Express, Kavim, Superbus, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [502] = "Month/Week/Day pass: Metropolis Haifa: Circular Haifa - Northern - Ring 1, 2.1, 3.1, 3.2 / [Egged, Dan North, Carmelit, Nativ Express, Kavim, Superbus, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [503] = "Month/Week/Day pass: Metropolis Haifa: Circular Haifa - Southern - Ring 1, 2.2, 3.3, 3.4 / [Egged, Dan North, Carmelit, Nativ Express, Kavim, Superbus, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [504] = "Month/Week/Day pass: Metropolis Haifa: Northern - Ring 2.1, 3.1, 3.2 / [Egged, Nativ Express, Kavim, Superbus, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [505] = "Month/Week/Day pass: Metropolis Haifa: Southern - Ring 2.2, 3.3, 3.4 / [Egged, Nativ Express, Kavim, Superbus, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [506] = "Month/Week/Day pass: Metropolis Haifa: Eastern - Ring 2.1, 2.2, 3.2, 3.3 / [Egged, Nativ Express, Kavim, Superbus, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [507] = "Month/Week/Day pass: FULL Metropolis Haifa: Ring 1, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4 / [Egged, Dan North, Carmelit, Nativ Express, Kavim, Superbus, Israel Railways] / (standard, standard student, extended student)", --included in 499/741 + [508] = "(obsolete) Month/Week/Day pass: Metropolis Haifa: Ring 2.1, 2.2, 3.1, 3.2, 3.3, 3.4 / [Egged, Dan North, Nativ Express, Kavim, Superbus, Israel Railways] / (standard, standard student, extended student)", + [509] = "Month pass: Netanya / [Egged Transport, Nativ Express] / (sold by Egged Transport / (standard, standard student, extended student) )", + [510] = "(obsolete) Month pass: Netanya / [Egged Transport, Nativ Express] / (sold by Egged Transport / (standard, standard student, extended student) )", + [511] = "(obsolete) Month pass: Netanya <> Kfar Yona / [Egged Transport, Nativ Express] / (sold by Nativ Express)", + [512] = "(obsolete) Month pass: Netanya <> Tel-Aviv / [Egged Transport, Nativ Express] / (sold by Nativ Express)", + [515] = "Month pass: Hadera / [Egged, Nativ Express] / (standard, standard student, extended student)", + [516] = "Month pass: Hadera <> Or Akiva / [Egged, Nativ Express]", + [517] = "Month pass: Hadera <> Pardes Hana / [Egged, Nativ Express]", + [518] = "Month pass: Netanya <> Hadera / [Egged, Egged Transport, Nativ Express, Kavim] / (standard, standard student, extended student)", + [520] = "(obsolete) Month pass: Metropolis Haifa / [Egged, Dan North, Nativ Express] / (interchange areas 51, 52, 53, 54, 55, 56, 57, 58, 59)", + [521] = "Single ride - code 1 / [Egged, Dan North]", + [522] = "Single ride - code 2 / [Egged, Dan North]", + [524] = "Single ride - code 4 / [Egged, Dan North]", + [526] = "Single ride - code 6 / [Egged, Dan North]", + [527] = "(obsolete) Single ride - code 7 / [Egged, Dan North, Nativ Express]", + [528] = "(obsolete) Single ride - code 8 / [Egged, Dan North, Nativ Express]", + [529] = "(obsolete) Single ride - code 9 / [Egged, Dan North, Nativ Express]", + [530] = "(obsolete) Month pass: Haifa zone / [Egged, Dan North, Nativ Express] / (interchange areas 51, 56, 57, 58, 59)", + [532] = "(obsolete) Month pass: Haifa extended / [Egged, Dan North, Nativ Express] / (interchange areas 51, 52, 54, 56, 57, 58, 59)", + [534] = "Semester pass: Afula <> Haifa / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 56, 57)", + [535] = "Annual pass: Afula <> Haifa / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 56, 57)", + [536] = "Semester pass: Metropolis Haifa / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 52, 54, 56, 57, 58, 59)", + [537] = "Annual pass: Metropolis Haifa / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 52, 54, 56, 57, 58, 59)", + [538] = "Semester pass: Haifa zone / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 56, 57, 58, 59)", + [539] = "Annual pass: Haifa zone / [Egged, Dan North, Nativ Express] / (students only) (interchange areas 51, 56, 57, 58, 59)", + [540] = "(obsolete) Continuation of trips - intercity lines / [Egged, Dan North]", + [541] = "Amount of rides - code 1 / [Egged, Dan North, Superbus, Nativ Express] / (interchange areas 52, 53, 55, 56, 57, 58, 59, trip start and transit inside area only, except 52, 56)", + [542] = "Amount of rides - code 2 / [Egged, Dan North] / (interchange areas 51, 56, 57, 58, 59)", + [544] = "Amount of rides - code 4 / [Egged, Dan North, Nativ Express] / (interchange areas 51, 52, 53, 54, 55, 56, 57, 58, 59)", + [545] = "Amount of rides - code 6 - with hourly transfers / [Egged, Dan North, Nativ Express] / (interchange areas 51, 52, 53, 54, 55, 56, 57, 58, 59)", + [546] = "(obsolete) Amount of rides - code 6 - without hourly transfers / [Egged, Nativ Express]", + [547] = "(obsolete) Amount of rides - code 7 / [Egged, Nativ Express]", + [548] = "(obsolete) Amount of rides - code 8 / [Egged, Nativ Express]", + [549] = "(obsolete) Amount of rides - code 9 / [Egged, Nativ Express]", + [550] = "Month pass: Krayot area / [Egged, Dan North] / (interchange area 52)", + [551] = "(obsolete) Month pass: Nahariya <> Akko / [Egged, Nativ Express]", + [552] = "(obsolete) Month pass: Nahariya <> Kiryat Ata Junction / [Egged, Nativ Express]", + [553] = "(obsolete) Month pass: Nahariya <> Akko <> Haifa / [Egged, Dan North, Nativ Express] / (interchange areas 51, 56, 57, 58, 59)", + [554] = "(obsolete) Month pass: Carmiel <> Akko / [Egged, Nativ Express]", + [555] = "(obsolete) Month pass: Akko <> Carmiel <> Haifa / [Egged, Dan North] / (interchange areas 51, 52, 56, 57, 58, 59)", + [556] = "(obsolete) Month pass: Shefa-\'Amr <> Haifa / [Egged, Dan North, Nativ Express] / (interchange areas 51, 52, 56, 57, 58, 59)", + [557] = "(obsolete) Month pass: Akko <> Kiryat Ata Junction / [Egged, Dan North]", + [558] = "(obsolete) Month pass: Akko <> Haifa / [Egged, Dan North] / (interchange areas 51, 52, 56, 57, 58, 59)", + [559] = "Month pass: Akko / [Egged, Nativ Express, Dan North]", + [560] = "Month/Week/Day pass: Metropolis Haifa: Akko area only - Ring 2.1 / [Egged, Nativ Express, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [561] = "Month/Week/Day pass: Metropolis Haifa: Yokneam - Tiv\'on area only - Ring 2.2 / [Egged, Nativ Express, Kavim, Superbus, NTT (Nazareth Travel & Tourism), Israel Railways] / (standard, standard student, extended student)", + [562] = "Month/Week/Day pass: Metropolis Haifa: Nahariya area only - Ring 3.1 / [Egged, Nativ Express, Superbus, Israel Railways] / (standard, standard student, extended student)", + [563] = "Month/Week/Day pass: Metropolis Haifa: Carmiel area only - Ring 3.2 / [Egged, Nativ Express, Superbus, NTT (Nazareth Travel & Tourism)] / (standard, standard student, extended student)", --overlap 1007 + [564] = "Month/Week/Day pass: Metropolis Haifa: Nazareth area only - Ring 3.3 / [Egged, Nativ Express, Superbus, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism)] / (standard, standard student, extended student)", + [565] = "Month/Week/Day pass: Metropolis Haifa: Zichron area only - Ring 3.4 / [Egged, Nativ Express, Kavim, Superbus, Israel Railways] / (standard, standard student, extended student)", + [568] = "Semester pass: Krayot / [Egged, Dan North]", + [569] = "Annual pass: Krayot / [Egged, Dan North]", + [570] = "(obsolete) Continuation of trip - Afula <> Haifa", + [574] = "Continuation of trip - Haifa - code 4 / [Egged, Dan North, Nativ Express]", + [575] = "Continuation of trip - Haifa - code 5 / [Egged, Dan North, Nativ Express]", + [576] = "Continuation of trip - Haifa - code 6 / [Egged, Dan North, Nativ Express]", + [577] = "Continuation of trip - Haifa - code 7 / [Egged, Dan North, Nativ Express]", + [578] = "Continuation of trip - Haifa - code 8 / [Egged, Dan North, Nativ Express]", + [581] = "Continuation of trip - Haifa - code 11 / [Egged, Dan North, Nativ Express]", + [586] = "Continuation of trip - Haifa - code 16 / [Egged, Dan North, Nativ Express]", + [590] = "Continuation of trip - Haifa - code 20 / [Egged, Dan North]", + [591] = "Continuation of trip - Safed <> Haifa - code 20 / [Egged, Dan North, Nativ Express]", + [593] = "Continuation of trip - code 23 / [Egged, Dan North]", + [597] = "Continuation of trip - Afula <> Haifa - code 7 / [Egged, Dan North, Nativ Express] / (sold by Nativ Express)", + [598] = "Continuation of trip - Afula <> Haifa - code 8 / [Egged, Dan North, Nativ Express] / (sold by Nativ Express)", + [599] = "Continuation of trip - Afula <> Haifa - code 9 / [Egged, Dan North, Nativ Express] / (sold by Nativ Express)", + [602] = "Single ride - Jerusalem extended - code 2 + continuation / [Egged, Superbus, Egged Transport]", + [603] = "Single ride - Peruzdor <> Jerusalem - code 3 + continuation / [Egged, Superbus, Egged Transport]", + [604] = "Amount of rides - Jerusalem extended - code 2 + continuation / [Egged, Superbus, Egged Transport]", + [605] = "Amount of rides - Peruzdor <> Jerusalem - code 5 + continuation / [Egged, Superbus, Egged Transport]", + [606] = "Amount of rides - Peruzdor <> Jerusalem - code 6 + continuation / [Egged, Superbus, Egged Transport]", + [607] = "Amount of 2 rides - Beitar Illit <> Jerusalem + continuation / [Egged, Kavim]", + [608] = "Amount of 10 rides - Beitar Illit <> Jerusalem + continuation / [Egged, Kavim] / (eligible for Social Security only (not including youth / senior citizen))", + [609] = "Amount of 5 rides - Beitar Illit <> Jerusalem + continuation / [Egged, Kavim]", + [610] = "Amount of 10 rides - Beitar Illit <> Jerusalem + continuation / [Egged, Kavim] / (youth/senior citizen only)", + [611] = "Annual pass: Beitar Illit - extended student / [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [612] = "Semester A pass: Beitar Illit - extended student / [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [613] = "Semester B pass: Beitar Illit - extended student / [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [614] = "Annual pass: Beitar Illit - standard student / [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [615] = "Semester A pass: Beitar Illit - standard student [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [616] = "Semester B pass: Beitar Illit - standard student [Egged, Kavim, Egged Transport, CityPass] / (students only)", + [617] = "Amount of 2 rides (Forward and return) + continuation / [Egged, Kavim] / (youth / senior citizen only)", + [620] = "Annual pass: Beit Shemesh neighborhood <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [621] = "Annual pass: Peruzdor <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [622] = "Semester A pass: Beit Shemesh neighborhood <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [623] = "Semester A pass: Peruzdor <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [624] = "Semester B pass: Beit Shemesh neighborhood <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [625] = "Semester B pass: Peruzdor <> Jerusalem - standard student / [Egged, Superbus, CityPass, Egged Transport] / (students only)", + [626] = "Annual pass: Modi\'in Illit <> Jerusalem - standard student / [Egged, Kavim, CityPass, Egged Transport] / (students only)", + [627] = "Semester A pass: Modi\'in Illit <> Jerusalem - standard student / [Egged, Kavim, CityPass, Egged Transport] / (students only)", + [628] = "Semester B pass: Modi\'in Illit <> Jerusalem - standard student / [Egged, Kavim, CityPass, Egged Transport] / (students only)", + [629] = "Month pass: Jerusalem <> Shapirim / [Egged Transport, Kavim] / (sold by Egged Transport)", + [632] = "Month pass: Beit Shemesh <> Jerusalem / [Egged, Superbus, CityPass, Egged Transport] / (sold by Superbus)", + [633] = "(obsolete) Month pass: Peruzdor <> Jerusalem / [Egged, Superbus, CityPass, Egged Transport] / (sold by Superbus)", + [634] = "(obsolete) Month pass: Modi\'in Illit <> Jerusalem / [Egged, Kavim, CityPass, Egged Transport] / (sold by Kavim)", + [636] = "(obsolete) Month pass: Beitar Illit extended youth / [Egged, Kavim, Egged Transport, CityPass] / (sold by Kavim)", + [637] = "(obsolete) Month pass: Extended - Beitar Illit <> Jerusalem / [Egged, Kavim, Egged Transport, CityPass] / (sold by Kavim)", + [638] = "(obsolete) Month pass: Standard - Beitar Illit <> Jerusalem / [Egged, Kavim, Egged Transport, CityPass] / (sold by Kavim)", + [640] = "Month pass: Jerusalem <> Gush Etziyon / [Egged, CityPass, Egged Transport] / (sold by Egged)", + [641] = "Single ride - Jerusalem shared - code 2 / [Egged, CityPass, Egged Transport]", + [642] = "Single ride - Jerusalem shared - code 3 / [CityPass, Egged Transport]", + [645] = "Amount of rides - Jerusalem shared - code 2 / [Egged, CityPass, Egged Transport]", + [646] = "Amount of rides - Jerusalem shared - code 3 / [Egged, CityPass, Egged Transport]", + [647] = "Amount of 2 rides - Jerusalem / [Egged, CityPass, Egged Transport]", + [651] = "Semester A pass: Jerusalem extended / [Egged, CityPass, Superbus] / (students only)", + [652] = "Annual pass: Jerusalem extended / [Egged, CityPass, Superbus] / (students only)", + [653] = "(obsolete) Month pass: Jerusalem extended / [Egged, CityPass, Superbus] / (valid on Superbus only)", + [654] = "Semester B pass: Jerusalem extended / [Egged, CityPass, Superbus] / (students only)", + [656] = "Annual pass: Beit Shemesh <> Jerusalem / [Egged, CityPass, Egged Transport] / (students only)", + [657] = "Semester A pass: Beit Shemesh <> Jerusalem / [Egged, CityPass, Egged Transport] / (students only)", + [658] = "Semester B pass: Beit Shemesh <> Jerusalem / [Egged, CityPass, Egged Transport] / (students only)", + [659] = "(obsolete) Month pass: Beit Shemesh Jerusalem / [Egged, CityPass, Egged Transport]", + [660] = "Amount of rides - Binyamin - code 54 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 4)", + [661] = "Amount of rides - Binyamin - code 55 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 5)", + [662] = "Amount of rides - Binyamin - code 56 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 6)", + [663] = "Amount of rides - Binyamin - code 57 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 7)", + [664] = "Amount of rides - Binyamin - code 58 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 8)", + [665] = "Amount of rides - Binyamin - code 61 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 11)", + [666] = "Amount of rides - Binyamin - code 63 + continuation / [Egged, CityPass, Egged Transport] / (50% discount - code 16)", + [670] = "Month pass for New Immigrant in Rural Settlement / [Egged, Egged Transport] / (New Immigrant in Rural Settlement profile only)", + [671] = "Month/Week/Day pass: Metropolis Jerusalem - Circular Jerusalem - Ring 1 / [Egged, Egged Transport, Superbus, CityPass, Israel Railways] / (standard, standard student, extended student)", + [672] = "Month/Week/Day pass: Metropolis Jerusalem - Circular Jerusalem Extended - Ring 1, 2.1, 2.2, 2.3 / [Egged, Egged Transport, Kavim, Superbus, CityPass, Israel Railways] / (standard, standard student, extended student)", + [673] = "Month/Week/Day pass: Metropolis Jerusalem - Circular Jerusalem + West - Ring 1, 2.1, 3.1, 3.2, 3.5 / [Egged, Egged Transport, Kavim, Superbus, Afikim, CityPass, Israel Railways] / (standard, standard student, extended student)", + [674] = "Month/Week/Day pass: Metropolis Jerusalem - Circular Jerusalem + East - Ring 1, 2.2, 2.3, 3.3, 3.4, 3.5 / [Egged, Egged Transport, Superbus, Afikim, CityPass, Israel Railways] / (standard, standard student, extended student)", + [675] = "Month/Week/Day pass: Metropolis Jerusalem: West - Ring 2.1, 3.1, 3.2, 3.5 / [Egged, Egged Transport, Superbus, Kavim, Afikim, Israel Railways] / (standard, standard student, extended student)", + [676] = "Month/Week/Day pass: Metropolis Jerusalem: East - Ring 2.2, 2.3, 3.3, 3.4, 3.5 / [Egged, Egged Transport] / (standard, standard student, extended student)", + [677] = "Month/Week/Day pass: FULL Metropolis Jerusalem - Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, 3.5 / [Egged, Egged Transport, Kavim, Superbus, Afikim, CityPass, Israel Railways] / (standard, standard student, extended student)", + [678] = "(obsolete) Month/Week/Day pass: Metropolis Jerusalem - Ring 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, 3.5 / [Egged, Egged Transport, Kavim, Superbus, Afikim, CityPass, Israel Railways] / (standard, standard student, extended student)", + [680] = "Bnei Brak <> Jerusalem - lines 400-402 / [Egged, Afikim]", + [681] = "Amount of rides - Gush Etsion - code 54 + continuation / [Egged, Egged Transport] / (50% discount - code 4)", + [682] = "Amount of rides - Gush Etsion - code 55 + continuation / [Egged, Egged Transport] / (50% discount - code 5)", + [683] = "Amount of rides - Gush Etsion - code 56 + continuation / [Egged, Egged Transport] / (50% discount - code 6)", + [684] = "Amount of rides - Gush Etsion - code 57 + continuation / [Egged, Egged Transport] / (50% discount - code 7)", + [685] = "Amount of rides - Gush Etsion - code 58 + continuation / [Egged, Egged Transport] / (50% discount - code 8)", + [690] = "Month pass: East Jerusalem / [East Jerusalem operators]", + [692] = "Month pass: East Jerusalem extended / [East Jerusalem operators]", + [693] = "Month/Week/Day pass: Metropolis Jerusalem: Jerusalem Mountains - Ring 2.1 / [Egged, Kavim, Superbus, Afikim] / (standard, standard student, extended student)", + [694] = "Month/Week/Day pass: Metropolis Jerusalem: Binyamin area only - Ring 2.2 / [Egged, Egged Transport] / (standard, standard student, extended student)", + [695] = "Month/Week/Day pass: Metropolis Jerusalem: Gush Etsion area only - Ring 2.3 / [Egged, Egged Transport] / (standard, standard student, extended student)", + [696] = "Month/Week/Day pass: Metropolis Jerusalem: Beit Shemesh area only - Ring 3.1 / [Egged, Kavim, Superbus, Afikim, Israel Railways] / (standard, standard student, extended student)", + [697] = "Month/Week/Day pass: Metropolis Jerusalem: Hebel Modi\'in area only - Ring 3.2 / [Egged, Egged Transport, Kavim, Superbus, Israel Railways] / (standard, standard student, extended student)", + [698] = "Month/Week/Day pass: Metropolis Jerusalem: Dead Sea Valley area only - Ring 3.3 / [Egged, Egged Transport] / (standard, standard student, extended student)", + [699] = "Month/Week/Day pass: Metropolis Jerusalem: Kiryat Arba area only - Ring 3.4 / [Egged, Egged Transport] / (standard, standard student, extended student)", + [701] = "(obsolete) Month pass: Ramla <> Lod / [Egged, Superbus, Kavim] / (including students)", + [702] = "(obsolete) Month pass: Be\'er Ya\'akov <> Ben-Gurion Airport / [Egged, Superbus, Kavim] / (including students)", + [703] = "(obsolete) Month pass: Ramla <> Lod <> Tel-Aviv / [Egged, Superbus, Kavim] / (including students)", + [704] = "(obsolete) Month pass: Rehovot <> Rishon Le\'Tziyon <> Ramla <> Lod / [Egged, Superbus, Kavim] / (including students)", + [705] = "(obsolete) Month pass: Lod urban / [Egged, Superbus, Kavim] / (including students)", + [706] = "(obsolete) Month pass: Lod <> Petah-Tikva / [Egged, Kavim] / (sold by Egged)", + [707] = "(obsolete) Month pass: Rehovot <> Rishon Le\'Tziyon Be\'er Ya\'akov junction / [Egged, Kavim] / (sold by Egged)", + [708] = "(obsolete) Semester B pass: Rehovot <> Tel-Aviv / [Egged, Kavim] / (students only, not valid for 5773/2013)", + [709] = "(obsolete) Month pass: Rehovot <> Tel-Aviv / [Egged, Kavim] / (sold by Egged)", + [711] = "Semester A pass: Rehovot <> Tel-Aviv / [Egged, Kavim] / (students only) (sold by Egged)", + [712] = "Semester B pass: Rehovot <> Tel-Aviv / [Egged, Kavim] / (students only) (sold by Egged)", + [713] = "Annual pass: Rehovot <> Tel-Aviv / [Egged, Kavim] / (students only) (sold by Egged)", + [724] = "Continuation of trip - Haifa - code 4 / [Egged, Dan North, Nativ Express] / (Galilee)", + [725] = "Continuation of trip - Haifa - code 5 / [Egged, Dan North, Nativ Express] / (Galilee)", + [726] = "Continuation of trip - Haifa - code 6 / [Egged, Dan North, Nativ Express] / (Galilee)", + [727] = "Continuation of trip - Haifa - code 7 / [Egged, Dan North, Nativ Express] / (Galilee)", + [728] = "Continuation of trip - Haifa - code 8 / [Egged, Dan North, Nativ Express] / (Galilee)", + [731] = "Continuation of trip - Haifa - code 11 / [Egged, Dan North, Nativ Express] / (Galilee)", + [736] = "Continuation of trip - Haifa - code 16 / [Egged, Dan North, Nativ Express] / (Galilee)", + [740] = "Month/Week/Day pass: Reform zone 501 + 502 - Eastern Galilee / [Egged, Nativ Express, Superbus, Golan Regional Council, NTT (Nazareth Travel & Tourism)] / (standard, standard student, extended student)", + [741] = "Month/Week/Day pass: FULL Metropolis Haifa + Golan Heights + Circular Kineret + Upper Galilee - Ring 1, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4 + zone 501, 502 / [Egged, Dan North, Carmelit, Nativ Express, Superbus, GB Tours, Golan Regional Council, NTT (Nazareth Travel & Tourism), NTT (United Bus Services Nazareth), Israel Railways] / (standard, standard student, extended student)", + [742] = "Month/Week/Day pass: Reform zone 501 - Central and Northern Golan Heights + Upper Galilee / [Egged, Nativ Express, Superbus, NTT (Nazareth Travel & Tourism), Golan Regional Council] / (standard, standard student, extended student)", + [743] = "Month/Week/Day pass: Reform zone 502 - Circular Kineret + Southern Golan Heights / [Egged, Nativ Express, Superbus, Golan Regional Council, NTT (Nazareth Travel & Tourism)] / (standard, standard student, extended student)", + [744] = "Month/Week/Day pass: Reform second phase, to be updated", + [745] = "Month/Week/Day pass: Reform zone 601 + 602 - Afula region + Beit She\'an region / [Superbus, Nativ Express, Egged] / (standard, standard student, extended student)", + [746] = "Month/Week/Day pass: Reform zone 602 - Beit She\'an region / [Superbus, Egged] / (standard, standard student, extended student)", + [747] = "Month/Week/Day pass: Reform zone 601 - Afula region / [Egged, Superbus, Nativ Express] / (standard, standard student, extended student)", + [748] = "Month/Week/Day pass: Reform zone 702 - Hadera region / [Kavim, Afikim, Egged, Israel Railways] / (standard, standard student, extended student)", + [749] = "Month/Week/Day pass: Reform zone 701 - Harish + Wadi Ara region / [Egged, Kavim, Metropoline, Afikim] / (standard, standard student, extended student)", + [750] = "Month/Week/Day pass: Reform zone 802 - Kiryat Gat region / [Metropoline, Nativ Express, Egged, Egged Transport] / (standard, standard student, extended student)", + [751] = "Month/Week/Day pass: Reform zone 801 - Ashkelon region / [Dan South, Metropoline, Afikim, Egged, Egged Transport, Israel Railways] / (standard, standard student, extended student)", + [752] = "Month/Week/Day pass: Reform zone 801 + 802 - Ashkelon region + Kiryat Gat region / [Dan South, Afikim, Metropoline, Nativ Express, Egged Transport, Egged, Israel Railways] / (standard, standard student, extended student)", + [753] = "Month/Week/Day pass: Dan Region - Samaria - Ring 3.4 / [Egged Transport, Afikim] / (standard, standard student, extended student)", + [754] = "Month/Week/Day pass: Metropolis Jerusalem - Gush Shila - Ring 3.5 / [Egged Transport, Egged, Afikim] / (standard, standard student, extended student)", + [755] = "Month/Week/Day pass: Reform zone 701 - Harish <> Hadera / [Afikim, Kavim, Metropoline, Egged, Israel Railways] / (standard, standard student, extended student)", + [756] = "Day pass: Metropolis Be\'er Sheva: Eilat <> Arava <> Be\'er Sheva - Ring 1, 2.2, 2.3, 3.2, 3.3, zone 811/903, 901, 902 / [Dan Be\'er Sheva, Egged, Metropoline]", + [800] = "(obsolete) [Free travel] Ashdod cluster + Egged Transport Ashdod cluster / [Afikim, Egged Transport] / (operator employees and family)", + [811] = "Semester pass: South / [Egged, Dan Be\'er Sheva, Dan South, Metrodan, Metropoline, Egged Transport, Galim (Narkis Gal)] / (students only)", + [812] = "Annual pass: South / [Egged, Dan Be\'er Sheva, Dan South, Metrodan, Metropoline, Egged Transport, Galim (Narkis Gal)] / (students only)", + [821] = "(obsolete) Amount of 2 rides - Tel-Aviv <> Be\'er Sheva / [Metropoline, Egged Transport]", + [830] = "Day pass: Cross-zone Reform + Israel Railways", + [831] = "Day pass: Cross-zone Reform + Israel Railways", + [832] = "Day pass: Cross-zone Reform + Israel Railways", + [833] = "Day pass: Cross-zone Reform + Israel Railways", + [834] = "Day pass: Cross-zone Reform + Israel Railways", + [835] = "Day pass: Cross-zone Reform + Israel Railways", + [836] = "Day pass: Cross-zone Reform + Israel Railways", + [837] = "Day pass: Cross-zone Reform + Israel Railways", + [838] = "Day pass: Cross-zone Reform + Israel Railways", + [839] = "Day pass: Cross-zone Reform + Israel Railways", + [840] = "Day pass: Cross-zone Reform + Israel Railways", + [841] = "Day pass: Cross-zone Reform + Israel Railways", + [842] = "Day pass: Cross-zone Reform + Israel Railways", + [843] = "Day pass: Cross-zone Reform + Israel Railways", + [844] = "Day pass: Cross-zone Reform + Israel Railways", + [845] = "Day pass: Cross-zone Reform + Israel Railways", + [846] = "Day pass: Cross-zone Reform + Israel Railways", + [847] = "Day pass: Cross-zone Reform + Israel Railways", + [848] = "Day pass: Cross-zone Reform + Israel Railways", + [849] = "Day pass: Cross-zone Reform + Israel Railways", + [850] = "Day pass: Cross-zone Reform + Israel Railways", + [851] = "Day pass: Cross-zone Reform + Israel Railways", + [852] = "Day pass: Cross-zone Reform + Israel Railways", + [853] = "Day pass: Cross-zone Reform + Israel Railways", + [854] = "Day pass: Cross-zone Reform + Israel Railways", + [855] = "Day pass: Cross-zone Reform + Israel Railways", + [856] = "Day pass: Cross-zone Reform + Israel Railways", + [857] = "Day pass: Cross-zone Reform + Israel Railways", + [858] = "Day pass: Cross-zone Reform + Israel Railways", + [859] = "Day pass: Cross-zone Reform + Israel Railways", + [860] = "Day pass: Cross-zone Reform + Israel Railways", + [861] = "Day pass: Cross-zone Reform + Israel Railways", + [862] = "Day pass: Cross-zone Reform + Israel Railways", + [863] = "Day pass: Cross-zone Reform + Israel Railways", + [864] = "Day pass: Cross-zone Reform + Israel Railways", + [865] = "Day pass: Cross-zone Reform + Israel Railways", + [866] = "Day pass: Cross-zone Reform + Israel Railways", + [867] = "Day pass: Cross-zone Reform + Israel Railways", + [868] = "Day pass: Cross-zone Reform + Israel Railways", + [869] = "Day pass: Cross-zone Reform + Israel Railways", + [870] = "Day pass: Cross-zone Reform + Israel Railways", + [871] = "Day pass: Cross-zone Reform + Israel Railways", + [872] = "Day pass: Cross-zone Reform + Israel Railways", + [873] = "Day pass: Cross-zone Reform + Israel Railways", + [874] = "Day pass: Cross-zone Reform + Israel Railways", + [875] = "Day pass: Cross-zone Reform + Israel Railways", + [876] = "Day pass: Cross-zone Reform + Israel Railways", + [877] = "Day pass: Cross-zone Reform + Israel Railways", + [878] = "Day pass: Cross-zone Reform + Israel Railways", + [879] = "Day pass: Cross-zone Reform + Israel Railways", + [880] = "Day pass: Cross-zone Reform + Israel Railways", + [882] = "Day pass: Cross-zone Reform + Israel Railways", + [883] = "Day pass: Cross-zone Reform + Israel Railways", + [884] = "Day pass: Cross-zone Reform + Israel Railways", + [885] = "Day pass: Cross-zone Reform + Israel Railways", + [886] = "Day pass: Cross-zone Reform + Israel Railways", + [887] = "Day pass: Cross-zone Reform + Israel Railways", + [888] = "Day pass: Cross-zone Reform + Israel Railways", + [889] = "Day pass: Cross-zone Reform + Israel Railways", + [890] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [891] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [892] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [893] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [894] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [895] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [896] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [897] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [898] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [899] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [900] = "[Free travel] Israel Railways - Free travel / [Israel Railways] / (internal)", + [901] = "(obsolete) Day pass: Israel Rail + Dan Region - senior citizen / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [902] = "(obsolete) Day pass: Israel Rail + Dan Region - standard / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [903] = "(obsolete) Month pass: Israel Rail + Dan Region - Ring 1 - senior citizen - reform / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [904] = "(obsolete) Month pass: Israel Rail + Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 - senior citizen - reform / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [905] = "(obsolete) Month pass: Israel Rail + Dan Region Ring 1 - adult - reform / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [906] = "(obsolete) Month pass: Israel Rail + Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 - adult - reform / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [907] = "(obsolete) Month pass: Israel Rail + Dan Region - Ring 1 - senior citizen - special / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [908] = "(obsolete) Month pass: Israel Rail + Dan Region - Ring 1 - senior citizen - standard / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [909] = "(obsolete) Month pass: Israel Rail + Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 - senior citizen - special / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [910] = "(obsolete) Month pass: Israel Rail + Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 - senior citizen - standard / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [911] = "(obsolete) Month pass: Israel Rail + Dan Region - Ring 1 - adult - special / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [912] = "(obsolete) Month pass: Israel Rail + Dan Region - Ring 1 - adult - standard / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [913] = "Month pass: Cross Metropolis: Kiryat Gat + Center: Dan Region (Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3) + Kiryat Gat (Zone 802) / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [914] = "(obsolete) Month pass: Israel Rail + Dan Region - Extended - Ring 1, 2.1, 2.2, 2.3 - adult - standard / [Israel Railways, Dan, Egged, Kavim, Metropoline] / (sold by Israel Railways)", + [915] = "(obsolete) Month pass: Samaria / [Israel Railways, Afikim] / (sold by Israel Railways)", + [916] = "(obsolete) Month pass: Be\'er Sheva + Dan Region / [Dan Region operators, Dan Be\'er Sheva, Metrodan, Israel Railways] / (sold by Israel Railways)", + [917] = "Month/Day pass: Dan Region + Be\'er Sheva - Dan Region: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Be\'er Sheva: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Ashkelon (Zone 801) + Kiryat Gat (Zone 802) / [Egged, Egged Transport, Dan, Dan Be\'er Sheva, Dan South, Illit, Metropoline, Kavim, Superbus, Afikim, Galim (Narkis Gal), Nativ Express, Israel Railways]", + [918] = "(obsolete) Month pass: Haifa + Dan Region / [Dan Region operators, Egged, Israel Railways] / (sold by Israel Railways)", + [919] = "Month/Day pass: Dan Region + Haifa - Dan Region: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Haifa: Ring 1, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4 + Wadi Aara (Zone 701) + Hadera (Zone 702) / [Egged, Egged Transport, Dan, Dan North, Illit, Metropoline, Kavim, Superbus, Afikim, Nativ Express, GB Tours, NTT (Nazareth Travel & Tourism), NTT (United Bus Services Nazareth), Israel Railways]", + [920] = "(obsolete) Month pass: Modi\'in + Dan Region / [Dan Region operators, Kavim, Israel Railways] / (sold by Israel Railways)", + [921] = "(obsolete) Month pass: Modi\'in + Dan Region extended / [Dan Region operators, Kavim, Israel Railways] / (sold by Israel Railways)", + [922] = "(obsolete) Month pass: Beit Shemesh + Dan Region / [Dan Region operators, Superbus, Israel Railways] / (sold by Israel Railways)", + [923] = "Month pass: Cross Metropolis: Beit Shemesh + Center: Metropolis Jerusalem (Ring 3.1) + Dan Region Extended (Ring 1, 2.1, 2.2, 2.3) / [Dan Region operators, Superbus, Israel Railways] / (sold by Israel Railways)", + [924] = "Month/Day pass: Dan Region + Jerusalem - Dan Region: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Jerusalem: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, 3.5 / [Egged, Egged Transport, Dan, Metropoline, Kavim, Superbus, CityPass, Afikim, Illit, Nativ Express, Israel Railways]", + [925] = "(obsolete) Month pass: Israel Rail + Metropolis Haifa - senior citizen / [Israel Railways, Egged] / (sold by Israel Railways)", + [926] = "(obsolete) Month pass: Israel Rail + Metropolis Haifa - adult / [Israel Railways, Egged] / (sold by Israel Railways)", + [927] = "(obsolete) 30 days: Rehovot / [Israel Railways, Egged] / (sold by Israel Railways)", + [928] = "(obsolete) Month pass: Beit Shemesh (one-sided) / [Israel Railways, Superbus] / (sold by Israel Railways)", + [929] = "(obsolete) 30 days: Yavne / [Israel Railways, Afikim]", + [930] = "(obsolete) Month pass: Modi\'in (one-sided) / [Israel Railways, Kavim]", + [931] = "(obsolete) 30 days: Ceasarea / [Israel Railways, Egged, Kavim]", + [932] = "(obsolete) 30 days: Pardes Hana / [Israel Railways, Egged, Kavim]", + [933] = "(obsolete) 30 days: Akko urban / [Israel Railways, Nativ Express]", + [934] = "(obsolete) 30 days: Akko extended / [Israel Railways, Nativ Express]", + [935] = "Month pass: Israel Rail + Shoham <> Ben-Gurion - senior citizen / [Israel Railways, Kavim] / (sold by Israel Railways)", + [936] = "Month pass: Israel Rail + Shoham <> Ben-Gurion - adult / [Israel Railways, Kavim] / (sold by Israel Railways)", + [937] = "(obsolete) Forward and return: Nazareth / [Israel Railways, NTT (Nazareth Travel & Tourism)]", + [938] = "(obsolete) Forward and return: Nazareth / [Israel Railways, NTT (Nazareth Travel & Tourism)]", + [939] = "(obsolete) 30 days: Binyamina / [Israel Railways, Kavim] / (any profile)", + [940] = "Single ticket: Israel Rail + Samaria lines / [Israel Railways, Afikim] / (sold by Israel Railways)", + [941] = "(obsolete) 30 days: Binyamina + Dan Region / [Israel Railways, Dan Region operators, Kavim] / (any profile)", + [942] = "(obsolete) 30 days: Binyamina + Dan Region extended / [Israel Railways, Dan Region operators, Kavim] / (any profile)", + [943] = "(obsolete) 30 days: Sderot / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [944] = "(obsolete) 30 days: Netivot / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [950] = "Month/Day pass: Entire country except Arava and Eilat - Dan Region: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Be\'er Sheva: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4 + Metropolis Haifa: Ring 1, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4 + Metropolis Jerusalem: Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4, 3.5 + zone 501, 502, 601, 602, 701, 702, 801, 802, 811/903 / [Dan, Dan North, Dan South, Dan Be\'er Sheva, Egged, Egged Transport, Kavim, Metropoline, Afikim, Superbus, CityPass, Golan Regional Council, Galim (Narkis Gal), GB Tours, Illit, Nativ Express, NTT (United Bus Services Nazareth), NTT (Nazareth Travel & Tourism), Israel Railways]", + [960] = "Month pass: Cross Metropolis: Kiryat Gat + South: Metropolis Be\'er Sheva (Ring 1, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3, 3.4) + Kiryat Gat (Zone 802) + Mitzpe Ramon (Zone 811/903) / [Dan South, Israel Railways] / (sold by Israel Railways)", + [962] = "Month pass: Cross Metropolis: Sharon + Binyamina + Hadera: Dan Region (Ring 2.1, 3.1) + Hadera (Zone 702) / [Egged Transport, Israel Railways] / (sold by Israel Railways)", + [964] = "Month pass: Cross Metropolis: Haifa + Netaniya: Metropolis Haifa (Ring 1, 2.2, 3.3, 3.4) + Hadera (Zone 702) + Dan Region (Ring 3.1) / [Haifa operators, Dan Region operators, Israel Railways] / (sold by Israel Railways)", + [966] = "(obsolete) Month pass: Israel Rail + Lod / [Israel Railways, Kavim, Superbus, Egged] / (sold by Israel Railways)", + [967] = "(obsolete) Month pass: Israel Rail + Ramla-Lod (extended) / [Israel Railways, Egged, Superbus] / (sold by Israel Railways)", + [968] = "(obsolete) Month pass: Israel Rail + Ashkelon / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [970] = "(obsolete) Month pass: Israel Rail + Kiryat Gat / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [972] = "(obsolete) Month pass: Israel Rail + Hadera / [Israel Railways, Egged, Nativ Express] / (sold by Israel Railways)", + [974] = "(obsolete) Month pass: Israel Rail + Beit Yehoshua/Netanya / [Israel Railways, Kavim] / (sold by Israel Railways)", + [976] = "(obsolete) Month pass: Israel Rail + Dimona-Tel Aviv / [Israel Railways, Egged] / (sold by Israel Railways)", + [978] = "(obsolete) Month pass: Israel Rail + Ashdod / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [980] = "(obsolete) Month pass: Israel Rail + Kiryat Motzkin / [Israel Railways, Egged] / (sold by Israel Railways)", + [982] = "(obsolete) Month pass: Israel Rail + Akko / [Israel Railways, Egged] / (sold by Israel Railways)", + [984] = "(obsolete) Month pass: Israel Rail + Nahariya / [Israel Railways, Nativ Express] / (sold by Israel Railways)", + [986] = "(obsolete) Month pass: Israel Rail + Hadera / [Israel Railways, Nativ Express] / (sold by Israel Railways)", + [988] = "(obsolete) Month pass: Israel Rail + Ceasarea/Pardes Hana / [Israel Railways, Nativ Express] / (sold by Israel Railways)", + [990] = "(obsolete) Month pass: Israel Rail + Binyamina / [Israel Railways, Nativ Express] / (sold by Israel Railways)", + [992] = "(obsolete) Month pass: Israel Rail + Sderot / [Israel Railways, Egged Transport] / (sold by Israel Railways)", + [997] = "Haifa Municipality Guest / [Carmelit] / (unlimited travel on Carmelit only)", + [998] = "New Haifa citizen / [Carmelit] / (unlimited travel on Carmelit only)", + [999] = "Haifa / [Carmelit] / (unlimited travel on Carmelit only)", + [1001] = "Month/Day pass: Kiryat Shmona / [Egged] / (standard, standard student, extended student)", + [1007] = "Month/Day pass: Carmiel urban / [Egged, Israel Railways] / (standard, standard student, extended student)", --overlap 563 + [1009] = "Amount of rides - Single ride / [Israel Railways]", + [1010] = "Amount of rides - Forward and return / [Israel Railways]", + [1026] = "Month pass: Pardes Hana urban / [Egged] / (standard, standard student, extended student)", + [1067] = "Month/Day pass: Rehovot, Ekron and Mazkeret Batya / [Egged] / (standard, standard student, extended student)", + [1072] = "Month/Day pass: Rehovot, Rishon Le\'Tziyon and Nes Tsiyona / [Egged] / (standard, standard student, extended student)", + [1074] = "Month/Day pass: Nes Tsiyona urban / [Egged] / (standard, standard student, extended student)", + [1085] = "Month/Day pass: Eilat / [Egged] / (standard, standard student, extended student)", + [1852] = "Six days pass: Eilat / [Egged] / (standard, standard student, extended student)" +} \ No newline at end of file diff --git a/dot_cardpeek_dir/scripts/gsm (beta).lua b/dot_cardpeek_dir/scripts/gsm (beta).lua index d4ba2b2..af22438 100644 --- a/dot_cardpeek_dir/scripts/gsm (beta).lua +++ b/dot_cardpeek_dir/scripts/gsm (beta).lua @@ -693,6 +693,20 @@ function pin_wrap(pin) return r end +function read_ki_string() + local sw,resp + local KI = ui.readline("Enter Authentication key (Ki)",256,"1111111111111111") + if KI ~= "" then + sw,resp = card.send(bytes.new(8,"A0 88 00 00 10",KI)) + if sw ~= 0x9000 then + log.print(log.ERROR,"Ki Verification failed") + ui.question("Ki string Verfication failed, halting.",{"OK"}) + return false + end + end + return true +end + local PIN local sw,resp @@ -712,7 +726,9 @@ if card.connect() then ui.question("This does not seem to be a GSM SIM card, halting.",{"OK"}) end else - gsm_map(CARD,GSM_MAP) + if read_ki_string() then + gsm_map(CARD,GSM_MAP) + end end card.disconnect() diff --git a/dot_cardpeek_dir/scripts/lib/country_codes.lua b/dot_cardpeek_dir/scripts/lib/country_codes.lua index 436f5b0..3cb94b1 100644 --- a/dot_cardpeek_dir/scripts/lib/country_codes.lua +++ b/dot_cardpeek_dir/scripts/lib/country_codes.lua @@ -1,244 +1,244 @@ COUNTRY_NUM_CODES = { [004]="Afghanistan", -[008]="Albania, People's Socialist Republic of", -[010]="Antarctica (the territory South of 60 deg S)", -[012]="Algeria, People's Democratic Republic of", +[008]="Albani", +[010]="Antarctica", +[012]="Algeria", [016]="American Samoa", -[020]="Andorra, Principality of", -[024]="Angola, Republic of", +[020]="Andorra", +[024]="Angola", [028]="Antigua and Barbuda", -[031]="Azerbaijan, Republic of", -[032]="Argentina, Argentine Republic", -[036]="Australia, Commonwealth of", -[040]="Austria, Republic of", -[044]="Bahamas, Commonwealth of the", -[048]="Bahrain, Kingdom of", -[050]="Bangladesh, People's Republic of", +[031]="Azerbaijan", +[032]="Argentina", +[036]="Australia", +[040]="Austria", +[044]="Bahamas", +[048]="Bahrain", +[050]="Bangladesh", [051]="Armenia", [052]="Barbados", -[056]="Belgium, Kingdom of", +[056]="Belgium", [060]="Bermuda", -[064]="Bhutan, Kingdom of", -[068]="Bolivia, Republic of", +[064]="Bhutan", +[068]="Bolivia", [070]="Bosnia and Herzegovina", -[072]="Botswana, Republic of", -[074]="Bouvet Island (Bouvetoya)", -[076]="Brazil, Federative Republic of", +[072]="Botswana", +[074]="Bouvet Island", +[076]="Brazil", [084]="Belize", -[086]="British Indian Ocean Territory (Chagos Archipelago)", +[086]="British Indian Ocean Territory", [090]="Solomon Islands", [092]="British Virgin Islands", [096]="Brunei Darussalam", -[100]="Bulgaria, People's Republic of", +[100]="Bulgaria", [104]="Myanmar", -[108]="Burundi, Republic of", +[108]="Burundi", [112]="Belarus", -[116]="Cambodia, Kingdom of", -[120]="Cameroon, United Republic of", +[116]="Cambodia", +[120]="Cameroon", [124]="Canada", -[132]="Cape Verde, Republic of", +[132]="Cape Verde", [136]="Cayman Islands", [140]="Central African Republic", -[144]="Sri Lanka, Democratic Socialist Republic of", -[148]="Chad, Republic of", -[152]="Chile, Republic of", -[156]="China, People's Republic of", -[158]="Taiwan, Province of China", +[144]="Sri Lanka", +[148]="Chad", +[152]="Chile", +[156]="China", +[158]="Taiwan", [162]="Christmas Island", [166]="Cocos (Keeling) Islands", -[170]="Colombia, Republic of", -[174]="Comoros, Union of the", +[170]="Colombia", +[174]="Comoros", [175]="Mayotte", -[178]="Congo, People's Republic of", -[180]="Congo, Democratic Republic of", +[178]="Congo PR", +[180]="Congo DR", [184]="Cook Islands", -[188]="Costa Rica, Republic of", -[191]="Hrvatska (Croatia)", -[192]="Cuba, Republic of", -[196]="Cyprus, Republic of", +[188]="Costa Rica", +[191]="Croatia", +[192]="Cuba", +[196]="Cyprus", [203]="Czech Republic", -[204]="Benin, People's Republic of", -[208]="Denmark, Kingdom of", -[212]="Dominica, Commonwealth of", +[204]="Benin", +[208]="Denmark", +[212]="Dominica", [214]="Dominican Republic", -[218]="Ecuador, Republic of", -[222]="El Salvador, Republic of", -[226]="Equatorial Guinea, Republic of", +[218]="Ecuador", +[222]="El Salvador", +[226]="Equatorial Guinea", [231]="Ethiopia", [232]="Eritrea", [233]="Estonia", [234]="Faeroe Islands", [238]="Falkland Islands (Malvinas)", [239]="South Georgia and the South Sandwich Islands", -[242]="Fiji, Republic of the Fiji Islands", -[246]="Finland, Republic of", -[250]="France, French Republic", +[242]="Fiji", +[246]="Finland", +[250]="France", [254]="French Guiana", [258]="French Polynesia", [260]="French Southern Territories", -[262]="Djibouti, Republic of", -[266]="Gabon, Gabonese Republic", +[262]="Djibouti", +[266]="Gabon", [268]="Georgia", -[270]="Gambia, Republic of the", -[275]="Palestinian Territory, Occupied", +[270]="Gambia", +[275]="Palestinia", [276]="Germany", -[288]="Ghana, Republic of", +[288]="Ghana", [292]="Gibraltar", -[296]="Kiribati, Republic of", -[300]="Greece, Hellenic Republic", +[296]="Kiribati", +[300]="Greece", [304]="Greenland", [308]="Grenada", [312]="Guadaloupe", [316]="Guam", -[320]="Guatemala, Republic of", -[324]="Guinea, Revolutionary People's Rep'c of", -[328]="Guyana, Republic of", -[332]="Haiti, Republic of", +[320]="Guatemala", +[324]="Guinea", +[328]="Guyana", +[332]="Haiti", [334]="Heard and McDonald Islands", -[336]="Holy See (Vatican City State)", -[340]="Honduras, Republic of", -[344]="Hong Kong, Special Administrative Region of China", -[348]="Hungary, Hungarian People's Republic", -[352]="Iceland, Republic of", -[356]="India, Republic of", -[360]="Indonesia, Republic of", -[364]="Iran, Islamic Republic of", -[368]="Iraq, Republic of", +[336]="Vatican", +[340]="Honduras", +[344]="Hong Kong", +[348]="Hungary", +[352]="Iceland", +[356]="India", +[360]="Indonesia", +[364]="Iran", +[368]="Iraq", [372]="Ireland", -[376]="Israel, State of", -[380]="Italy, Italian Republic", -[384]="Cote D'Ivoire, Ivory Coast, Republic of the", +[376]="Israel", +[380]="Italy", +[384]="Cote D'Ivoire", [388]="Jamaica", [392]="Japan", -[398]="Kazakhstan, Republic of", -[400]="Jordan, Hashemite Kingdom of", -[404]="Kenya, Republic of", -[408]="Korea, Democratic People's Republic of", -[410]="Korea, Republic of", -[414]="Kuwait, State of", +[398]="Kazakhstan", +[400]="Jordan", +[404]="Kenya", +[408]="North Korea", +[410]="South Korea", +[414]="Kuwait", [417]="Kyrgyz Republic", -[418]="Lao People's Democratic Republic", -[422]="Lebanon, Lebanese Republic", -[426]="Lesotho, Kingdom of", +[418]="Laos", +[422]="Lebanon", +[426]="Lesotho", [428]="Latvia", -[430]="Liberia, Republic of", -[434]="Libyan Arab Jamahiriya", -[438]="Liechtenstein, Principality of", +[430]="Liberia", +[434]="Libya", +[438]="Liechtenstein", [440]="Lithuania", -[442]="Luxembourg, Grand Duchy of", -[446]="Macao, Special Administrative Region of China", -[450]="Madagascar, Republic of", -[454]="Malawi, Republic of", +[442]="Luxembourg", +[446]="Macao", +[450]="Madagascar", +[454]="Malawi", [458]="Malaysia", -[462]="Maldives, Republic of", -[466]="Mali, Republic of", -[470]="Malta, Republic of", +[462]="Maldives", +[466]="Mali", +[470]="Malta", [474]="Martinique", -[478]="Mauritania, Islamic Republic of", +[478]="Mauritania", [480]="Mauritius", -[484]="Mexico, United Mexican States", -[492]="Monaco, Principality of", -[496]="Mongolia, Mongolian People's Republic", -[498]="Moldova, Republic of", +[484]="Mexico", +[492]="Monaco", +[496]="Mongolia", +[498]="Moldova", [500]="Montserrat", -[504]="Morocco, Kingdom of", -[508]="Mozambique, People's Republic of", -[512]="Oman, Sultanate of", +[504]="Morocco", +[508]="Mozambique", +[512]="Oman", [516]="Namibia", -[520]="Nauru, Republic of", -[524]="Nepal, Kingdom of", -[528]="Netherlands, Kingdom of the", +[520]="Nauru", +[524]="Nepal", +[528]="Netherlands", [530]="Netherlands Antilles", [533]="Aruba", [540]="New Caledonia", [548]="Vanuatu", [554]="New Zealand", -[558]="Nicaragua, Republic of", -[562]="Niger, Republic of the", -[566]="Nigeria, Federal Republic of", -[570]="Niue, Republic of", +[558]="Nicaragua", +[562]="Niger", +[566]="Nigeria", +[570]="Niue", [574]="Norfolk Island", -[578]="Norway, Kingdom of", +[578]="Norway", [580]="Northern Mariana Islands", [581]="United States Minor Outlying Islands", -[583]="Micronesia, Federated States of", +[583]="Micronesia", [584]="Marshall Islands", [585]="Palau", -[586]="Pakistan, Islamic Republic of", -[591]="Panama, Republic of", +[586]="Pakistan", +[591]="Panama", [598]="Papua New Guinea", -[600]="Paraguay, Republic of", -[604]="Peru, Republic of", -[608]="Philippines, Republic of the", +[600]="Paraguay", +[604]="Peru", +[608]="Philippines", [612]="Pitcairn Island", -[616]="Poland, Polish People's Republic", -[620]="Portugal, Portuguese Republic", -[624]="Guinea-Bissau, Republic of", -[626]="Timor-Leste, Democratic Republic of", +[616]="Poland", +[620]="Portugal", +[624]="Guinea-Bissau", +[626]="Timor-Leste", [630]="Puerto Rico", -[634]="Qatar, State of", +[634]="Qatar", [638]="Reunion", -[642]="Romania, Socialist Republic of", -[643]="Russian Federation", -[646]="Rwanda, Rwandese Republic", +[642]="Romania", +[643]="Russia", +[646]="Rwanda", [654]="St. Helena", [659]="St. Kitts and Nevis", [660]="Anguilla", [662]="St. Lucia", [666]="St. Pierre and Miquelon", [670]="St. Vincent and the Grenadines", -[674]="San Marino, Republic of", -[678]="Sao Tome and Principe, Democratic Republic of", -[682]="Saudi Arabia, Kingdom of", -[686]="Senegal, Republic of", -[690]="Seychelles, Republic of", -[694]="Sierra Leone, Republic of", -[702]="Singapore, Republic of", -[703]="Slovakia (Slovak Republic)", -[704]="Viet Nam, Socialist Republic of", +[674]="San Marino", +[678]="Sao Tome and Principe", +[682]="Saudi Arabia", +[686]="Senegal", +[690]="Seychelles", +[694]="Sierra Leone", +[702]="Singapore", +[703]="Slovakia", +[704]="Viet Nam", [705]="Slovenia", -[706]="Somalia, Somali Republic", -[710]="South Africa, Republic of", +[706]="Somalia", +[710]="South Africa", [716]="Zimbabwe", -[724]="Spain, Spanish State", +[724]="Spain", [732]="Western Sahara", -[736]="Sudan, Democratic Republic of the", -[740]="Suriname, Republic of", +[736]="Sudan", +[740]="Suriname", [744]="Svalbard & Jan Mayen Islands", -[748]="Swaziland, Kingdom of", -[752]="Sweden, Kingdom of", -[756]="Switzerland, Swiss Confederation", -[760]="Syrian Arab Republic", +[748]="Swaziland", +[752]="Sweden", +[756]="Switzerland", +[760]="Syria", [762]="Tajikistan", -[764]="Thailand, Kingdom of", -[768]="Togo, Togolese Republic", -[772]="Tokelau (Tokelau Islands)", -[776]="Tonga, Kingdom of", -[780]="Trinidad and Tobago, Republic of", +[764]="Thailand", +[768]="Togo", +[772]="Tokelau", +[776]="Tonga", +[780]="Trinidad and Tobago", [784]="United Arab Emirates", -[788]="Tunisia, Republic of", -[792]="Turkey, Republic of", +[788]="Tunisia", +[792]="Turkey", [795]="Turkmenistan", [796]="Turks and Caicos Islands", [798]="Tuvalu", -[800]="Uganda, Republic of", +[800]="Uganda", [804]="Ukraine", -[807]="Macedonia, the former Yugoslav Republic of", -[818]="Egypt, Arab Republic of", -[826]="United Kingdom of Great Britain & N. Ireland", -[834]="Tanzania, United Republic of", +[807]="Macedonia", +[818]="Egypt", +[826]="United Kingdom", +[834]="Tanzania", [840]="United States of America", [850]="US Virgin Islands", [854]="Burkina Faso", -[858]="Uruguay, Eastern Republic of", +[858]="Uruguay", [860]="Uzbekistan", -[862]="Venezuela, Bolivarian Republic of", +[862]="Venezuela", [876]="Wallis and Futuna Islands", -[882]="Samoa, Independent State of", +[882]="Samoa", [887]="Yemen", [891]="Serbia and Montenegro", -[894]="Zambia, Republic of" +[894]="Zambia" } function iso_country_code_name(cc) diff --git a/dot_cardpeek_dir/scripts/lib/en1545.lua b/dot_cardpeek_dir/scripts/lib/en1545.lua index 5e79fc8..24a53e3 100644 --- a/dot_cardpeek_dir/scripts/lib/en1545.lua +++ b/dot_cardpeek_dir/scripts/lib/en1545.lua @@ -19,27 +19,76 @@ require('lib.strict') require('lib.country_codes') - -EPOCH = os.time({hour=0, min=0, year=1997, month=1, sec=0, day=1}) ---EPOCH = 852073200 -date_days = 0 -function en1545_DATE(source) - date_days = EPOCH+bytes.tonumber(source)*24*3600 - return os.date("%a %x",date_days) +require('lib.network_codes') + +EPOCH = os.time({hour=0, min=0, year=1997, month=1, sec=0, day=1}) -- (localized) +local EPOCH_GMT = 852076800 -- (GMT) +local TIMEZONE_DELAY = EPOCH - EPOCH_GMT + +local _l = {} -- Local functions + +function en1545_DATE(source, ref_date) + --[[ + @param source Number of days (in a bytes object). + @param ref_date Refererence date for @source, expressed as a number of days since EPOCH in local time. + If nil, 1997-01-01 is used. + If negative, @source is a number of days UNTIL -ref_date. + ]] + ref_date = ref_date or EPOCH + local date_days + if ref_date < 0 then + date_days = (-ref_date) - bytes.tonumber(source) * 24 * 3600 + else + date_days = ref_date + bytes.tonumber(source) * 24 * 3600 + end + return os.date("%a %x", date_days) end function en1545_TIME(source) - local date_minutes - local part = bytes.sub(source, 0, 10) -- 11 bits - part = bytes.pad_left(part,32,0) - date_minutes = date_days + bytes.tonumber(part)*60 - date_days = 0 - return os.date("%X",date_minutes) + --[[ + @param source Number of minutes (11 first bits of a bytes object). + ]] + local part = bytes.sub(source, 0, 10) + part = bytes.pad_left(part, 32, 0) + + local date_seconds = TIMEZONE_DELAY + bytes.tonumber(part) * 60 + return os.date("%H:%M", date_seconds) +end + +function en1545_QUARTERS(source) + --[[ + @param source Number of quarters (7 first bits of a bytes object). + Special values: + - 96 (coding 24h00): end of service + - 127 (all bits to 1): not significant + ]] + local part = bytes.sub(source, 0, 6) + part = bytes.pad_left(part, 32, 0) + if part == 96 then + return "End of service" + elseif part == 127 then + return "Not significant" + end + + local date_seconds = TIMEZONE_DELAY + bytes.tonumber(part) * 60 * 15 + return os.date("%H:%M", date_seconds) end -function en1545_DATE_TIME(source) - local dtSeconds = EPOCH + bytes.tonumber(source) - return os.date("%c", dtSeconds) +function en1545_DATE_TIME(source, ref_date) + --[[ + @param source Number of seconds (in a bytes object). + @param ref_date Refererence date for @source, expressed as a number of days since EPOCH in local time. + If nil, 1997-01-01 is used. + If negative, @source is a number of days UNTIL -ref_date. + ]] + ref_date = ref_date or EPOCH + local date_seconds + if ref_date < 0 then + date_seconds = (-ref_date) - bytes.tonumber(source) + else + date_seconds = ref_date + bytes.tonumber(source) + end + return os.date("%c", date_seconds) end function en1545_BCD_DATE(source) @@ -57,37 +106,37 @@ ALPHA = { "-","A","B","C","D","E","F","G", "X","Y","Z","?","?","?","?"," " } function en1545_ALPHA(source) - local i - local c - local str = {""} - - for i=0,#source-4,5 do - c=bytes.tonumber(bytes.sub(source,i,i+4)) - table.insert(str,ALPHA[c+1]) - end - return table.concat(str) + local i + local c + local str = {""} + + for i=0,#source-4,5 do + c=bytes.tonumber(bytes.sub(source,i,i+4)) + table.insert(str,ALPHA[c+1]) + end + return table.concat(str) end function en1545_NETWORKID(source) - local country = bytes.sub(source, 0, 11) - local region = bytes.sub(source, 12, 23) - local country_code - local region_code - - country_code = iso_country_code_name(tonumber(country:convert(4):format("%D"))) - region_code = tonumber(region:convert(4):format("%D")) - if region_code then - return "country "..country_code.." / network "..region_code - end - return "country "..country_code + local country = bytes.sub(source, 0, 11) + local region = bytes.sub(source, 12, 23) + local country_code = tonumber(country:convert(4):format("%D")) + local region_code = tonumber(region:convert(4):format("%D")) + + local country_name = iso_country_code_name(country_code) + if region_code then + local region_name = network_code_name(country_code, region_code) + return "country "..country_name.." / network "..region_name + end + return "country "..country_name end function en1545_NUMBER(source) - return bytes.tonumber(source) + return bytes.tonumber(source) end function en1545_AMOUNT(source) - return string.format("%.2f€",bytes.tonumber(source)/100) + return string.format("%.2f€",bytes.tonumber(source)/100) end function en1545_ZONES(source) @@ -111,130 +160,243 @@ function en1545_ZONES(source) end function en1545_UNDEFINED(source) - local hex_info = source:convert(8) - return hex_info:format("0x%D") + local hex_info = source:convert(8) + return hex_info:format("0x%D") +end + +function _l.split_bitmap_subtable(subtable, bitmap_size, subtable_name) + --[[ + Split sub-table according to the size of the bitmap. + @return 2 arrays. + a) head: part of subtable considered "always present". It is applicable (non empty) only if the size of subtable is greater than the size of bitmap. + b) tail: part of subtable on which the bitmap applies. It has the same size as the bitmap. + ]] + subtable_name = subtable_name or "" + + -- List and sort all keys in the subtable + local total_count = 0 + local keys = {} + for key in pairs(subtable) do + table.insert(keys, key) + total_count = total_count + 1 + end + table.sort(keys) + + -- Do the split + local head_subtable = {} + local bitmap_subtable = {} + + if total_count > bitmap_size then + local head_size = total_count - bitmap_size + local count = 0 + for _, key in ipairs(keys) do + local value = subtable[key] + count = count + 1 + if count <= head_size then + head_subtable[count] = value + else + bitmap_subtable[count - head_size - 1] = value -- Bitmap subtable indices must start with 0 + end + end + if count ~= total_count then + error("subtable "..subtable_name.." contains "..total_count.." elements, but only "..count.." where iterated using ipairs: please ensure subtable keys are successive integers"); + end + else + bitmap_subtable = subtable + end + + return head_subtable, bitmap_subtable end en1545_BITMAP = 1 en1545_REPEAT = 2 +en1545_BITMAP_BE = 3 ---[[ - in en1545_parse_item, the "format" parameter is a table with entries containing 3 or 4 elements: +function _l.parse_item(ctx, format, data, position) + --[[ + @param format Table with entries containing 3 or 4 elements: - format[1]: the type of the entry, which is either - a) en1545_BITMAP: indicates a bitmap field (1 => field is present) - b) en1545_REPEAT: indicates the field is repeated n times. - c) en1545_XXXXXX: a function to call on the data for further processing. - - format[2]: the length of the entry in bits. + a) en1545_BITMAP: indicates a bitmap field (1 => field is present) in little-endian order (lowest bit byte = first bit) + b) en1545_BITMAP_BE : same as en1545_BITMAP but in big-endian order (lowest significant bit = last bit) + c) en1545_REPEAT: indicates the field is repeated n times. + d) en1545_XXXXXX: a function to call on the data for further processing. + - format[2]: + * for en1545_BITMAP, en1545_BITMAP_BE or en1545_XXXXXX: the length of the entry in bits. + * for en1545_REPEAT: not used. - format[3]: the name of the entry - - format[4]: used only for en1545_BITMAP, points to a sub-table of entries. + - format[4]: + * for en1545_BITMAP or en1545_BITMAP_BE: points to a sub-table of entries. + * for en1545_REPEAT: the sub-table to repeat. + * for en1545_XXXXXX: optional argument for the callback function (in addition to the "source" argument). + --]] + + local parsed = 0 + local index, item_node, bitmap_node, bitmap_size, item, alt + + if format == nil then + return 0 + end + + parsed = format[2] -- entry length + + item = bytes.sub(data, position, position + parsed - 1) + + item_node = ctx:append{ classname="item", label=format[3] } + + -- ------------------------------------------------------------------------ + if format[1] == en1545_BITMAP or format[1] == en1545_BITMAP_BE then -- entry type is bitmap + + bitmap_size = parsed + parsed = bitmap_size + + if bitmap_size > 0 then + -- Bitmap is not empty ---]] + if item == nil then + log.print(log.WARNING,"item is empty for non-empty bitmap "..format[3]) + return 0 + end -function en1545_parse_item(ctx, format, data, position, reference_index) - local parsed = 0 - local index, item_node, bitmap_node, bitmap_size, item, alt + item_node:append{ classname = "item", + label = "("..format[3].."Bitmap)", + size = #item, + val = item } - if format == nil then - return 0 + local head_subtable + local bitmap_subtable + head_subtable, bitmap_subtable = _l.split_bitmap_subtable(format[4], bitmap_size, format[3].."Bitmap") + + for index, sub in pairs(head_subtable) do + parsed = parsed + _l.parse_item(item_node, sub, data, position + parsed) + end + + local bitmap_ordered + if format[1] == en1545_BITMAP_BE then + bitmap_ordered = item + else + -- go through bit table in reverse order + bitmap_ordered = item:reverse() + end + + for index,bit in bitmap_ordered:ipairs() do + if bit==1 then + parsed = parsed + _l.parse_item(item_node, bitmap_subtable[index], data, position + parsed) + end + end + else + -- Bitmap is empty: all fields are considered as accepted + for index, sub in pairs(format[4]) do + parsed = parsed + _l.parse_item(item_node, sub, data, position + parsed) + end end - parsed = format[2] -- entry length + -- ------------------------------------------------------------------------ + elseif format[1] == en1545_REPEAT then -- entry type is repeat + if item == nil then + log.print(log.WARNING,"item is empty for repeat entry "..format[3]) + return 0 + end - item = bytes.sub(data,position,position+parsed-1) + item_node:set_attribute("val",item) + item_node:set_attribute("size",#item) + item_node:set_attribute("alt",bytes.tonumber(item)) + + for index=1,bytes.tonumber(item) do + parsed = parsed + _l.parse_item(item_node, format[4][0], data, position + parsed) + end + -- ------------------------------------------------------------------------ + else -- entry type is item if item == nil then + log.print(log.WARNING,"item is empty for entry "..format[3]) return 0 end - item_node = ctx:append{ classname="item", - label=format[3], - --[[ id=reference_index --]] } - - if format[1] == en1545_BITMAP then -- entry type is bitmap - - bitmap_size = parsed - parsed = bitmap_size - item_node:append{ classname="item", - label="("..format[3].."Bitmap)", - val=item } - - -- go through bit table in reverse order, since lsb=first bit - for index,bit in item:reverse():ipairs() do - if bit==1 then - parsed = parsed + en1545_parse_item(item_node, format[4][index], data, position+parsed, index) - end - end - - elseif format[1] == en1545_REPEAT then -- entry type is repeat - - item_node:set_attribute("val",item) - item_node:set_attribute("alt",bytes.tonumber(item)) - - for index=1,bytes.tonumber(item) do - parsed = parsed + en1545_parse_item(ctx, format[4][0], data, position+parsed, reference_index+index) - end - - else -- entry type is item - - alt = format[1](item) - if alt==nil then - item_node:remove() - else - item_node:set_attribute("val",item) - item_node:set_attribute("size",#item) - item_node:set_attribute("alt",alt) - end + -- call the callback function with the optional additional arguments + if format[4] then + if type(format[4]) == 'table' then + alt = format[1](item, unpack(format[4])) + else + alt = format[1](item, format[4]) + end + else + alt = format[1](item) + end + -- update node + if alt == nil then + item_node:remove() + else + item_node:set_attribute("val",item) + item_node:set_attribute("size",#item) + item_node:set_attribute("alt",alt) end - return parsed + + end + return parsed end -function en1545_parse(ctx, format, data) - local index - local parsed = 0 +function _l.parse(ctx, format, data) + local index + local parsed = 0 - for index=0,#format do - parsed = parsed + en1545_parse_item(ctx,format[index],data,parsed,index) - end - return parsed + for index=0,#format do + parsed = parsed + _l.parse_item(ctx, format[index], data, parsed) + end + return parsed end -function en1545_unparsed(ctx, data) - if data and bytes.tonumber(data)>0 then - ctx:append{ classname="item", - label="(remaining unparsed data)", - val=data, - alt="(binary data)" } - end +function _l.unparsed(ctx, data) + if data and bytes.tonumber(data)>0 then + ctx:append{ classname="item", + label="(remaining unparsed data)", + val=data, + alt="(binary data)" } + end end -function en1545_map(cardenv, data_type, ...) - local record_node - local bits - local i - local parsed - local block - - for file in cardenv:find({label=data_type}) do - for record_node in file:find({label="record"}) do - - if record_node==nil then break end - - bits = record_node:get_attribute("val"):convert(1) - parsed = 0 - for i,template in ipairs({...}) do - block = bytes.sub(bits,parsed) - if bytes.is_all(block,0)==false then - parsed = parsed + en1545_parse(record_node,template,block) - end - end - - en1545_unparsed(record_node,bytes.sub(bits,parsed)) +function en1545_map(cardenv, condition, ...) + --[[ + Map card records to en1545 items (as defined in _l.parse_item() function). + @param condition: + - If condition is a string, all records under the file having label "condition" will be mapped. + - If condition is an array, records with find(condition.records) under files with find(condition.file) will be mapped. + ]] + local record_node + local bits + local i + local parsed + local block + + local file_condition + local record_condition + if type(condition) == "string" then + file_condition = { label = condition } + record_condition = { label = "record" } + else + file_condition = condition.file + record_condition = condition.record + end + + for file in cardenv:find(file_condition) do + for record_node in file:find(record_condition) do + if record_node==nil then + break + end + + bits = record_node:get_attribute("val"):convert(1) + parsed = 0 + for i,template in ipairs({...}) do + block = bytes.sub(bits,parsed) + if bytes.is_all(block,0)==false then + parsed = parsed + _l.parse(record_node,template,block) end + end + + _l.unparsed(record_node,bytes.sub(bits,parsed)) + end file:set_attribute("parsed","true") end end - - diff --git a/dot_cardpeek_dir/scripts/lib/network_codes.lua b/dot_cardpeek_dir/scripts/lib/network_codes.lua new file mode 100644 index 0000000..2713c0b --- /dev/null +++ b/dot_cardpeek_dir/scripts/lib/network_codes.lua @@ -0,0 +1,29 @@ +NETWORK_CODES = { + -- ------------------------------------------------------------------------ + -- Belgium + -- ------------------------------------------------------------------------ + [56] = { + [1] = "Brussels", + }, + -- ------------------------------------------------------------------------ + -- France + -- ------------------------------------------------------------------------ + [250] = { + [000] = "Nord-Pas-de-Calais", + [502] = "Rhône-Alpes", + [901] = "ÃŽle-de-France", + [920] = "Provence-Alpes-Côte d'Azur", + [921] = "Aquitaine", + }, +} + +function network_code_name(cc, network_code) + local country_network_codes = NETWORK_CODES[cc] + if country_network_codes then + local network_name = country_network_codes[network_code] + if network_name then + return network_name + end + end + return tostring(network_code) +end diff --git a/dot_cardpeek_dir/scripts/tachograph.lua b/dot_cardpeek_dir/scripts/tachograph.lua index 3ca9a58..17d52bc 100644 --- a/dot_cardpeek_dir/scripts/tachograph.lua +++ b/dot_cardpeek_dir/scripts/tachograph.lua @@ -500,7 +500,16 @@ local TACHO_MAP = { { "cardCertificate", "item", 194, 0 } }, ["EF_CA_Certificate"] = - { { "memberStateCertificate", "item", 194, 0 + { { "memberStateCertificate", "item", 194, 0 + } }, + ["EF_Card_Certificate_GOST"] = + { { "cardCertificateGost", "item", 1000, 0 + } }, + ["EF_Key_Identificators"] = + { { "keyIdentificators", "item", 16, 0 + } }, + ["EF_CA_Certificate_GOST"] = + { { "memberStateCertificateGost", "item", 1000, 0 } }, ["EF_Identification"] = { { "CardIdentification", "record", 1, { @@ -783,7 +792,7 @@ if card.connect() then local export_signed local export_ddd - answer = ui.question("Do you whish to export content of this card in an ESM file\n(also called DDD or C1B)?", + answer = ui.question("Do you wish to export content of this card in an ESM file\n(also called DDD or C1B)?", {"Export signed data file", "Export unsigned data file", "No, don't export"}) if answer==1 then @@ -817,12 +826,18 @@ if card.connect() then ui.question("This is not a tachograph DRIVER card.\nThis script may not work on this card.",{"OK"}) end - tacho_read_file_and_store(TACHO,".C100",194,"EF_Card_Certificate","file", true, false) + tacho_read_file_and_store(TACHO,".C100",194,"EF_Card_Certificate", "file", true, false) + + tacho_read_file_and_store(TACHO,".C108",194,"EF_CA_Certificate", "file", true, false) + + tacho_read_file_and_store(TACHO,".C200",1000,"EF_Card_Certificate_GOST", "file", true, false) + + tacho_read_file_and_store(TACHO,".C201",16,"EF_Key_Identificators", "file", true, export_signed) + + tacho_read_file_and_store(TACHO,".C208",1000,"EF_CA_Certificate_GOST", "file", true, false) - tacho_read_file_and_store(TACHO,".C108",194, "EF_CA_Certificate", "file", true, false) - tacho_read_file_and_store(TACHO,".0520",143,"EF_Identification", "file", true, export_signed) - + tacho_read_file_and_store(TACHO,".050E",4,"EF_Card_Download","file", true, false) tacho_read_file_and_store(TACHO,".0521",53,"EF_Driving_Licence_info","file", true, export_signed) diff --git a/dot_cardpeek_dir/scripts/thai eID.lua b/dot_cardpeek_dir/scripts/thai eID.lua new file mode 100644 index 0000000..7ca7173 --- /dev/null +++ b/dot_cardpeek_dir/scripts/thai eID.lua @@ -0,0 +1,167 @@ +-- By David Huang +-- Based on information from https://github.com/chakphanu/ThaiNationalIDCard +-- +-- @name Thai eID +-- @description Thai national ID card. +-- @targets 0.8 + +require("lib.strict") + +-- Convert a TIS-620-encoded string to UTF-8 and remove trailing spaces +function format_string(node) + local data = node:get_attribute("val") + local s = converter:iconv(data:format("%C")) + s = string.gsub(s, " +$", "") + node:set_attribute("alt", s) + return s +end + +-- Split string with "#" as the delimiter, and label the pieces +function format_splithash(node, labels) + local s = format_string(node) + local pattern = string.rep("([^#]*)", #labels, "#") + for i,e in ipairs({string.match(s, pattern)}) do + if labels[i] then + node:append({ classname="item", label=labels[i], val=e }) + end + end +end + +function format_name(node) + local labels = { "Prefix", "First", "Middle", "Last" } + format_splithash(node, labels) +end + +function format_address(node) + local labels = { + "House Number", "Village Number (Mu Ban)", + "Lane (Trok/Soi)", "Road (Thanon)", nil, "Tambon/Khwaeng", + "Amphur/Khet", "Province" + } + format_splithash(node, labels) +end + +-- Thai ID number +function format_id(node) + local s = format_string(node) + local a, b, c, d, e + local _, _, a, b, c, d, e = string.find(s, "(.)(....)(.....)(..)(.)") + node:set_attribute("alt", string.format("%s-%s-%s-%s-%s", a, b, c, d, e)) +end + +-- Thai ID card serial number +function format_serial(node) + local s = format_string(node) + local a, b, c + local _, _, a, b, c = string.find(s, "(....)(..)(.*)") + node:set_attribute("alt", string.format("%s-%s-%s", a, b, c)) +end + +-- Convert Buddhist calendar date to Gregorian +-- Expiration date of 9999-99-99 means no expiration +-- Note that some people may only have a birth year, with no birth month +-- or day. In that case, the month and day will be "00". +function format_date(node) + local s = format_string(node) + local m, d, y + if s == "99999999" then + s = "Lifelong" + else + _, _, y, m, d = string.find(s, "(....)(..)(..)") + y = tonumber(y) + m = tonumber(m) + d = tonumber(d) + s = string.format("%04d-%02d-%02d", y-543, m, d) + end + node:set_attribute("alt", s) +end + +function format_sex(node) + local data = bytes.get(node:get_attribute("val"), 0) + local s + if data == 0x31 then + s = "Male" + elseif data == 0x32 then + s = "Female" + else + s = string.format("Unknown %c", data) + end + node:set_attribute("alt", s) +end + +function format_photo(node) + node:set_attribute("mime-type", "image/jpeg") +end + +local eid_elements = +{ -- Label, offset, length, format function + { "Citizen ID", 4, 13, format_id }, + { "Thai Name", 17, 100, format_name }, + { "Romanized Name", 117, 100, format_name } , + { "Date of Birth", 217, 8, format_date }, + { "Sex", 225, 1, format_sex }, + { "Address", 5497, 160, format_address }, + { "Date of Issue", 359, 8, format_date }, + { "Date of Expiry", 367, 8, format_date }, + { "Issuer", 246, 100, format_string }, + { "Card Number", 5657, 14, format_serial }, +-- { "Unknown", 375, 2, format_string }, -- Is 0x3031 ("01") on my cards +-- { "Unknown", 377, 2, }, +-- { "Unknown (Fingerprints?)", 5671, 279, }, + { "Photo", 379, 5118, format_photo } +} + +function thaiid_get_binary(offset, length) + return card.send(bytes.new(8, 0x80, 0xB0, bit.AND(bit.SHR(offset,8),0x7F), bit.AND(offset,0xFF), 0x02, 0x00, length)) +end + +-- Read the contents of the card into a bytestring +function thaiid_read_data(card) + local sw, resp + local data + + -- Thailand Ministry of Interior AID + sw, resp = card.select("#A000000054480001") + if sw == 0x9000 then + local offset, size, chunk + + data = bytes.new(8) + size = 5950 + chunk = 255 + + for offset = 0, size-1, chunk do + sw, resp = thaiid_get_binary(offset, math.min(chunk, size)) + if sw ~= 0x9000 then + break + end + data = data .. resp + size = size - chunk + end + end + + return data +end + +if card.connect() then + local CARD + local data + + CARD = card.tree_startup("Thai eID") + data = thaiid_read_data(card) + card.disconnect() + + -- Strings are encoded in TIS-620; need to convert to UTF-8 for Cardpeek + converter = iconv.open("TIS-620", "UTF-8") + + -- Extract info from the bytestring by offset and length + for k, v in ipairs(eid_elements) do + local element + local node + + element = bytes.sub(data, v[2], v[2]+v[3]-1) + node = CARD:append({ classname="item", label=v[1], val=element, size=v[3] }) + if type(v[4])=="function" then + v[4](node) + end + end +end diff --git a/drivers/usbserial_driver.c b/drivers/usbserial_driver.c new file mode 100644 index 0000000..dcef161 --- /dev/null +++ b/drivers/usbserial_driver.c @@ -0,0 +1,225 @@ +/********************************************************************** +* +* This file is part of Cardpeek, the smartcard reader utility. +* +* Copyright 2009 by 'L1L1' +* +* Cardpeek is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Cardpeek is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Cardpeek. If not, see . +* +*/ + +typedef struct +{ + char *dev_path; + ssize_t usbdev_fd; + LONG status; + +} serial_connection_t; + + +#define MAX_SERIAL_READ_LENGTH 270 + + +static int usbserial_error(cardreader_t *cr) +{ + serial_connection_t *serial = cr->extra_data; + close(serial->usbdev_fd); + cr->connected=0; + + log_printf(LOG_ERROR,"Reader disconnected"); + return 0; +} + +static int usbserial_connect(cardreader_t *cr, unsigned prefered_protocol) +{ + UNUSED(prefered_protocol); + serial_connection_t *serial = cr->extra_data; + + serial->usbdev_fd = open(serial->dev_path, O_RDWR | O_NOCTTY); //O_NDELAY + if(serial->usbdev_fd==-1) { + log_printf(LOG_ERROR,"Operation failed: can't open serial port"); + return 0; + } + else + cr->connected=1; + + + return 1; + return usbserial_error(cr); +} + +static BYTE read_APDU_byte(int fd, BYTE *buf, int blocking) { + int n; + if(blocking) + fcntl(fd, F_SETFL, 0); + else + fcntl(fd, F_SETFL, FNDELAY); + + n = read(fd, &buf, 1); + switch(n) { + case -1: + log_printf(LOG_ERROR,"Read byte: %x", buf); + break; + case 0: + log_printf(LOG_INFO,"EOF reached"); + break; + default: + log_printf(LOG_INFO,"Read byte: %x", buf); + } + return *buf; +} +static long send_APDU(int fd, const SCARD_IO_REQUEST *t0_t1, const unsigned char *data, DWORD size, BYTE *resp, DWORD* resp_len ) { + + int n; + n = write(fd, &data, size); + if (n < 0) + return CARDPEEK_ERROR_SW; + + + resp[0] = read_APDU_byte(fd, resp, 1); + resp[1] = read_APDU_byte(fd, resp, 0); + + return 0; +} + +static unsigned short usbserial_transmit(cardreader_t* cr, + const bytestring_t* command, + bytestring_t* result) +{ + UNUSED(command); + UNUSED(result); + + BYTE REC_DAT[MAX_SERIAL_READ_LENGTH]; + DWORD REC_LEN=MAX_SERIAL_READ_LENGTH; + + serial_connection_t *serial = cr->extra_data; + + //usbserial_error(cr); + + unsigned short SW; + +/* + +T=0 protocol +An asynchronous, character-oriented half-duplex transmission protocol. +T=1 protocol +An asynchronous, block-oriented half-duplex transmission protocol. + +*/ + +//#define SCARD_PCI_T0 (&g_rgSCardT0Pci) +//#define SCARD_PCI_T1 (&g_rgSCardT1Pci) +//extern const SCARD_IO_REQUEST g_rgSCardT0Pci, g_rgSCardT1Pci, g_rgSCardRawPci; + + if (cr->protocol==SCARD_PROTOCOL_T0) + { + serial->status = send_APDU(serial->usbdev_fd,SCARD_PCI_T0, + bytestring_get_data(command), + bytestring_get_size(command), + REC_DAT,&REC_LEN); + } + else if (cr->protocol==SCARD_PROTOCOL_T1) + { + serial->status = send_APDU(serial->usbdev_fd,SCARD_PCI_T1, + bytestring_get_data(command), + bytestring_get_size(command), + REC_DAT,&REC_LEN); + } + else + { + log_printf(LOG_ERROR,"Unknown smartcard protocol: %i",cr->protocol); + return CARDPEEK_ERROR_SW; + } + + if (serial->status!=SCARD_S_SUCCESS) + { + log_printf(LOG_ERROR,"Failed to transmit command to card: %s (error 0x%08x).", + pcsc_stringify_error(serial->status), + serial->status ); + return CARDPEEK_ERROR_SW; + } + + if (REC_LEN>=2) + { + bytestring_assign_data(result,REC_LEN-2,REC_DAT); + SW = (REC_DAT[REC_LEN-2]<<8)|REC_DAT[REC_LEN-1]; + } + else if (REC_LEN==1) + { + bytestring_clear(result); + SW = REC_DAT[0]; + } + else + { + log_printf(LOG_ERROR,"Transmited %i bytes to the card (%s), but recieved a response of length %i, without any status word included.", + bytestring_get_size(command), + pcsc_stringify_protocol(cr->protocol), + REC_LEN); + return CARDPEEK_ERROR_SW; + } + + return SW; + + +} + +static const bytestring_t* usbserial_last_atr(cardreader_t* cr) +{ + return cr->atr; +} + +static int usbserial_fail(cardreader_t* cr) +{ + UNUSED(cr); + + return 1; +} + +static void usbserial_finalize(cardreader_t* cr) +{ + free(((serial_connection_t*)cr->extra_data)->dev_path); + free(cr->extra_data); + + /* usbserial_error(cr); */ +} + +static int usbserial_initialize(cardreader_t *reader) +{ + char *path; + int pre_len=strlen("usbserial://"); + path = calloc(18+strlen(reader->name)+1-pre_len, sizeof(char)); + path = strcat(path, "/dev/serial/by-id/"); + path = strcat(path, reader->name + pre_len); + + serial_connection_t* serial = malloc(sizeof(serial_connection_t)); + memset(serial,0,sizeof(serial_connection_t)); + reader->extra_data = serial; + + log_printf(LOG_INFO,"Device path: %s", path); + + serial->dev_path = path; + + //system("stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parity -icanon min 1 time 1"); //depends on hardware, by default not needed and needs gui changes + + + reader->connect = usbserial_connect; + reader->disconnect = usbserial_error; + reader->reset = usbserial_error; + reader->transmit = usbserial_transmit; + reader->last_atr = usbserial_last_atr; + reader->fail = usbserial_fail; + reader->finalize = usbserial_finalize; + return 1; +} + diff --git a/linux/compile.sh b/linux/compile.sh new file mode 100644 index 0000000..7a0a4ed --- /dev/null +++ b/linux/compile.sh @@ -0,0 +1,4 @@ +cd .. +autoreconf -i +./configure +make diff --git a/misc.c b/misc.c index 44b2d0e..8e95a0d 100644 --- a/misc.c +++ b/misc.c @@ -125,7 +125,10 @@ void log_open_file(void) { time_t now = time(NULL); - LOGFILE = g_fopen(path_config_get_string(PATH_CONFIG_FILE_CARDPEEK_LOG),"w+"); + // changed mode from "w+"" to "w" (LOGFILE is never read, and "w+" is buggy + // on mingw glib2 versions 2.44.1-2 to 2.58.1-1 (at least) due to patch + // "0001-Use-CreateFile-on-Win32-to-make-sure-g_unlink-always.patch") + LOGFILE = g_fopen(path_config_get_string(PATH_CONFIG_FILE_CARDPEEK_LOG),"w"); if (LOGFILE) fprintf(LOGFILE,"cardpeek log start: %s",ctime(&now)); diff --git a/smartcard.c b/smartcard.c index 052afec..ddcaac7 100644 --- a/smartcard.c +++ b/smartcard.c @@ -39,6 +39,7 @@ #endif int cardmanager_search_pcsc_readers(cardmanager_t *cm); +int cardmanager_search_usbserial_readers(cardmanager_t *cm); int cardmanager_search_replay_readers(cardmanager_t *cm); /******************************************************************** @@ -49,7 +50,11 @@ cardmanager_t *cardmanager_new(void) { cardmanager_t *cm = (cardmanager_t *)malloc(sizeof(cardmanager_t)); memset(cm,0,sizeof(cardmanager_t)); + cm->readers_count=0; cardmanager_search_pcsc_readers(cm); + #if defined(__unix__) + cardmanager_search_usbserial_readers(cm); + #endif cardmanager_search_replay_readers(cm); return cm; } @@ -88,6 +93,7 @@ const char **cardmanager_reader_name_list(cardmanager_t* cm) #include "ui.h" #include "drivers/null_driver.c" #include "drivers/pcsc_driver.c" +#include "drivers/usbserial_driver.c" #include "drivers/replay_driver.c" cardreader_t* cardreader_new(const char *card_reader_name) @@ -105,6 +111,11 @@ cardreader_t* cardreader_new(const char *card_reader_name) reader->name=strdup(card_reader_name); if (pcsc_initialize(reader)==0) return NULL; } + else if (strncmp(card_reader_name,"usbserial://",12)==0) + { + reader->name=strdup(card_reader_name); + if (usbserial_initialize(reader)==0) return NULL; + } else if (strncmp(card_reader_name,"replay://",9)==0) { reader->name=strdup(card_reader_name); @@ -345,10 +356,51 @@ static int cardmanager_check_pcscd_is_running(void) return 1; } + +/* this should not be here but in usbserial_driver.c */ +int cardmanager_search_usbserial_readers(cardmanager_t *cm) +{ + unsigned r; + + a_string_t *rlist; + struct dirent **readers; + unsigned int usbserial_readers_count; + unsigned int old_readers_count; + usbserial_readers_count = scandir("/dev/serial/by-id", &readers, NULL, alphasort); + if (!usbserial_readers_count) + log_printf(LOG_WARNING, "custom error"); + old_readers_count=cm->readers_count; + cm->readers_count+=usbserial_readers_count-2; // //first two entries from scandir are '.' and '..' + + cm->readers=(char **)realloc(cm->readers,sizeof(char*)*cm->readers_count); + + rlist = a_strnew(NULL); + for (r = 2; r < usbserial_readers_count; r++) + { + a_strcpy(rlist,"usbserial://"); + a_strcat(rlist,readers[r]->d_name); + cm->readers[old_readers_count + r - 2 ]=strdup(a_strval(rlist)); + } + + + a_strfree(rlist); + for (int i = 0; i < usbserial_readers_count; i++) + { + free(readers[i]); + } + free(readers); + + log_printf(LOG_DEBUG,"Found %i usbserial readers", usbserial_readers_count-2); + log_printf(LOG_DEBUG,"Found %i readers",cm->readers_count); + + return cm->readers_count; + +} + /* this should not be here but in pcsc_driver.c */ int cardmanager_search_pcsc_readers(cardmanager_t *cm) { - DWORD dwReaders; + DWORD dwReaders = 0; char *p; LONG hcontext = 0; long status; @@ -390,7 +442,6 @@ int cardmanager_search_pcsc_readers(cardmanager_t *cm) } p=readers; - cm->readers_count=0; while (*p) { cm->readers_count++; diff --git a/ui/gtk/gui_cardview.c b/ui/gtk/gui_cardview.c index fe04608..82b5c0f 100644 --- a/ui/gtk/gui_cardview.c +++ b/ui/gtk/gui_cardview.c @@ -431,7 +431,7 @@ static GtkWidget *script_info_add(const char *path, const char *fname) si = (ScriptInfo *)g_malloc0(sizeof(ScriptInfo)); si->script_file = strdup(fname); - rtrim(line); + //rtrim(line); uninitialized yet for (i=0; i<30; i++) {