Please refer https://www.ventoy.net/en/doc_start.html for details.
+========== VentoyWeb.sh ===============
+sudo sh VentoyWeb.sh
+Normally, it will popup a web browser window.
+If not you can open your browser and visit http://127.0.0.1:24680
--- /dev/null
+#!/bin/sh
+
+print_usage() {
+ echo 'Usage: VentoyWeb.sh [ OPTION ]'
+ echo ' OPTION: (optional)'
+ echo ' -H x.x.x.x http server IP address (default is 127.0.0.1)'
+ echo ' -p PORT http server PORT (default is 24680)'
+ echo " -n don't start web browser"
+ echo ' -h print this help'
+ echo ''
+}
+
+print_err() {
+ echo ""
+ echo "$*"
+ echo ""
+}
+
+check_option() {
+ app="$1"
+ $app --help 2>&1 | grep -q "$2"
+}
+
+get_user() {
+ name=$(logname)
+ if [ -n "$name" -a "$name" != "root" ]; then
+ echo $name; return
+ fi
+
+ name=${HOME#/home/}
+ if [ -n "$name" -a "$name" != "root" ]; then
+ echo $name; return
+ fi
+}
+
+chromium_proc() {
+ app="$1"
+
+ url="http://${HOST}:${PORT}/index.html"
+
+ if check_option "$app" '[-][-]app='; then
+ su $VUSER -c "$app --app=$url >> $LOGFILE 2>&1"
+ elif check_option "$app" '[-][-]new[-]window='; then
+ su $VUSER -c "$app --new-window $url >> $LOGFILE 2>&1"
+ else
+ su $VUSER -c "$app $url >> $LOGFILE 2>&1"
+ fi
+}
+
+uid=$(id -u)
+if [ $uid -ne 0 ]; then
+ print_err "Please use sudo or run the script as root."
+ exit 1
+fi
+
+OLDDIR=$(pwd)
+
+if uname -a | egrep -q 'aarch64|arm64'; then
+ TOOLDIR=aarch64
+elif uname -a | egrep -q 'x86_64|amd64'; then
+ TOOLDIR=x86_64
+else
+ TOOLDIR=i386
+fi
+
+if [ ! -f ./tool/$TOOLDIR/V2DServer ]; then
+ if [ -f ${0%VentoyWeb.sh}/tool/$TOOLDIR/V2DServer ]; then
+ cd ${0%VentoyWeb.sh}
+ fi
+fi
+
+PATH=./tool/$TOOLDIR:$PATH
+
+if [ ! -f ./boot/boot.img ]; then
+ if [ -d ./grub ]; then
+ echo "Don't run VentoyWeb.sh here, please download the released install package, and run the script in it."
+ else
+ echo "Please run under the correct directory!"
+ fi
+ exit 1
+fi
+
+HOST="127.0.0.1"
+PORT=24680
+
+while [ -n "$1" ]; do
+ if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ print_usage
+ exit 0
+ elif [ "$1" = "-n" ]; then
+ NOWEB=1
+ elif [ "$1" = "-H" ]; then
+ shift
+ if echo $1 | grep -q '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*'; then
+ HOST="$1"
+ else
+ print_err "Invalid host $1"
+ exit 1
+ fi
+ elif [ "$1" = "-p" ]; then
+ shift
+ if [ $1 -gt 0 -a $1 -le 65535 ]; then
+ PORT="$1"
+ else
+ print_err "Invalid port $1"
+ exit 1
+ fi
+ fi
+
+ shift
+done
+
+
+if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
+ print_err "Another ventoy server is running now, please close it first."
+ exit 1
+fi
+
+VUSER=$(get_user)
+LOGFILE=log.txt
+#delete the log.txt if it's more than 8MB
+if [ -f $LOGFILE ]; then
+ logsize=$(stat -c '%s' $LOGFILE)
+ if [ $logsize -gt 8388608 ]; then
+ rm -f $LOGFILE
+ su $VUSER -c "touch $LOGFILE"
+ fi
+else
+ su $VUSER -c "touch $LOGFILE"
+fi
+
+
+
+if [ -f ./tool/$TOOLDIR/V2DServer.xz ]; then
+ xz -d ./tool/$TOOLDIR/V2DServer.xz
+ chmod +x ./tool/$TOOLDIR/V2DServer
+fi
+
+V2DServer "$HOST" "$PORT" &
+
+vtVer=$(cat ventoy/version)
+echo ""
+echo "=================================================================="
+echo " Ventoy Server $vtVer is running at http://${HOST}:${PORT} ..."
+echo "=================================================================="
+echo ""
+echo "################ Press Ctrl + C to exit ######################"
+echo ""
+
+if [ "$NOWEB" = "1" ]; then
+ echo "Please open your web browser and visit http://${HOST}:${PORT}"
+else
+ if which -a google-chrome-stable >> $LOGFILE 2>&1; then
+ chromium_proc google-chrome-stable
+ elif which -a google-chrome >> $LOGFILE 2>&1; then
+ chromium_proc google-chrome
+ elif which -a chrome >> $LOGFILE 2>&1; then
+ chromium_proc chrome
+ elif which -a browser >> $LOGFILE 2>&1; then
+ chromium_proc browser
+ elif which -a firefox >> $LOGFILE 2>&1; then
+ su $VUSER -c "firefox --no-remote \"http://${HOST}:${PORT}/index.html\""
+ else
+ echo "Please open your web browser and visit http://${HOST}:${PORT}"
+ fi
+fi
+
+if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
+ echo ""
+else
+ print_err "Ventoy Server Error! Please check log.txt."
+fi
+
+wait $!
+
+
+if [ -n "$OLDDIR" ]; then
+ CURDIR=$(pwd)
+ if [ "$CURDIR" != "$OLDDIR" ]; then
+ cd "$OLDDIR"
+ fi
+fi
sh mkloopex.sh
cd -
+cd ../LinuxGUI
+sh language.sh || exit 1
+sh build.sh
+cd -
+
LOOP=$(losetup -f)
cp $OPT ./tool $tmpdir/
rm -f $tmpdir/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
cp $OPT Ventoy2Disk.sh $tmpdir/
+cp $OPT VentoyWeb.sh $tmpdir/
cp $OPT README $tmpdir/
cp $OPT plugin $tmpdir/
cp $OPT CreatePersistentImg.sh $tmpdir/
dos2unix -q $tmpdir/Ventoy2Disk.sh
+dos2unix -q $tmpdir/VentoyWeb.sh
dos2unix -q $tmpdir/CreatePersistentImg.sh
+cp $OPT ../LinuxGUI/WebUI $tmpdir/
+sed 's/.*SCRIPT_DEL_THIS \(.*\)/\1/g' -i $tmpdir/WebUI/index.html
+
#32MB disk img
dd status=none if=$LOOP of=$tmpdir/ventoy/ventoy.disk.img bs=512 count=$VENTOY_SECTOR_NUM skip=$part2_start_sector
xz --check=crc32 $tmpdir/ventoy/ventoy.disk.img
find $tmpdir/ -type d -exec chmod 755 "{}" +
find $tmpdir/ -type f -exec chmod 644 "{}" +
chmod +x $tmpdir/Ventoy2Disk.sh
+chmod +x $tmpdir/VentoyWeb.sh
chmod +x $tmpdir/CreatePersistentImg.sh
tar -czvf ventoy-${curver}-linux.tar.gz $tmpdir
cp $OPT $LANG_DIR/languages.ini $tmpdir/ventoy/
rm -rf $tmpdir/tool
rm -f $tmpdir/*.sh
+rm -rf $tmpdir/WebUI
rm -f $tmpdir/README
--- /dev/null
+/******************************************************************************
+ * crc32.c ---- ventoy crc32
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+static uint32_t g_crc_table[256] = {
+ 0x00000000,
+ 0x77073096,
+ 0xEE0E612C,
+ 0x990951BA,
+ 0x076DC419,
+ 0x706AF48F,
+ 0xE963A535,
+ 0x9E6495A3,
+ 0x0EDB8832,
+ 0x79DCB8A4,
+ 0xE0D5E91E,
+ 0x97D2D988,
+ 0x09B64C2B,
+ 0x7EB17CBD,
+ 0xE7B82D07,
+ 0x90BF1D91,
+ 0x1DB71064,
+ 0x6AB020F2,
+ 0xF3B97148,
+ 0x84BE41DE,
+ 0x1ADAD47D,
+ 0x6DDDE4EB,
+ 0xF4D4B551,
+ 0x83D385C7,
+ 0x136C9856,
+ 0x646BA8C0,
+ 0xFD62F97A,
+ 0x8A65C9EC,
+ 0x14015C4F,
+ 0x63066CD9,
+ 0xFA0F3D63,
+ 0x8D080DF5,
+ 0x3B6E20C8,
+ 0x4C69105E,
+ 0xD56041E4,
+ 0xA2677172,
+ 0x3C03E4D1,
+ 0x4B04D447,
+ 0xD20D85FD,
+ 0xA50AB56B,
+ 0x35B5A8FA,
+ 0x42B2986C,
+ 0xDBBBC9D6,
+ 0xACBCF940,
+ 0x32D86CE3,
+ 0x45DF5C75,
+ 0xDCD60DCF,
+ 0xABD13D59,
+ 0x26D930AC,
+ 0x51DE003A,
+ 0xC8D75180,
+ 0xBFD06116,
+ 0x21B4F4B5,
+ 0x56B3C423,
+ 0xCFBA9599,
+ 0xB8BDA50F,
+ 0x2802B89E,
+ 0x5F058808,
+ 0xC60CD9B2,
+ 0xB10BE924,
+ 0x2F6F7C87,
+ 0x58684C11,
+ 0xC1611DAB,
+ 0xB6662D3D,
+ 0x76DC4190,
+ 0x01DB7106,
+ 0x98D220BC,
+ 0xEFD5102A,
+ 0x71B18589,
+ 0x06B6B51F,
+ 0x9FBFE4A5,
+ 0xE8B8D433,
+ 0x7807C9A2,
+ 0x0F00F934,
+ 0x9609A88E,
+ 0xE10E9818,
+ 0x7F6A0DBB,
+ 0x086D3D2D,
+ 0x91646C97,
+ 0xE6635C01,
+ 0x6B6B51F4,
+ 0x1C6C6162,
+ 0x856530D8,
+ 0xF262004E,
+ 0x6C0695ED,
+ 0x1B01A57B,
+ 0x8208F4C1,
+ 0xF50FC457,
+ 0x65B0D9C6,
+ 0x12B7E950,
+ 0x8BBEB8EA,
+ 0xFCB9887C,
+ 0x62DD1DDF,
+ 0x15DA2D49,
+ 0x8CD37CF3,
+ 0xFBD44C65,
+ 0x4DB26158,
+ 0x3AB551CE,
+ 0xA3BC0074,
+ 0xD4BB30E2,
+ 0x4ADFA541,
+ 0x3DD895D7,
+ 0xA4D1C46D,
+ 0xD3D6F4FB,
+ 0x4369E96A,
+ 0x346ED9FC,
+ 0xAD678846,
+ 0xDA60B8D0,
+ 0x44042D73,
+ 0x33031DE5,
+ 0xAA0A4C5F,
+ 0xDD0D7CC9,
+ 0x5005713C,
+ 0x270241AA,
+ 0xBE0B1010,
+ 0xC90C2086,
+ 0x5768B525,
+ 0x206F85B3,
+ 0xB966D409,
+ 0xCE61E49F,
+ 0x5EDEF90E,
+ 0x29D9C998,
+ 0xB0D09822,
+ 0xC7D7A8B4,
+ 0x59B33D17,
+ 0x2EB40D81,
+ 0xB7BD5C3B,
+ 0xC0BA6CAD,
+ 0xEDB88320,
+ 0x9ABFB3B6,
+ 0x03B6E20C,
+ 0x74B1D29A,
+ 0xEAD54739,
+ 0x9DD277AF,
+ 0x04DB2615,
+ 0x73DC1683,
+ 0xE3630B12,
+ 0x94643B84,
+ 0x0D6D6A3E,
+ 0x7A6A5AA8,
+ 0xE40ECF0B,
+ 0x9309FF9D,
+ 0x0A00AE27,
+ 0x7D079EB1,
+ 0xF00F9344,
+ 0x8708A3D2,
+ 0x1E01F268,
+ 0x6906C2FE,
+ 0xF762575D,
+ 0x806567CB,
+ 0x196C3671,
+ 0x6E6B06E7,
+ 0xFED41B76,
+ 0x89D32BE0,
+ 0x10DA7A5A,
+ 0x67DD4ACC,
+ 0xF9B9DF6F,
+ 0x8EBEEFF9,
+ 0x17B7BE43,
+ 0x60B08ED5,
+ 0xD6D6A3E8,
+ 0xA1D1937E,
+ 0x38D8C2C4,
+ 0x4FDFF252,
+ 0xD1BB67F1,
+ 0xA6BC5767,
+ 0x3FB506DD,
+ 0x48B2364B,
+ 0xD80D2BDA,
+ 0xAF0A1B4C,
+ 0x36034AF6,
+ 0x41047A60,
+ 0xDF60EFC3,
+ 0xA867DF55,
+ 0x316E8EEF,
+ 0x4669BE79,
+ 0xCB61B38C,
+ 0xBC66831A,
+ 0x256FD2A0,
+ 0x5268E236,
+ 0xCC0C7795,
+ 0xBB0B4703,
+ 0x220216B9,
+ 0x5505262F,
+ 0xC5BA3BBE,
+ 0xB2BD0B28,
+ 0x2BB45A92,
+ 0x5CB36A04,
+ 0xC2D7FFA7,
+ 0xB5D0CF31,
+ 0x2CD99E8B,
+ 0x5BDEAE1D,
+ 0x9B64C2B0,
+ 0xEC63F226,
+ 0x756AA39C,
+ 0x026D930A,
+ 0x9C0906A9,
+ 0xEB0E363F,
+ 0x72076785,
+ 0x05005713,
+ 0x95BF4A82,
+ 0xE2B87A14,
+ 0x7BB12BAE,
+ 0x0CB61B38,
+ 0x92D28E9B,
+ 0xE5D5BE0D,
+ 0x7CDCEFB7,
+ 0x0BDBDF21,
+ 0x86D3D2D4,
+ 0xF1D4E242,
+ 0x68DDB3F8,
+ 0x1FDA836E,
+ 0x81BE16CD,
+ 0xF6B9265B,
+ 0x6FB077E1,
+ 0x18B74777,
+ 0x88085AE6,
+ 0xFF0F6A70,
+ 0x66063BCA,
+ 0x11010B5C,
+ 0x8F659EFF,
+ 0xF862AE69,
+ 0x616BFFD3,
+ 0x166CCF45,
+ 0xA00AE278,
+ 0xD70DD2EE,
+ 0x4E048354,
+ 0x3903B3C2,
+ 0xA7672661,
+ 0xD06016F7,
+ 0x4969474D,
+ 0x3E6E77DB,
+ 0xAED16A4A,
+ 0xD9D65ADC,
+ 0x40DF0B66,
+ 0x37D83BF0,
+ 0xA9BCAE53,
+ 0xDEBB9EC5,
+ 0x47B2CF7F,
+ 0x30B5FFE9,
+ 0xBDBDF21C,
+ 0xCABAC28A,
+ 0x53B39330,
+ 0x24B4A3A6,
+ 0xBAD03605,
+ 0xCDD70693,
+ 0x54DE5729,
+ 0x23D967BF,
+ 0xB3667A2E,
+ 0xC4614AB8,
+ 0x5D681B02,
+ 0x2A6F2B94,
+ 0xB40BBE37,
+ 0xC30C8EA1,
+ 0x5A05DF1B,
+ 0x2D02EF8D
+};
+
+uint32_t ventoy_crc32(void *Buffer, uint32_t Length)
+{
+ uint32_t i;
+ uint8_t *Ptr = Buffer;
+ uint32_t Crc = 0xFFFFFFFF;
+
+ for (i = 0; i < Length; i++, Ptr++)
+ {
+ Crc = (Crc >> 8) ^ g_crc_table[(uint8_t) Crc ^ *Ptr];
+ }
+
+ return Crc ^ 0xffffffff;
+}
+
--- /dev/null
+/******************************************************************************
+ * ventoy_define.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_DEFINE_H__
+#define __VENTOY_DEFINE_H__
+
+#define MAX_DISK_NUM 256
+
+#define SIZE_1MB 1048576
+#define SIZE_1GB 1073741824
+
+#define VTOYIMG_PART_START_BYTES (1024 * 1024)
+#define VTOYIMG_PART_START_SECTOR 2048
+
+#define VTOYEFI_PART_BYTES (32 * 1024 * 1024)
+#define VTOYEFI_PART_SECTORS 65536
+
+#define VTOY_LOG_FILE "log.txt"
+
+
+#pragma pack(1)
+
+typedef struct vtoy_guid
+{
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+}vtoy_guid;
+
+typedef struct PART_TABLE
+{
+ uint8_t Active; // 0x00 0x80
+
+ uint8_t StartHead;
+ uint16_t StartSector : 6;
+ uint16_t StartCylinder : 10;
+
+ uint8_t FsFlag;
+
+ uint8_t EndHead;
+ uint16_t EndSector : 6;
+ uint16_t EndCylinder : 10;
+
+ uint32_t StartSectorId;
+ uint32_t SectorCount;
+}PART_TABLE;
+
+typedef struct MBR_HEAD
+{
+ uint8_t BootCode[446];
+ PART_TABLE PartTbl[4];
+ uint8_t Byte55;
+ uint8_t ByteAA;
+}MBR_HEAD;
+
+typedef struct VTOY_GPT_HDR
+{
+ char Signature[8]; /* EFI PART */
+ uint8_t Version[4];
+ uint32_t Length;
+ uint32_t Crc;
+ uint8_t Reserved1[4];
+ uint64_t EfiStartLBA;
+ uint64_t EfiBackupLBA;
+ uint64_t PartAreaStartLBA;
+ uint64_t PartAreaEndLBA;
+ vtoy_guid DiskGuid;
+ uint64_t PartTblStartLBA;
+ uint32_t PartTblTotNum;
+ uint32_t PartTblEntryLen;
+ uint32_t PartTblCrc;
+ uint8_t Reserved2[420];
+}VTOY_GPT_HDR;
+
+typedef struct VTOY_GPT_PART_TBL
+{
+ vtoy_guid PartType;
+ vtoy_guid PartGuid;
+ uint64_t StartLBA;
+ uint64_t LastLBA;
+ uint64_t Attr;
+ uint16_t Name[36];
+}VTOY_GPT_PART_TBL;
+
+typedef struct VTOY_GPT_INFO
+{
+ MBR_HEAD MBR;
+ VTOY_GPT_HDR Head;
+ VTOY_GPT_PART_TBL PartTbl[128];
+}VTOY_GPT_INFO;
+#pragma pack()
+
+
+#define MBR_PART_STYLE 0
+#define GPT_PART_STYLE 1
+
+typedef struct disk_ventoy_data
+{
+ int ventoy_valid;
+
+ char ventoy_ver[32]; // 1.0.33 ...
+ int secure_boot_flag;
+ uint64_t preserved_space;
+
+ uint64_t part2_start_sector;
+
+ int partition_style; // MBR_PART_STYLE/GPT_PART_STYLE
+ VTOY_GPT_INFO gptinfo;
+ uint8_t rsvdata[4096];
+}disk_ventoy_data;
+
+
+typedef struct ventoy_disk
+{
+ char disk_name[32]; // sda
+ char disk_path[64]; // /dev/sda
+
+ char part1_name[32]; // sda1
+ char part1_path[64]; // /dev/sda1
+ char part2_name[32]; // sda2
+ char part2_path[64]; // /dev/sda2
+
+ char disk_model[256]; // Sandisk/Kingston ...
+ char human_readable_size[32];
+
+ int major;
+ int minor;
+ int type;
+ int partstyle;
+ uint64_t size_in_byte;
+
+ disk_ventoy_data vtoydata;
+}ventoy_disk;
+
+#pragma pack(1)
+typedef struct ventoy_guid
+{
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+}ventoy_guid;
+#pragma pack()
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define VLOG_LOG 1
+#define VLOG_DEBUG 2
+
+#define ulong unsigned long
+#define _ll long long
+#define _ull unsigned long long
+#define strlcpy(dst, src) strncpy(dst, src, sizeof(dst) - 1)
+#define scnprintf(dst, fmt, args...) snprintf(dst, sizeof(dst) - 1, fmt, ##args)
+
+#define vlog(fmt, args...) ventoy_syslog(VLOG_LOG, fmt, ##args)
+#define vdebug(fmt, args...) ventoy_syslog(VLOG_DEBUG, fmt, ##args)
+
+void ventoy_syslog(int level, const char *Fmt, ...);
+void ventoy_set_loglevel(int level);
+uint32_t ventoy_crc32(void *Buffer, uint32_t Length);
+
+
+static inline void * zalloc(size_t n)
+{
+ void *p = malloc(n);
+ if (p) memset(p, 0, n);
+ return p;
+}
+
+
+#endif /* __VENTOY_DEFINE_H__ */
+
--- /dev/null
+/******************************************************************************
+ * ventoy_disk.c ---- ventoy disk
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <time.h>
+#include <ventoy_define.h>
+#include <ventoy_disk.h>
+#include <ventoy_util.h>
+#include <fat_filelib.h>
+
+int g_disk_num = 0;
+static int g_fatlib_media_fd = 0;
+static uint64_t g_fatlib_media_offset = 0;
+ventoy_disk *g_disk_list = NULL;
+
+static const char *g_ventoy_dev_type_str[VTOY_DEVICE_END] =
+{
+ "unknown", "scsi", "USB", "ide", "dac960",
+ "cpqarray", "file", "ataraid", "i2o",
+ "ubd", "dasd", "viodasd", "sx8", "dm",
+ "xvd", "sd/mmc", "virtblk", "aoe",
+ "md", "loopback", "nvme", "brd", "pmem"
+};
+
+static const char * ventoy_get_dev_type_name(ventoy_dev_type type)
+{
+ return (type < VTOY_DEVICE_END) ? g_ventoy_dev_type_str[type] : "unknown";
+}
+
+static int ventoy_check_blk_major(int major, const char *type)
+{
+ int flag = 0;
+ int valid = 0;
+ int devnum = 0;
+ int len = 0;
+ char line[64];
+ char *pos = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen("/proc/devices", "r");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ len = (int)strlen(type);
+ while (fgets(line, sizeof(line), fp))
+ {
+ if (flag)
+ {
+ pos = strchr(line, ' ');
+ if (pos)
+ {
+ devnum = (int)strtol(line, NULL, 10);
+ if (devnum == major)
+ {
+ if (strncmp(pos + 1, type, len) == 0)
+ {
+ valid = 1;
+ }
+ break;
+ }
+ }
+ }
+ else if (strncmp(line, "Block devices:", 14) == 0)
+ {
+ flag = 1;
+ }
+ }
+
+ fclose(fp);
+ return valid;
+}
+
+static int ventoy_get_disk_devnum(const char *name, int *major, int* minor)
+{
+ int rc;
+ char *pos;
+ char devnum[16] = {0};
+
+ rc = ventoy_get_sys_file_line(devnum, sizeof(devnum), "/sys/block/%s/dev", name);
+ if (rc)
+ {
+ return 1;
+ }
+
+ pos = strstr(devnum, ":");
+ if (!pos)
+ {
+ return 1;
+ }
+
+ *major = (int)strtol(devnum, NULL, 10);
+ *minor = (int)strtol(pos + 1, NULL, 10);
+
+ return 0;
+}
+
+static ventoy_dev_type ventoy_get_dev_type(const char *name, int major, int minor)
+{
+ int rc;
+ char syspath[128];
+ char dstpath[256];
+
+ memset(syspath, 0, sizeof(syspath));
+ memset(dstpath, 0, sizeof(dstpath));
+
+ scnprintf(syspath, "/sys/block/%s", name);
+ rc = readlink(syspath, dstpath, sizeof(dstpath) - 1);
+ if (rc > 0 && strstr(dstpath, "/usb"))
+ {
+ return VTOY_DEVICE_USB;
+ }
+
+ if (SCSI_BLK_MAJOR(major) && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_SCSI;
+ }
+ else if (IDE_BLK_MAJOR(major) && (minor % 0x40 == 0))
+ {
+ return VTOY_DEVICE_IDE;
+ }
+ else if (major == DAC960_MAJOR && (minor % 0x8 == 0))
+ {
+ return VTOY_DEVICE_DAC960;
+ }
+ else if (major == ATARAID_MAJOR && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_ATARAID;
+ }
+ else if (major == AOE_MAJOR && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_AOE;
+ }
+ else if (major == DASD_MAJOR && (minor % 0x4 == 0))
+ {
+ return VTOY_DEVICE_DASD;
+ }
+ else if (major == VIODASD_MAJOR && (minor % 0x8 == 0))
+ {
+ return VTOY_DEVICE_VIODASD;
+ }
+ else if (SX8_BLK_MAJOR(major) && (minor % 0x20 == 0))
+ {
+ return VTOY_DEVICE_SX8;
+ }
+ else if (I2O_BLK_MAJOR(major) && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_I2O;
+ }
+ else if (CPQARRAY_BLK_MAJOR(major) && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_CPQARRAY;
+ }
+ else if (UBD_MAJOR == major && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_UBD;
+ }
+ else if (XVD_MAJOR == major && (minor % 0x10 == 0))
+ {
+ return VTOY_DEVICE_XVD;
+ }
+ else if (SDMMC_MAJOR == major && (minor % 0x8 == 0))
+ {
+ return VTOY_DEVICE_SDMMC;
+ }
+ else if (ventoy_check_blk_major(major, "virtblk"))
+ {
+ return VTOY_DEVICE_VIRTBLK;
+ }
+ else if (major == LOOP_MAJOR)
+ {
+ return VTOY_DEVICE_LOOP;
+ }
+ else if (major == MD_MAJOR)
+ {
+ return VTOY_DEVICE_MD;
+ }
+ else if (major == RAM_MAJOR)
+ {
+ return VTOY_DEVICE_RAM;
+ }
+ else if (strstr(name, "nvme") && ventoy_check_blk_major(major, "blkext"))
+ {
+ return VTOY_DEVICE_NVME;
+ }
+ else if (strstr(name, "pmem") && ventoy_check_blk_major(major, "blkext"))
+ {
+ return VTOY_DEVICE_PMEM;
+ }
+
+ return VTOY_DEVICE_END;
+}
+
+static int ventoy_is_possible_blkdev(const char *name)
+{
+ if (name[0] == '.')
+ {
+ return 0;
+ }
+
+ /* /dev/ramX */
+ if (name[0] == 'r' && name[1] == 'a' && name[2] == 'm')
+ {
+ return 0;
+ }
+
+ /* /dev/loopX */
+ if (name[0] == 'l' && name[1] == 'o' && name[2] == 'o' && name[3] == 'p')
+ {
+ return 0;
+ }
+
+ /* /dev/dm-X */
+ if (name[0] == 'd' && name[1] == 'm' && name[2] == '-' && isdigit(name[3]))
+ {
+ return 0;
+ }
+
+ /* /dev/srX */
+ if (name[0] == 's' && name[1] == 'r' && isdigit(name[2]))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+uint64_t ventoy_get_disk_size_in_byte(const char *disk)
+{
+ int fd;
+ int rc;
+ unsigned long long size = 0;
+ char diskpath[256] = {0};
+ char sizebuf[64] = {0};
+
+ // Try 1: get size from sysfs
+ snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", disk);
+ if (access(diskpath, F_OK) >= 0)
+ {
+ vdebug("get disk size from sysfs for %s\n", disk);
+
+ fd = open(diskpath, O_RDONLY | O_BINARY);
+ if (fd >= 0)
+ {
+ read(fd, sizebuf, sizeof(sizebuf));
+ size = strtoull(sizebuf, NULL, 10);
+ close(fd);
+ return (uint64_t)(size * 512);
+ }
+ }
+ else
+ {
+ vdebug("%s not exist \n", diskpath);
+ }
+
+ // Try 2: get size from ioctl
+ snprintf(diskpath, sizeof(diskpath) - 1, "/dev/%s", disk);
+ fd = open(diskpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ vdebug("get disk size from ioctl for %s\n", disk);
+ rc = ioctl(fd, BLKGETSIZE64, &size);
+ if (rc == -1)
+ {
+ size = 0;
+ vdebug("failed to ioctl %d\n", rc);
+ }
+ close(fd);
+ }
+ else
+ {
+ vdebug("failed to open %s %d\n", diskpath, errno);
+ }
+
+ vdebug("disk %s size %llu bytes\n", disk, size);
+ return size;
+}
+
+int ventoy_get_disk_vendor(const char *name, char *vendorbuf, int bufsize)
+{
+ return ventoy_get_sys_file_line(vendorbuf, bufsize, "/sys/block/%s/device/vendor", name);
+}
+
+int ventoy_get_disk_model(const char *name, char *modelbuf, int bufsize)
+{
+ return ventoy_get_sys_file_line(modelbuf, bufsize, "/sys/block/%s/device/model", name);
+}
+
+static int fatlib_media_sector_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+ lseek(g_fatlib_media_fd, (sector + g_fatlib_media_offset) * 512ULL, SEEK_SET);
+ read(g_fatlib_media_fd, buffer, sector_count * 512);
+
+ return 1;
+}
+
+static int fatlib_is_secure_boot_enable(void)
+{
+ void *flfile = NULL;
+
+ flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+ if (flfile)
+ {
+ fl_fclose(flfile);
+ return 1;
+ }
+ else
+ {
+ vlog("/EFI/BOOT/grubx64_real.efi not exist\n");
+ }
+
+ return 0;
+}
+
+static int fatlib_get_ventoy_version(char *verbuf, int bufsize)
+{
+ int rc = 1;
+ int size = 0;
+ char *buf = NULL;
+ char *pos = NULL;
+ char *end = NULL;
+ void *flfile = NULL;
+
+ flfile = fl_fopen("/grub/grub.cfg", "rb");
+ if (flfile)
+ {
+ fl_fseek(flfile, 0, SEEK_END);
+ size = (int)fl_ftell(flfile);
+
+ fl_fseek(flfile, 0, SEEK_SET);
+
+ buf = malloc(size + 1);
+ if (buf)
+ {
+ fl_fread(buf, 1, size, flfile);
+ buf[size] = 0;
+
+ pos = strstr(buf, "VENTOY_VERSION=");
+ if (pos)
+ {
+ pos += strlen("VENTOY_VERSION=");
+ if (*pos == '"')
+ {
+ pos++;
+ }
+
+ end = pos;
+ while (*end != 0 && *end != '"' && *end != '\r' && *end != '\n')
+ {
+ end++;
+ }
+
+ *end = 0;
+
+ snprintf(verbuf, bufsize - 1, "%s", pos);
+ rc = 0;
+ }
+ free(buf);
+ }
+
+ fl_fclose(flfile);
+ }
+ else
+ {
+ vdebug("No grub.cfg found\n");
+ }
+
+ return rc;
+}
+
+int ventoy_get_vtoy_data(ventoy_disk *info, int *ppartstyle)
+{
+ int i;
+ int fd;
+ int len;
+ int rc = 1;
+ int ret = 1;
+ int part_style;
+ uint64_t part1_start_sector;
+ uint64_t part1_sector_count;
+ uint64_t part2_start_sector;
+ uint64_t part2_sector_count;
+ uint64_t preserved_space;
+ char name[64] = {0};
+ disk_ventoy_data *vtoy = NULL;
+ VTOY_GPT_INFO *gpt = NULL;
+
+ vtoy = &(info->vtoydata);
+ gpt = &(vtoy->gptinfo);
+ memset(vtoy, 0, sizeof(disk_ventoy_data));
+
+ vdebug("ventoy_get_vtoy_data %s\n", info->disk_path);
+
+ if (info->size_in_byte < (2 * VTOYEFI_PART_BYTES))
+ {
+ vdebug("disk %s is too small %llu\n", info->disk_path, (_ull)info->size_in_byte);
+ return 1;
+ }
+
+ fd = open(info->disk_path, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ vdebug("failed to open %s %d\n", info->disk_path, errno);
+ return 1;
+ }
+
+ len = (int)read(fd, &(vtoy->gptinfo), sizeof(VTOY_GPT_INFO));
+ if (len != sizeof(VTOY_GPT_INFO))
+ {
+ vdebug("failed to read %s %d\n", info->disk_path, errno);
+ goto end;
+ }
+
+ if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
+ {
+ vdebug("Invalid mbr magic 0x%x 0x%x\n", gpt->MBR.Byte55, gpt->MBR.ByteAA);
+ goto end;
+ }
+
+ if (gpt->MBR.PartTbl[0].FsFlag == 0xEE && strncmp(gpt->Head.Signature, "EFI PART", 8) == 0)
+ {
+ part_style = GPT_PART_STYLE;
+ if (ppartstyle)
+ {
+ *ppartstyle = part_style;
+ }
+
+ if (gpt->PartTbl[0].StartLBA == 0 || gpt->PartTbl[1].StartLBA == 0)
+ {
+ vdebug("NO ventoy efi part layout <%llu %llu>\n",
+ (_ull)gpt->PartTbl[0].StartLBA,
+ (_ull)gpt->PartTbl[1].StartLBA);
+ goto end;
+ }
+
+ for (i = 0; i < 36; i++)
+ {
+ name[i] = (char)(gpt->PartTbl[1].Name[i]);
+ }
+ if (strcmp(name, "VTOYEFI"))
+ {
+ vdebug("Invalid efi part2 name <%s>\n", name);
+ goto end;
+ }
+
+ part1_start_sector = gpt->PartTbl[0].StartLBA;
+ part1_sector_count = gpt->PartTbl[0].LastLBA - part1_start_sector + 1;
+ part2_start_sector = gpt->PartTbl[1].StartLBA;
+ part2_sector_count = gpt->PartTbl[1].LastLBA - part2_start_sector + 1;
+
+ preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count + 33) * 512;
+ }
+ else
+ {
+ part_style = MBR_PART_STYLE;
+ if (ppartstyle)
+ {
+ *ppartstyle = part_style;
+ }
+
+ part1_start_sector = gpt->MBR.PartTbl[0].StartSectorId;
+ part1_sector_count = gpt->MBR.PartTbl[0].SectorCount;
+ part2_start_sector = gpt->MBR.PartTbl[1].StartSectorId;
+ part2_sector_count = gpt->MBR.PartTbl[1].SectorCount;
+
+ preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count) * 512;
+ }
+
+ if (part1_start_sector != VTOYIMG_PART_START_SECTOR ||
+ part2_sector_count != VTOYEFI_PART_SECTORS ||
+ (part1_start_sector + part1_sector_count) != part2_start_sector)
+ {
+ vdebug("Not valid ventoy partition layout [%llu %llu] [%llu %llu]\n",
+ part1_start_sector, part1_sector_count, part2_start_sector, part2_sector_count);
+ goto end;
+ }
+
+ vdebug("now check secure boot ...\n");
+
+ g_fatlib_media_fd = fd;
+ g_fatlib_media_offset = part2_start_sector;
+ fl_init();
+
+ if (0 == fl_attach_media(fatlib_media_sector_read, NULL))
+ {
+ ret = fatlib_get_ventoy_version(vtoy->ventoy_ver, sizeof(vtoy->ventoy_ver));
+ if (ret == 0 && vtoy->ventoy_ver[0])
+ {
+ vtoy->secure_boot_flag = fatlib_is_secure_boot_enable();
+ vtoy->ventoy_valid = 1;
+ }
+ else
+ {
+ vdebug("fatlib_get_ventoy_version failed %d\n", ret);
+ }
+ }
+ else
+ {
+ vdebug("fl_attach_media failed\n");
+ }
+
+ fl_shutdown();
+ g_fatlib_media_fd = -1;
+ g_fatlib_media_offset = 0;
+
+ if (0 == vtoy->ventoy_valid)
+ {
+ goto end;
+ }
+
+ lseek(fd, 2040 * 512, SEEK_SET);
+ read(fd, vtoy->rsvdata, sizeof(vtoy->rsvdata));
+
+ vtoy->preserved_space = preserved_space;
+ vtoy->partition_style = part_style;
+ vtoy->part2_start_sector = part2_start_sector;
+
+ rc = 0;
+end:
+ close(fd);
+ return rc;
+}
+
+int ventoy_get_disk_info(const char *name, ventoy_disk *info)
+{
+ char vendor[64] = {0};
+ char model[128] = {0};
+
+ vdebug("get disk info %s\n", name);
+
+ strlcpy(info->disk_name, name);
+ scnprintf(info->disk_path, "/dev/%s", name);
+
+ if (strstr(name, "nvme") || strstr(name, "mmc") || strstr(name, "nbd"))
+ {
+ scnprintf(info->part1_name, "%sp1", name);
+ scnprintf(info->part1_path, "/dev/%sp1", name);
+ scnprintf(info->part2_name, "%sp2", name);
+ scnprintf(info->part2_path, "/dev/%sp2", name);
+ }
+ else
+ {
+ scnprintf(info->part1_name, "%s1", name);
+ scnprintf(info->part1_path, "/dev/%s1", name);
+ scnprintf(info->part2_name, "%s2", name);
+ scnprintf(info->part2_path, "/dev/%s2", name);
+ }
+
+ info->size_in_byte = ventoy_get_disk_size_in_byte(name);
+
+ ventoy_get_disk_devnum(name, &info->major, &info->minor);
+ info->type = ventoy_get_dev_type(name, info->major, info->minor);
+ ventoy_get_disk_vendor(name, vendor, sizeof(vendor));
+ ventoy_get_disk_model(name, model, sizeof(model));
+
+ scnprintf(info->human_readable_size, "%llu GB", (_ull)ventoy_get_human_readable_gb(info->size_in_byte));
+ scnprintf(info->disk_model, "%s %s (%s)", vendor, model, ventoy_get_dev_type_name(info->type));
+
+ ventoy_get_vtoy_data(info, &(info->partstyle));
+
+ vdebug("disk:<%s %d:%d> model:<%s> size:%llu (%s)\n",
+ info->disk_path, info->major, info->minor, info->disk_model, info->size_in_byte, info->human_readable_size);
+
+ if (info->vtoydata.ventoy_valid)
+ {
+ vdebug("%s Ventoy:<%s> %s secureboot:%d preserve:%llu\n", info->disk_path, info->vtoydata.ventoy_ver,
+ info->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
+ info->vtoydata.secure_boot_flag, (_ull)(info->vtoydata.preserved_space));
+ }
+ else
+ {
+ vdebug("%s NO Ventoy detected\n", info->disk_path);
+ }
+
+ return 0;
+}
+
+static int ventoy_disk_compare(const ventoy_disk *disk1, const ventoy_disk *disk2)
+{
+ if (disk1->type == VTOY_DEVICE_USB && disk2->type == VTOY_DEVICE_USB)
+ {
+ return strcmp(disk1->disk_name, disk2->disk_name);
+ }
+ else if (disk1->type == VTOY_DEVICE_USB)
+ {
+ return -1;
+ }
+ else if (disk2->type == VTOY_DEVICE_USB)
+ {
+ return 1;
+ }
+ else
+ {
+ return strcmp(disk1->disk_name, disk2->disk_name);
+ }
+}
+
+static int ventoy_disk_sort(void)
+{
+ int i, j;
+ ventoy_disk *tmp;
+
+ tmp = malloc(sizeof(ventoy_disk));
+ if (!tmp)
+ {
+ return 1;
+ }
+
+ for (i = 0; i < g_disk_num; i++)
+ for (j = i + 1; j < g_disk_num; j++)
+ {
+ if (ventoy_disk_compare(g_disk_list + i, g_disk_list + j) > 0)
+ {
+ memcpy(tmp, g_disk_list + i, sizeof(ventoy_disk));
+ memcpy(g_disk_list + i, g_disk_list + j, sizeof(ventoy_disk));
+ memcpy(g_disk_list + j, tmp, sizeof(ventoy_disk));
+ }
+ }
+
+ free(tmp);
+ return 0;
+}
+
+int ventoy_disk_enumerate_all(void)
+{
+ int rc = 0;
+ DIR* dir = NULL;
+ struct dirent* p = NULL;
+
+ vdebug("ventoy_disk_enumerate_all\n");
+
+ dir = opendir("/sys/block");
+ if (!dir)
+ {
+ vlog("Failed to open /sys/block %d\n", errno);
+ return 1;
+ }
+
+ while (((p = readdir(dir)) != NULL) && (g_disk_num < MAX_DISK_NUM))
+ {
+ if (ventoy_is_possible_blkdev(p->d_name))
+ {
+ memset(g_disk_list + g_disk_num, 0, sizeof(ventoy_disk));
+ if (0 == ventoy_get_disk_info(p->d_name, g_disk_list + g_disk_num))
+ {
+ g_disk_num++;
+ }
+ }
+ }
+ closedir(dir);
+
+ ventoy_disk_sort();
+
+ return rc;
+}
+
+void ventoy_disk_dump(ventoy_disk *cur)
+{
+ if (cur->vtoydata.ventoy_valid)
+ {
+ vdebug("%s [%s] %s\tVentoy: %s %s secureboot:%d preserve:%llu\n",
+ cur->disk_path, cur->human_readable_size, cur->disk_model,
+ cur->vtoydata.ventoy_ver, cur->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
+ cur->vtoydata.secure_boot_flag, (_ull)(cur->vtoydata.preserved_space));
+ }
+ else
+ {
+ vdebug("%s [%s] %s\tVentoy: NA\n", cur->disk_path, cur->human_readable_size, cur->disk_model);
+ }
+}
+
+void ventoy_disk_dump_all(void)
+{
+ int i;
+
+ vdebug("============= DISK DUMP ============\n");
+ for (i = 0; i < g_disk_num; i++)
+ {
+ ventoy_disk_dump(g_disk_list + i);
+ }
+}
+
+int ventoy_disk_install(ventoy_disk *disk, void *efipartimg)
+{
+ return 0;
+}
+
+
+int ventoy_disk_init(void)
+{
+ g_disk_list = malloc(sizeof(ventoy_disk) * MAX_DISK_NUM);
+
+ ventoy_disk_enumerate_all();
+ ventoy_disk_dump_all();
+
+ return 0;
+}
+
+void ventoy_disk_exit(void)
+{
+ check_free(g_disk_list);
+ g_disk_list = NULL;
+ g_disk_num = 0;
+}
+
+
--- /dev/null
+/******************************************************************************
+ * ventoy_disk.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_DISK_H__
+#define __VENTOY_DISK_H__
+
+typedef enum
+{
+ VTOY_DEVICE_UNKNOWN = 0,
+ VTOY_DEVICE_SCSI,
+ VTOY_DEVICE_USB,
+ VTOY_DEVICE_IDE,
+ VTOY_DEVICE_DAC960,
+ VTOY_DEVICE_CPQARRAY,
+ VTOY_DEVICE_FILE,
+ VTOY_DEVICE_ATARAID,
+ VTOY_DEVICE_I2O,
+ VTOY_DEVICE_UBD,
+ VTOY_DEVICE_DASD,
+ VTOY_DEVICE_VIODASD,
+ VTOY_DEVICE_SX8,
+ VTOY_DEVICE_DM,
+ VTOY_DEVICE_XVD,
+ VTOY_DEVICE_SDMMC,
+ VTOY_DEVICE_VIRTBLK,
+ VTOY_DEVICE_AOE,
+ VTOY_DEVICE_MD,
+ VTOY_DEVICE_LOOP,
+ VTOY_DEVICE_NVME,
+ VTOY_DEVICE_RAM,
+ VTOY_DEVICE_PMEM,
+
+ VTOY_DEVICE_END
+}ventoy_dev_type;
+
+/* from <linux/major.h> */
+#define IDE0_MAJOR 3
+#define IDE1_MAJOR 22
+#define IDE2_MAJOR 33
+#define IDE3_MAJOR 34
+#define IDE4_MAJOR 56
+#define IDE5_MAJOR 57
+#define SCSI_CDROM_MAJOR 11
+#define SCSI_DISK0_MAJOR 8
+#define SCSI_DISK1_MAJOR 65
+#define SCSI_DISK2_MAJOR 66
+#define SCSI_DISK3_MAJOR 67
+#define SCSI_DISK4_MAJOR 68
+#define SCSI_DISK5_MAJOR 69
+#define SCSI_DISK6_MAJOR 70
+#define SCSI_DISK7_MAJOR 71
+#define SCSI_DISK8_MAJOR 128
+#define SCSI_DISK9_MAJOR 129
+#define SCSI_DISK10_MAJOR 130
+#define SCSI_DISK11_MAJOR 131
+#define SCSI_DISK12_MAJOR 132
+#define SCSI_DISK13_MAJOR 133
+#define SCSI_DISK14_MAJOR 134
+#define SCSI_DISK15_MAJOR 135
+#define COMPAQ_SMART2_MAJOR 72
+#define COMPAQ_SMART2_MAJOR1 73
+#define COMPAQ_SMART2_MAJOR2 74
+#define COMPAQ_SMART2_MAJOR3 75
+#define COMPAQ_SMART2_MAJOR4 76
+#define COMPAQ_SMART2_MAJOR5 77
+#define COMPAQ_SMART2_MAJOR6 78
+#define COMPAQ_SMART2_MAJOR7 79
+#define COMPAQ_SMART_MAJOR 104
+#define COMPAQ_SMART_MAJOR1 105
+#define COMPAQ_SMART_MAJOR2 106
+#define COMPAQ_SMART_MAJOR3 107
+#define COMPAQ_SMART_MAJOR4 108
+#define COMPAQ_SMART_MAJOR5 109
+#define COMPAQ_SMART_MAJOR6 110
+#define COMPAQ_SMART_MAJOR7 111
+#define DAC960_MAJOR 48
+#define ATARAID_MAJOR 114
+#define I2O_MAJOR1 80
+#define I2O_MAJOR2 81
+#define I2O_MAJOR3 82
+#define I2O_MAJOR4 83
+#define I2O_MAJOR5 84
+#define I2O_MAJOR6 85
+#define I2O_MAJOR7 86
+#define I2O_MAJOR8 87
+#define UBD_MAJOR 98
+#define DASD_MAJOR 94
+#define VIODASD_MAJOR 112
+#define AOE_MAJOR 152
+#define SX8_MAJOR1 160
+#define SX8_MAJOR2 161
+#define XVD_MAJOR 202
+#define SDMMC_MAJOR 179
+#define LOOP_MAJOR 7
+#define MD_MAJOR 9
+#define BLKEXT_MAJOR 259
+#define RAM_MAJOR 1
+
+#define SCSI_BLK_MAJOR(M) ( \
+ (M) == SCSI_DISK0_MAJOR \
+ || (M) == SCSI_CDROM_MAJOR \
+ || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \
+ || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
+
+#define IDE_BLK_MAJOR(M) \
+ ((M) == IDE0_MAJOR || \
+ (M) == IDE1_MAJOR || \
+ (M) == IDE2_MAJOR || \
+ (M) == IDE3_MAJOR || \
+ (M) == IDE4_MAJOR || \
+ (M) == IDE5_MAJOR)
+
+#define SX8_BLK_MAJOR(M) ((M) >= SX8_MAJOR1 && (M) <= SX8_MAJOR2)
+#define I2O_BLK_MAJOR(M) ((M) >= I2O_MAJOR1 && (M) <= I2O_MAJOR8)
+#define CPQARRAY_BLK_MAJOR(M) \
+ (((M) >= COMPAQ_SMART2_MAJOR && (M) <= COMPAQ_SMART2_MAJOR7) || \
+ (COMPAQ_SMART_MAJOR <= (M) && (M) <= COMPAQ_SMART_MAJOR7))
+
+#define VENTOY_FILE_STG1_IMG "boot/core.img.xz"
+#define VENTOY_FILE_DISK_IMG "ventoy/ventoy.disk.img.xz"
+
+extern int g_disk_num;
+extern ventoy_disk *g_disk_list;
+int ventoy_disk_enumerate_all(void);
+int ventoy_disk_init(void);
+void ventoy_disk_exit(void);
+
+#endif /* __VENTOY_DISK_H__ */
+
--- /dev/null
+/******************************************************************************\r
+ * ventoy_json.c\r
+ *\r
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License as\r
+ * published by the Free Software Foundation; either version 3 of the\r
+ * License, or (at your option) any later version.\r
+ * \r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdint.h>\r
+#include <string.h>\r
+#include <stdarg.h>\r
+#include <errno.h>\r
+#include <unistd.h>\r
+#include <ventoy_define.h>\r
+#include <ventoy_util.h>\r
+#include <ventoy_json.h>\r
+\r
+static void vtoy_json_free(VTOY_JSON *pstJsonHead)\r
+{\r
+ VTOY_JSON *pstNext = NULL;\r
+\r
+ while (NULL != pstJsonHead)\r
+ {\r
+ pstNext = pstJsonHead->pstNext;\r
+ if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))\r
+ {\r
+ vtoy_json_free(pstJsonHead->pstChild);\r
+ }\r
+\r
+ free(pstJsonHead);\r
+ pstJsonHead = pstNext;\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+static char *vtoy_json_skip(const char *pcData)\r
+{\r
+ while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))\r
+ {\r
+ pcData++;\r
+ }\r
+\r
+ return (char *)pcData;\r
+}\r
+\r
+VTOY_JSON *vtoy_json_find_item\r
+(\r
+ VTOY_JSON *pstJson,\r
+ JSON_TYPE enDataType,\r
+ const char *szKey\r
+)\r
+{\r
+ while (NULL != pstJson)\r
+ {\r
+ if ((enDataType == pstJson->enDataType) && \r
+ (0 == strcmp(szKey, pstJson->pcName)))\r
+ {\r
+ return pstJson;\r
+ }\r
+ pstJson = pstJson->pstNext;\r
+ }\r
+ \r
+ return NULL;\r
+}\r
+\r
+static int vtoy_json_parse_number\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *pcData,\r
+ const char **ppcEnd\r
+)\r
+{\r
+ unsigned long Value;\r
+\r
+ Value = strtoul(pcData, (char **)ppcEnd, 10);\r
+ if (*ppcEnd == pcData)\r
+ {\r
+ vdebug("Failed to parse json number %s.\n", pcData);\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pstJson->enDataType = JSON_TYPE_NUMBER;\r
+ pstJson->unData.lValue = Value;\r
+ \r
+ return JSON_SUCCESS;\r
+}\r
+\r
+static int vtoy_json_parse_string\r
+(\r
+ char *pcNewStart,\r
+ char *pcRawStart,\r
+ VTOY_JSON *pstJson, \r
+ const char *pcData,\r
+ const char **ppcEnd\r
+)\r
+{\r
+ uint32_t uiLen = 0;\r
+ const char *pcPos = NULL;\r
+ const char *pcTmp = pcData + 1;\r
+ \r
+ *ppcEnd = pcData;\r
+\r
+ if ('\"' != *pcData)\r
+ {\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcPos = strchr(pcTmp, '\"');\r
+ if ((NULL == pcPos) || (pcPos < pcTmp))\r
+ {\r
+ vdebug("Invalid string %s.\n", pcData);\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ *ppcEnd = pcPos + 1;\r
+ uiLen = (uint32_t)(unsigned long)(pcPos - pcTmp); \r
+ \r
+ pstJson->enDataType = JSON_TYPE_STRING;\r
+ pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);\r
+ pstJson->unData.pcStrVal[uiLen] = '\0';\r
+ \r
+ return JSON_SUCCESS;\r
+}\r
+\r
+static int vtoy_json_parse_array\r
+(\r
+ char *pcNewStart,\r
+ char *pcRawStart,\r
+ VTOY_JSON *pstJson, \r
+ const char *pcData,\r
+ const char **ppcEnd\r
+)\r
+{\r
+ int Ret = JSON_SUCCESS;\r
+ VTOY_JSON *pstJsonChild = NULL;\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ const char *pcTmp = pcData + 1;\r
+\r
+ *ppcEnd = pcData;\r
+ pstJson->enDataType = JSON_TYPE_ARRAY;\r
+\r
+ if ('[' != *pcData)\r
+ {\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcTmp = vtoy_json_skip(pcTmp);\r
+\r
+ if (']' == *pcTmp)\r
+ {\r
+ *ppcEnd = pcTmp + 1;\r
+ return JSON_SUCCESS;\r
+ }\r
+\r
+ JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);\r
+\r
+ Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pstJsonChild = pstJson->pstChild;\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ while ((NULL != pcTmp) && (',' == *pcTmp))\r
+ {\r
+ JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);\r
+ pstJsonChild->pstNext = pstJsonItem;\r
+ pstJsonItem->pstPrev = pstJsonChild;\r
+ pstJsonChild = pstJsonItem;\r
+\r
+ Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ }\r
+\r
+ if ((NULL != pcTmp) && (']' == *pcTmp))\r
+ {\r
+ *ppcEnd = pcTmp + 1;\r
+ return JSON_SUCCESS;\r
+ }\r
+ else\r
+ {\r
+ *ppcEnd = pcTmp;\r
+ return JSON_FAILED;\r
+ }\r
+}\r
+\r
+static int vtoy_json_parse_object\r
+(\r
+ char *pcNewStart,\r
+ char *pcRawStart,\r
+ VTOY_JSON *pstJson, \r
+ const char *pcData,\r
+ const char **ppcEnd\r
+)\r
+{\r
+ int Ret = JSON_SUCCESS;\r
+ VTOY_JSON *pstJsonChild = NULL;\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ const char *pcTmp = pcData + 1;\r
+\r
+ *ppcEnd = pcData;\r
+ pstJson->enDataType = JSON_TYPE_OBJECT;\r
+\r
+ if ('{' != *pcData)\r
+ {\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcTmp = vtoy_json_skip(pcTmp);\r
+ if ('}' == *pcTmp)\r
+ {\r
+ *ppcEnd = pcTmp + 1;\r
+ return JSON_SUCCESS;\r
+ }\r
+\r
+ JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);\r
+\r
+ Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pstJsonChild = pstJson->pstChild;\r
+ pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;\r
+ pstJsonChild->unData.pcStrVal = NULL;\r
+\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ if ((NULL == pcTmp) || (':' != *pcTmp))\r
+ {\r
+ *ppcEnd = pcTmp;\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ while ((NULL != pcTmp) && (',' == *pcTmp))\r
+ {\r
+ JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);\r
+ pstJsonChild->pstNext = pstJsonItem;\r
+ pstJsonItem->pstPrev = pstJsonChild;\r
+ pstJsonChild = pstJsonItem;\r
+\r
+ Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;\r
+ pstJsonChild->unData.pcStrVal = NULL;\r
+ if ((NULL == pcTmp) || (':' != *pcTmp))\r
+ {\r
+ *ppcEnd = pcTmp;\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse array child.\n");\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ pcTmp = vtoy_json_skip(*ppcEnd);\r
+ }\r
+\r
+ if ((NULL != pcTmp) && ('}' == *pcTmp))\r
+ {\r
+ *ppcEnd = pcTmp + 1;\r
+ return JSON_SUCCESS;\r
+ }\r
+ else\r
+ {\r
+ *ppcEnd = pcTmp;\r
+ return JSON_FAILED;\r
+ }\r
+}\r
+\r
+int vtoy_json_parse_value\r
+(\r
+ char *pcNewStart,\r
+ char *pcRawStart,\r
+ VTOY_JSON *pstJson, \r
+ const char *pcData,\r
+ const char **ppcEnd\r
+)\r
+{\r
+ pcData = vtoy_json_skip(pcData);\r
+ \r
+ switch (*pcData)\r
+ {\r
+ case 'n':\r
+ {\r
+ if (0 == strncmp(pcData, "null", 4))\r
+ {\r
+ pstJson->enDataType = JSON_TYPE_NULL;\r
+ *ppcEnd = pcData + 4;\r
+ return JSON_SUCCESS;\r
+ }\r
+ break;\r
+ }\r
+ case 'f':\r
+ {\r
+ if (0 == strncmp(pcData, "false", 5))\r
+ {\r
+ pstJson->enDataType = JSON_TYPE_BOOL;\r
+ pstJson->unData.lValue = 0;\r
+ *ppcEnd = pcData + 5;\r
+ return JSON_SUCCESS;\r
+ }\r
+ break;\r
+ }\r
+ case 't':\r
+ {\r
+ if (0 == strncmp(pcData, "true", 4))\r
+ {\r
+ pstJson->enDataType = JSON_TYPE_BOOL;\r
+ pstJson->unData.lValue = 1;\r
+ *ppcEnd = pcData + 4;\r
+ return JSON_SUCCESS;\r
+ }\r
+ break;\r
+ }\r
+ case '\"':\r
+ {\r
+ return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+ }\r
+ case '[':\r
+ {\r
+ return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+ }\r
+ case '{':\r
+ {\r
+ return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+ }\r
+ case '-':\r
+ {\r
+ return vtoy_json_parse_number(pstJson, pcData, ppcEnd);\r
+ }\r
+ default :\r
+ {\r
+ if (*pcData >= '0' && *pcData <= '9')\r
+ {\r
+ return vtoy_json_parse_number(pstJson, pcData, ppcEnd);\r
+ }\r
+ }\r
+ }\r
+\r
+ *ppcEnd = pcData;\r
+ vdebug("Invalid json data %u.\n", (uint8_t)(*pcData));\r
+ return JSON_FAILED;\r
+}\r
+\r
+VTOY_JSON * vtoy_json_create(void)\r
+{\r
+ VTOY_JSON *pstJson = NULL;\r
+\r
+ pstJson = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON));\r
+ if (NULL == pstJson)\r
+ {\r
+ return NULL;\r
+ }\r
+ \r
+ return pstJson;\r
+}\r
+\r
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)\r
+{\r
+ uint32_t uiMemSize = 0;\r
+ int Ret = JSON_SUCCESS;\r
+ char *pcNewBuf = NULL;\r
+ const char *pcEnd = NULL;\r
+\r
+ uiMemSize = strlen(szJsonData) + 1;\r
+ pcNewBuf = (char *)malloc(uiMemSize);\r
+ if (NULL == pcNewBuf)\r
+ {\r
+ vdebug("Failed to alloc new buf.\n");\r
+ return JSON_FAILED;\r
+ }\r
+ memcpy(pcNewBuf, szJsonData, uiMemSize);\r
+ pcNewBuf[uiMemSize - 1] = 0;\r
+\r
+ Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);\r
+ if (JSON_SUCCESS != Ret)\r
+ {\r
+ vdebug("Failed to parse json data %s start=%p, end=%p:%s.\n", \r
+ szJsonData, szJsonData, pcEnd, pcEnd);\r
+ return JSON_FAILED;\r
+ }\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_parse\r
+(\r
+ const VTOY_JSON *pstJson,\r
+ uint32_t uiParseNum,\r
+ VTOY_JSON_PARSE_S *pstJsonParse\r
+)\r
+{ \r
+ uint32_t i = 0;\r
+ const VTOY_JSON *pstJsonCur = NULL;\r
+ VTOY_JSON_PARSE_S *pstCurParse = NULL;\r
+\r
+ for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)\r
+ {\r
+ if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||\r
+ (JSON_TYPE_ARRAY == pstJsonCur->enDataType))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)\r
+ {\r
+ if (0 == strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))\r
+ { \r
+ pstCurParse = pstJsonParse + i;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (NULL == pstCurParse)\r
+ {\r
+ continue;\r
+ }\r
+ \r
+ switch (pstJsonCur->enDataType)\r
+ {\r
+ case JSON_TYPE_NUMBER:\r
+ {\r
+ if (sizeof(uint32_t) == pstCurParse->uiBufSize)\r
+ {\r
+ *(uint32_t *)(pstCurParse->pDataBuf) = (uint32_t)pstJsonCur->unData.lValue;\r
+ }\r
+ else if (sizeof(uint16_t) == pstCurParse->uiBufSize)\r
+ {\r
+ *(uint16_t *)(pstCurParse->pDataBuf) = (uint16_t)pstJsonCur->unData.lValue;\r
+ }\r
+ else if (sizeof(uint8_t) == pstCurParse->uiBufSize)\r
+ {\r
+ *(uint8_t *)(pstCurParse->pDataBuf) = (uint8_t)pstJsonCur->unData.lValue;\r
+ }\r
+ else if ((pstCurParse->uiBufSize > sizeof(uint64_t)))\r
+ {\r
+ snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu", \r
+ (unsigned long long)(pstJsonCur->unData.lValue));\r
+ }\r
+ else\r
+ {\r
+ vdebug("Invalid number data buf size %u.\n", pstCurParse->uiBufSize);\r
+ }\r
+ break;\r
+ }\r
+ case JSON_TYPE_STRING:\r
+ {\r
+ strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);\r
+ break;\r
+ }\r
+ case JSON_TYPE_BOOL:\r
+ {\r
+ *(uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;\r
+ break;\r
+ }\r
+ default :\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_array\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ VTOY_JSON **ppstArrayItem\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *ppstArrayItem = pstJsonItem;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_array_ex\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ VTOY_JSON **ppstArrayItem\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+ \r
+ *ppstArrayItem = pstJsonItem->pstChild;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_object\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ VTOY_JSON **ppstObjectItem\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+\r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *ppstObjectItem = pstJsonItem;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_int\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ int *piValue\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *piValue = (int)pstJsonItem->unData.lValue;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_uint\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ uint32_t *puiValue\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *puiValue = (uint32_t)pstJsonItem->unData.lValue;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_uint64\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ uint64_t *pui64Value\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *pui64Value = (uint64_t)pstJsonItem->unData.lValue;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_bool\r
+(\r
+ VTOY_JSON *pstJson,\r
+ const char *szKey, \r
+ uint8_t *pbValue\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ *pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_string\r
+(\r
+ VTOY_JSON *pstJson, \r
+ const char *szKey, \r
+ uint32_t uiBufLen,\r
+ char *pcBuf\r
+)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+ \r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return JSON_NOT_FOUND;\r
+ }\r
+\r
+ strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);\r
+\r
+ return JSON_SUCCESS;\r
+}\r
+\r
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey)\r
+{\r
+ VTOY_JSON *pstJsonItem = NULL;\r
+\r
+ if ((NULL == pstJson) || (NULL == szKey))\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);\r
+ if (NULL == pstJsonItem)\r
+ {\r
+ vdebug("Key %s is not found in json data.\n", szKey);\r
+ return NULL;\r
+ }\r
+\r
+ return pstJsonItem->unData.pcStrVal;\r
+}\r
+\r
+int vtoy_json_destroy(VTOY_JSON *pstJson)\r
+{\r
+ if (NULL == pstJson)\r
+ { \r
+ return JSON_SUCCESS;\r
+ }\r
+\r
+ if (NULL != pstJson->pstChild)\r
+ {\r
+ vtoy_json_free(pstJson->pstChild);\r
+ }\r
+\r
+ if (NULL != pstJson->pstNext)\r
+ {\r
+ vtoy_json_free(pstJson->pstNext);\r
+ }\r
+\r
+ free(pstJson);\r
+ \r
+ return JSON_SUCCESS;\r
+}\r
+\r
--- /dev/null
+/******************************************************************************
+ * ventoy_json.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_JSON_H__
+#define __VENTOY_JSON_H__
+
+#define JSON_NEW_ITEM(pstJson, ret) \
+{ \
+ (pstJson) = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON)); \
+ if (NULL == (pstJson)) \
+ { \
+ vdebug("Failed to alloc memory for json."); \
+ return (ret); \
+ } \
+}
+
+#define ssprintf(curpos, buf, len, fmt, args...) \
+ curpos += snprintf(buf + curpos, len - curpos, fmt, ##args)
+
+#define VTOY_JSON_IS_SKIPABLE(c) (((c) <= 32) ? 1 : 0)
+
+#define VTOY_JSON_PRINT_PREFIX(uiDepth, args...) \
+{ \
+ uint32_t _uiLoop = 0; \
+ for (_uiLoop = 0; _uiLoop < (uiDepth); _uiLoop++) \
+ { \
+ ssprintf(uiCurPos, pcBuf, uiBufLen, " "); \
+ } \
+ ssprintf(uiCurPos, pcBuf, uiBufLen, ##args); \
+}
+
+#define VTOY_JSON_SUCCESS_RET "{ \"result\" : \"success\" }"
+#define VTOY_JSON_FAILED_RET "{ \"result\" : \"failed\" }"
+#define VTOY_JSON_INVALID_RET "{ \"result\" : \"invalidfmt\" }"
+#define VTOY_JSON_TOKEN_ERR_RET "{ \"result\" : \"tokenerror\" }"
+#define VTOY_JSON_EXIST_RET "{ \"result\" : \"exist\" }"
+#define VTOY_JSON_TIMEOUT_RET "{ \"result\" : \"timeout\" }"
+#define VTOY_JSON_BUSY_RET "{ \"result\" : \"busy\" }"
+#define VTOY_JSON_INUSE_RET "{ \"result\" : \"inuse\" }"
+#define VTOY_JSON_NOTFOUND_RET "{ \"result\" : \"notfound\" }"
+#define VTOY_JSON_NOTRUNNING_RET "{ \"result\" : \"notrunning\" }"
+#define VTOY_JSON_NOT_READY_RET "{ \"result\" : \"notready\" }"
+#define VTOY_JSON_NOT_SUPPORT_RET "{ \"result\" : \"notsupport\" }"
+#define VTOY_JSON_MBR_2TB_RET "{ \"result\" : \"mbr2tb\" }"
+#define VTOY_JSON_INVALID_RSV_RET "{ \"result\" : \"reserve_invalid\" }"
+#define VTOY_JSON_FILE_NOT_FOUND_RET "{ \"result\" : \"file_not_found\" }"
+
+typedef enum tagJSON_TYPE
+{
+ JSON_TYPE_NUMBER = 0,
+ JSON_TYPE_STRING,
+ JSON_TYPE_BOOL,
+ JSON_TYPE_ARRAY,
+ JSON_TYPE_OBJECT,
+ JSON_TYPE_NULL,
+ JSON_TYPE_BUTT
+}JSON_TYPE;
+
+typedef struct tagVTOY_JSON
+{
+ struct tagVTOY_JSON *pstPrev;
+ struct tagVTOY_JSON *pstNext;
+ struct tagVTOY_JSON *pstChild;
+
+ JSON_TYPE enDataType;
+ union
+ {
+ char *pcStrVal;
+ int iNumVal;
+ uint64_t lValue;
+ }unData;
+
+ char *pcName;
+}VTOY_JSON;
+
+#define VTOY_JSON_FMT_BEGIN(uiCurPos, pcBuf, uiBufLen) \
+{\
+ uint32_t __uiCurPos = (uiCurPos);\
+ uint32_t __uiBufLen = (uiBufLen);\
+ char *__pcBuf = (pcBuf);
+
+#define VTOY_JSON_FMT_END(uiCurPos) \
+ (uiCurPos) = __uiCurPos;\
+}
+
+#define VTOY_JSON_FMT_OBJ_BEGIN() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "{")
+
+#define VTOY_JSON_FMT_OBJ_END() \
+{\
+ if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+ {\
+ __uiCurPos -= 1;\
+ }\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
+}
+
+#define VTOY_JSON_FMT_OBJ_ENDEX() \
+{\
+ if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+ {\
+ __uiCurPos -= 1;\
+ }\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "},");\
+}
+
+#define VTOY_JSON_FMT_KEY(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":", (Key))
+
+#define VTOY_JSON_FMT_ITEM(Item) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\",", (Item))
+
+#define VTOY_JSON_FMT_COMA() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");
+
+#define VTOY_JSON_FMT_APPEND_BEGIN() \
+{ \
+ if ('}' == *(__pcBuf + (__uiCurPos - 1)))\
+ {\
+ __uiCurPos -= 1;\
+ }\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");\
+}
+
+#define VTOY_JSON_FMT_APPEND_END() \
+{ \
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
+}
+
+#define VTOY_JSON_FMT_ARY_BEGIN() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "[")
+
+#define VTOY_JSON_FMT_ARY_END() \
+{\
+ if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+ {\
+ __uiCurPos -= 1;\
+ }\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "]");\
+}
+
+#define VTOY_JSON_FMT_ARY_ENDEX() \
+{\
+ if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+ {\
+ __uiCurPos -= 1;\
+ }\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "],");\
+}
+
+#define VTOY_JSON_FMT_UINT64(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%llu,", Key, (_ull)Val)
+
+#define VTOY_JSON_FMT_ULONG(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%lu,", Key, Val)
+#define VTOY_JSON_FMT_LONG(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%ld,", Key, Val)
+
+#define VTOY_JSON_FMT_UINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%u,", Key, Val)
+
+#define VTOY_JSON_FMT_STRINT(Key, Val) \
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%u\",", Key, Val)
+
+#define VTOY_JSON_FMT_STRINT64(Key, Val) \
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%llu\",", Key, Val)
+
+#define VTOY_JSON_FMT_SINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%d,", Key, Val)
+
+#define VTOY_JSON_FMT_DUBL(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%.1lf,", Key, Val)
+#define VTOY_JSON_FMT_DUBL2(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%10.02lf,", Key, Val)
+
+#define VTOY_JSON_FMT_STRN(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%s\",", Key, Val)
+
+#define VTOY_JSON_FMT_NULL(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":null,", Key)
+
+#define VTOY_JSON_FMT_TRUE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key))
+#define VTOY_JSON_FMT_FALSE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key))
+
+#define VTOY_JSON_FMT_BOOL(Key, Val) \
+{\
+ if (0 == (Val))\
+ {\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key));\
+ }\
+ else \
+ {\
+ ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key));\
+ }\
+}
+
+typedef struct tagVTOY_JSON_PARSE
+{
+ char *pcKey;
+ void *pDataBuf;
+ uint32_t uiBufSize;
+}VTOY_JSON_PARSE_S;
+
+#define JSON_SUCCESS 0
+#define JSON_FAILED 1
+#define JSON_NOT_FOUND 2
+
+int vtoy_json_parse_value
+(
+ char *pcNewStart,
+ char *pcRawStart,
+ VTOY_JSON *pstJson,
+ const char *pcData,
+ const char **ppcEnd
+);
+VTOY_JSON * vtoy_json_create(void);
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData);
+int vtoy_json_destroy(VTOY_JSON *pstJson);
+VTOY_JSON *vtoy_json_find_item
+(
+ VTOY_JSON *pstJson,
+ JSON_TYPE enDataType,
+ const char *szKey
+);
+int vtoy_json_scan_parse
+(
+ const VTOY_JSON *pstJson,
+ uint32_t uiParseNum,
+ VTOY_JSON_PARSE_S *pstJsonParse
+);
+int vtoy_json_get_int
+(
+ VTOY_JSON *pstJson,
+ const char *szKey,
+ int *piValue
+);
+int vtoy_json_get_uint
+(
+ VTOY_JSON *pstJson,
+ const char *szKey,
+ uint32_t *puiValue
+);
+int vtoy_json_get_uint64
+(
+ VTOY_JSON *pstJson,
+ const char *szKey,
+ uint64_t *pui64Value
+);
+int vtoy_json_get_bool
+(
+ VTOY_JSON *pstJson,
+ const char *szKey,
+ uint8_t *pbValue
+);
+int vtoy_json_get_string
+(
+ VTOY_JSON *pstJson,
+ const char *szKey,
+ uint32_t uiBufLen,
+ char *pcBuf
+);
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey);
+
+#endif /* __VENTOY_JSON_H__ */
+
--- /dev/null
+/******************************************************************************
+ * ventoy_log.c ---- ventoy log
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <ventoy_define.h>
+
+static int g_ventoy_log_level = VLOG_DEBUG;
+static pthread_mutex_t g_log_mutex;
+
+int ventoy_log_init(void)
+{
+ pthread_mutex_init(&g_log_mutex, NULL);
+ return 0;
+}
+
+void ventoy_log_exit(void)
+{
+ pthread_mutex_destroy(&g_log_mutex);
+}
+
+void ventoy_set_loglevel(int level)
+{
+ g_ventoy_log_level = level;
+}
+
+void ventoy_syslog_newline(int level, const char *Fmt, ...)
+{
+ char log[512];
+ va_list arg;
+ time_t stamp;
+ struct tm ttm;
+ FILE *fp;
+
+ if (level > g_ventoy_log_level)
+ {
+ return;
+ }
+
+ time(&stamp);
+ localtime_r(&stamp, &ttm);
+
+ va_start(arg, Fmt);
+ vsnprintf(log, 512, Fmt, arg);
+ va_end(arg);
+
+ pthread_mutex_lock(&g_log_mutex);
+ fp = fopen(VTOY_LOG_FILE, "a+");
+ if (fp)
+ {
+ fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s\n",
+ ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
+ ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
+ log);
+ fclose(fp);
+ }
+ pthread_mutex_unlock(&g_log_mutex);
+}
+
+void ventoy_syslog(int level, const char *Fmt, ...)
+{
+ char log[512];
+ va_list arg;
+ time_t stamp;
+ struct tm ttm;
+ FILE *fp;
+
+ if (level > g_ventoy_log_level)
+ {
+ return;
+ }
+
+ time(&stamp);
+ localtime_r(&stamp, &ttm);
+
+ va_start(arg, Fmt);
+ vsnprintf(log, 512, Fmt, arg);
+ va_end(arg);
+
+ pthread_mutex_lock(&g_log_mutex);
+ fp = fopen(VTOY_LOG_FILE, "a+");
+ if (fp)
+ {
+ fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s",
+ ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
+ ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
+ log);
+ fclose(fp);
+ }
+ pthread_mutex_unlock(&g_log_mutex);
+}
+
--- /dev/null
+/******************************************************************************
+ * ventoy_md5.c ---- ventoy md5
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+const static uint32_t k[64] =
+{
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+};
+
+const static uint32_t r[] =
+{
+ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
+};
+
+#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
+#define to_bytes(val, bytes) *((uint32_t *)(bytes)) = (val)
+
+#define ROTATE_CALC() \
+{\
+ temp = d; \
+ d = c; \
+ c = b; \
+ b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); \
+ a = temp; \
+}
+
+void ventoy_md5(const void *data, uint32_t len, uint8_t *md5)
+{
+ uint32_t h0, h1, h2, h3;
+ uint32_t w[16];
+ uint32_t a, b, c, d, i, f, g, temp;
+ uint32_t offset, mod, delta;
+ uint8_t postbuf[128] = {0};
+
+ // Initialize variables - simple count in nibbles:
+ h0 = 0x67452301;
+ h1 = 0xefcdab89;
+ h2 = 0x98badcfe;
+ h3 = 0x10325476;
+
+ //Pre-processing:
+ //append "1" bit to message
+ //append "0" bits until message length in bits ≡ 448 (mod 512)
+ //append length mod (2^64) to message
+
+ mod = len % 64;
+ if (mod)
+ {
+ memcpy(postbuf, (const uint8_t *)data + len - mod, mod);
+ }
+
+ postbuf[mod] = 0x80;
+ if (mod < 56)
+ {
+ to_bytes(len * 8, postbuf + 56);
+ to_bytes(len >> 29, postbuf + 60);
+ delta = 64;
+ }
+ else
+ {
+ to_bytes(len * 8, postbuf + 120);
+ to_bytes(len >> 29, postbuf + 124);
+ delta = 128;
+ }
+
+ len -= mod;
+
+ for (offset = 0; offset < len + delta; offset += 64)
+ {
+ if (offset < len)
+ {
+ memcpy(w, (const uint8_t *)data + offset, 64);
+ }
+ else
+ {
+ memcpy(w, postbuf + offset - len, 64);
+ }
+
+ // Initialize hash value for this chunk:
+ a = h0;
+ b = h1;
+ c = h2;
+ d = h3;
+
+ // Main loop:
+ for (i = 0; i < 16; i++)
+ {
+ f = (b & c) | ((~b) & d);
+ g = i;
+ ROTATE_CALC();
+ }
+
+ for (i = 16; i < 32; i++)
+ {
+ f = (d & b) | ((~d) & c);
+ g = (5 * i + 1) % 16;
+ ROTATE_CALC();
+ }
+
+ for (i = 32; i < 48; i++)
+ {
+ f = b ^ c ^ d;
+ g = (3 * i + 5) % 16;
+ ROTATE_CALC();
+ }
+
+ for (i = 48; i < 64; i++)
+ {
+ f = c ^ (b | (~d));
+ g = (7 * i) % 16;
+ ROTATE_CALC();
+ }
+
+ // Add this chunk's hash to result so far:
+ h0 += a;
+ h1 += b;
+ h2 += c;
+ h3 += d;
+ }
+
+ //var char md5[16] := h0 append h1 append h2 append h3 //(Output is in little-endian)
+ to_bytes(h0, md5);
+ to_bytes(h1, md5 + 4);
+ to_bytes(h2, md5 + 8);
+ to_bytes(h3, md5 + 12);
+}
+
--- /dev/null
+/******************************************************************************
+ * ventoy_util.c ---- ventoy util
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <time.h>
+#include <ventoy_define.h>
+#include <ventoy_util.h>
+
+uint8_t g_mbr_template[512];
+
+void ventoy_gen_preudo_uuid(void *uuid)
+{
+ int i;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ srand(time(NULL));
+ for (i = 0; i < 8; i++)
+ {
+ *((uint16_t *)uuid + i) = (uint16_t)(rand() & 0xFFFF);
+ }
+ }
+ else
+ {
+ read(fd, uuid, 16);
+ close(fd);
+ }
+}
+
+uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes)
+{
+ int i;
+ int Pow2 = 1;
+ double Delta;
+ double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
+
+ if ((SizeBytes % SIZE_1GB) == 0)
+ {
+ return (uint64_t)(SizeBytes / SIZE_1GB);
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ if (Pow2 > GB)
+ {
+ Delta = (Pow2 - GB) / Pow2;
+ }
+ else
+ {
+ Delta = (GB - Pow2) / Pow2;
+ }
+
+ if (Delta < 0.05)
+ {
+ return Pow2;
+ }
+
+ Pow2 <<= 1;
+ }
+
+ return (uint64_t)GB;
+}
+
+
+int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...)
+{
+ int len;
+ char c;
+ char path[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vsnprintf(path, 256, fmt, arg);
+ va_end(arg);
+
+ if (access(path, F_OK) >= 0)
+ {
+ FILE *fp = fopen(path, "r");
+ memset(buffer, 0, buflen);
+ len = (int)fread(buffer, 1, buflen - 1, fp);
+ fclose(fp);
+
+ while (len > 0)
+ {
+ c = buffer[len - 1];
+ if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
+ {
+ buffer[len - 1] = 0;
+ len--;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ vdebug("%s not exist \n", path);
+ return 1;
+ }
+}
+
+int ventoy_is_disk_mounted(const char *devpath)
+{
+ int len;
+ int mount = 0;
+ char line[512];
+ FILE *fp = NULL;
+
+ fp = fopen("/proc/mounts", "r");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ len = (int)strlen(devpath);
+ while (fgets(line, sizeof(line), fp))
+ {
+ if (strncmp(line, devpath, len) == 0)
+ {
+ mount = 1;
+ vdebug("%s mounted <%s>\n", devpath, line);
+ goto end;
+ }
+ }
+
+end:
+ fclose(fp);
+ return mount;
+}
+
+int ventoy_try_umount_disk(const char *devpath)
+{
+ int rc;
+ int len;
+ char line[512];
+ char *pos1 = NULL;
+ char *pos2 = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen("/proc/mounts", "r");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ len = (int)strlen(devpath);
+ while (fgets(line, sizeof(line), fp))
+ {
+ if (strncmp(line, devpath, len) == 0)
+ {
+ pos1 = strchr(line, ' ');
+ if (pos1)
+ {
+ pos2 = strchr(pos1 + 1, ' ');
+ if (pos2)
+ {
+ *pos2 = 0;
+ }
+
+ rc = umount(pos1 + 1);
+ if (rc)
+ {
+ vdebug("umount %s %s [ failed ] error:%d\n", devpath, pos1 + 1, errno);
+ }
+ else
+ {
+ vdebug("umount %s %s [ success ]\n", devpath, pos1 + 1);
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+
+int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen)
+{
+ int FileSize;
+ FILE *fp = NULL;
+ void *Data = NULL;
+
+ fp = fopen(FileName, "rb");
+ if (fp == NULL)
+ {
+ vlog("Failed to open file %s", FileName);
+ return 1;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ FileSize = (int)ftell(fp);
+
+ Data = malloc(FileSize + ExtLen);
+ if (!Data)
+ {
+ fclose(fp);
+ return 1;
+ }
+
+ fseek(fp, 0, SEEK_SET);
+ fread(Data, 1, FileSize, fp);
+
+ fclose(fp);
+
+ *Bufer = Data;
+ *BufLen = FileSize;
+
+ return 0;
+}
+
+const char * ventoy_get_local_version(void)
+{
+ int rc;
+ int FileSize;
+ char *Pos = NULL;
+ char *Buf = NULL;
+ static char LocalVersion[64] = { 0 };
+
+ if (LocalVersion[0] == 0)
+ {
+ rc = ventoy_read_file_to_buf("ventoy/version", 1, (void **)&Buf, &FileSize);
+ if (rc)
+ {
+ return "";
+ }
+ Buf[FileSize] = 0;
+
+ for (Pos = Buf; *Pos; Pos++)
+ {
+ if (*Pos == '\r' || *Pos == '\n')
+ {
+ *Pos = 0;
+ break;
+ }
+ }
+
+ scnprintf(LocalVersion, "%s", Buf);
+ free(Buf);
+ }
+
+ return LocalVersion;
+}
+
+int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
+{
+ memcpy(pMBR, g_mbr_template, 512);
+ return 0;
+}
+
+static int VentoyFillProtectMBR(uint64_t DiskSizeBytes, MBR_HEAD *pMBR)
+{
+ ventoy_guid Guid;
+ uint32_t DiskSignature;
+ uint64_t DiskSectorCount;
+
+ VentoyGetLocalBootImg(pMBR);
+
+ ventoy_gen_preudo_uuid(&Guid);
+
+ memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
+
+ vdebug("Disk signature: 0x%08x\n", DiskSignature);
+
+ memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
+
+ DiskSectorCount = DiskSizeBytes / 512 - 1;
+ if (DiskSectorCount > 0xFFFFFFFF)
+ {
+ DiskSectorCount = 0xFFFFFFFF;
+ }
+
+ memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));
+
+ pMBR->PartTbl[0].Active = 0x00;
+ pMBR->PartTbl[0].FsFlag = 0xee; // EE
+
+ pMBR->PartTbl[0].StartHead = 0;
+ pMBR->PartTbl[0].StartSector = 1;
+ pMBR->PartTbl[0].StartCylinder = 0;
+ pMBR->PartTbl[0].EndHead = 254;
+ pMBR->PartTbl[0].EndSector = 63;
+ pMBR->PartTbl[0].EndCylinder = 1023;
+
+ pMBR->PartTbl[0].StartSectorId = 1;
+ pMBR->PartTbl[0].SectorCount = (uint32_t)DiskSectorCount;
+
+ pMBR->Byte55 = 0x55;
+ pMBR->ByteAA = 0xAA;
+
+ pMBR->BootCode[92] = 0x22;
+
+ return 0;
+}
+
+static int ventoy_fill_gpt_partname(uint16_t Name[36], const char *asciiName)
+{
+ int i;
+ int len;
+
+ memset(Name, 0, 36 * sizeof(uint16_t));
+ len = (int)strlen(asciiName);
+ for (i = 0; i < 36 && i < len; i++)
+ {
+ Name[i] = asciiName[i];
+ }
+
+ return 0;
+}
+
+int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt)
+{
+ uint64_t ReservedSector = 33;
+ uint64_t Part1SectorCount = 0;
+ uint64_t DiskSectorCount = size / 512;
+ VTOY_GPT_HDR *Head = &gpt->Head;
+ VTOY_GPT_PART_TBL *Table = gpt->PartTbl;
+ ventoy_guid WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
+ //ventoy_guid EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
+ //ventoy_guid BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
+
+ VentoyFillProtectMBR(size, &gpt->MBR);
+
+ if (reserve > 0)
+ {
+ ReservedSector += reserve / 512;
+ }
+
+ // check aligned with 4KB
+ if (align4k)
+ {
+ if (DiskSectorCount % 8)
+ {
+ vdebug("Disk need to align with 4KB %u\n", (uint32_t)(DiskSectorCount % 8));
+ ReservedSector += (DiskSectorCount % 8);
+ }
+ }
+
+ Part1SectorCount = DiskSectorCount - ReservedSector - (VTOYEFI_PART_BYTES / 512) - 2048;
+
+ memcpy(Head->Signature, "EFI PART", 8);
+ Head->Version[2] = 0x01;
+ Head->Length = 92;
+ Head->Crc = 0;
+ Head->EfiStartLBA = 1;
+ Head->EfiBackupLBA = DiskSectorCount - 1;
+ Head->PartAreaStartLBA = 34;
+ Head->PartAreaEndLBA = DiskSectorCount - 34;
+ ventoy_gen_preudo_uuid(&Head->DiskGuid);
+ Head->PartTblStartLBA = 2;
+ Head->PartTblTotNum = 128;
+ Head->PartTblEntryLen = 128;
+
+
+ memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
+ ventoy_gen_preudo_uuid(&(Table[0].PartGuid));
+ Table[0].StartLBA = 2048;
+ Table[0].LastLBA = 2048 + Part1SectorCount - 1;
+ Table[0].Attr = 0;
+ ventoy_fill_gpt_partname(Table[0].Name, "Ventoy");
+
+ // to fix windows issue
+ //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
+ memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
+ ventoy_gen_preudo_uuid(&(Table[1].PartGuid));
+ Table[1].StartLBA = Table[0].LastLBA + 1;
+ Table[1].LastLBA = Table[1].StartLBA + VTOYEFI_PART_BYTES / 512 - 1;
+ Table[1].Attr = 0x8000000000000001ULL;
+ ventoy_fill_gpt_partname(Table[1].Name, "VTOYEFI");
+
+#if 0
+ memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(ventoy_guid));
+ ventoy_gen_preudo_uuid(&(Table[2].PartGuid));
+ Table[2].StartLBA = 34;
+ Table[2].LastLBA = 2047;
+ Table[2].Attr = 0;
+#endif
+
+ //Update CRC
+ Head->PartTblCrc = ventoy_crc32(Table, sizeof(gpt->PartTbl));
+ Head->Crc = ventoy_crc32(Head, Head->Length);
+
+ return 0;
+}
+
+int VentoyFillMBRLocation(uint64_t DiskSizeInBytes, uint32_t StartSectorId, uint32_t SectorCount, PART_TABLE *Table)
+{
+ uint8_t Head;
+ uint8_t Sector;
+ uint8_t nSector = 63;
+ uint8_t nHead = 8;
+ uint32_t Cylinder;
+ uint32_t EndSectorId;
+
+ while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
+ {
+ nHead = (uint8_t)nHead * 2;
+ }
+
+ if (nHead == 0)
+ {
+ nHead = 255;
+ }
+
+ Cylinder = StartSectorId / nSector / nHead;
+ Head = StartSectorId / nSector % nHead;
+ Sector = StartSectorId % nSector + 1;
+
+ Table->StartHead = Head;
+ Table->StartSector = Sector;
+ Table->StartCylinder = Cylinder;
+
+ EndSectorId = StartSectorId + SectorCount - 1;
+ Cylinder = EndSectorId / nSector / nHead;
+ Head = EndSectorId / nSector % nHead;
+ Sector = EndSectorId % nSector + 1;
+
+ Table->EndHead = Head;
+ Table->EndSector = Sector;
+ Table->EndCylinder = Cylinder;
+
+ Table->StartSectorId = StartSectorId;
+ Table->SectorCount = SectorCount;
+
+ return 0;
+}
+
+int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR)
+{
+ ventoy_guid Guid;
+ uint32_t DiskSignature;
+ uint32_t DiskSectorCount;
+ uint32_t PartSectorCount;
+ uint32_t PartStartSector;
+ uint32_t ReservedSector;
+
+ VentoyGetLocalBootImg(pMBR);
+
+ ventoy_gen_preudo_uuid(&Guid);
+
+ memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
+
+ vdebug("Disk signature: 0x%08x\n", DiskSignature);
+
+ memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
+
+ if (size / 512 > 0xFFFFFFFF)
+ {
+ DiskSectorCount = 0xFFFFFFFF;
+ }
+ else
+ {
+ DiskSectorCount = (uint32_t)(size / 512);
+ }
+
+ if (reserve <= 0)
+ {
+ ReservedSector = 0;
+ }
+ else
+ {
+ ReservedSector = (uint32_t)(reserve / 512);
+ }
+
+ if (PartStyle)
+ {
+ ReservedSector += 33; // backup GPT part table
+ }
+
+ // check aligned with 4KB
+ if (align4k)
+ {
+ uint64_t sectors = size / 512;
+ if (sectors % 8)
+ {
+ vlog("Disk need to align with 4KB %u\n", (uint32_t)(sectors % 8));
+ ReservedSector += (uint32_t)(sectors % 8);
+ }
+ }
+
+ vlog("ReservedSector: %u\n", ReservedSector);
+
+
+ //Part1
+ PartStartSector = VTOYIMG_PART_START_SECTOR;
+ PartSectorCount = DiskSectorCount - ReservedSector - VTOYEFI_PART_BYTES / 512 - PartStartSector;
+ VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl);
+
+ pMBR->PartTbl[0].Active = 0x80; // bootable
+ pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
+
+ //Part2
+ PartStartSector += PartSectorCount;
+ PartSectorCount = VTOYEFI_PART_BYTES / 512;
+ VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
+
+ pMBR->PartTbl[1].Active = 0x00;
+ pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
+
+ pMBR->Byte55 = 0x55;
+ pMBR->ByteAA = 0xAA;
+
+ return 0;
+}
+
--- /dev/null
+/******************************************************************************
+ * ventoy_util.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_UTIL_H__
+#define __VENTOY_UTIL_H__
+
+#define check_free(p) if (p) free(p)
+#define vtoy_safe_close_fd(fd) \
+{\
+ if ((fd) >= 0) \
+ { \
+ close(fd); \
+ (fd) = -1; \
+ }\
+}
+
+extern uint8_t g_mbr_template[512];
+void ventoy_gen_preudo_uuid(void *uuid);
+int ventoy_get_disk_part_name(const char *dev, int part, char *partbuf, int bufsize);
+int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...);
+uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes);
+void ventoy_md5(const void *data, uint32_t len, uint8_t *md5);
+int ventoy_is_disk_mounted(const char *devpath);
+int ventoy_try_umount_disk(const char *devpath);
+int unxz(unsigned char *in, int in_size,
+ int (*fill)(void *dest, unsigned int size),
+ int (*flush)(void *src, unsigned int size),
+ unsigned char *out, int *in_used,
+ void (*error)(char *x));
+int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen);
+const char * ventoy_get_local_version(void);
+int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt);
+int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR);
+
+#endif /* __VENTOY_UTIL_H__ */
+
--- /dev/null
+/******************************************************************************
+ * Ventoy2Disk.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY2DISK_H__
+#define __VENTOY2DISK_H__
+
+
+
+#endif /* __VENTOY2DISK_H__ */
+
--- /dev/null
+#!/bin/sh
+
+CUR="$PWD"
+
+rm -rf src
+mkdir -p src/libexfat
+mkdir -p src/mkfs
+
+rm -rf exfat-1.3.0
+unzip exfat-1.3.0.zip
+
+cd exfat-1.3.0
+autoreconf --install
+./configure --prefix="$CUR" CFLAGS='-O2 -D_FILE_OFFSET_BITS=64'
+make
+
+cp -a libexfat/*.c ../src/libexfat/
+cp -a libexfat/*.h ../src/libexfat/
+cp -a mkfs/*.c ../src/mkfs/
+cp -a mkfs/*.h ../src/mkfs/
+rm -f ../src/libexfat/log.c
+
+cd ..
+rm -rf exfat-1.3.0
+
+mv src/mkfs/main.c src/mkfs/mkexfat_main.c
+sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat_main.c
+sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat.h
+sed 's/int main/int mkexfat_main/g' -i src/mkfs/mkexfat_main.c
+
--- /dev/null
+/*
+ byteorder.h (12.01.10)
+ Endianness stuff. exFAT uses little-endian byte order.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#include "platform.h"
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct { uint16_t __u16; } le16_t;
+typedef struct { uint32_t __u32; } le32_t;
+typedef struct { uint64_t __u64; } le64_t;
+
+#if EXFAT_BYTE_ORDER == EXFAT_LITTLE_ENDIAN
+
+static inline uint16_t le16_to_cpu(le16_t v) { return v.__u16; }
+static inline uint32_t le32_to_cpu(le32_t v) { return v.__u32; }
+static inline uint64_t le64_to_cpu(le64_t v) { return v.__u64; }
+
+static inline le16_t cpu_to_le16(uint16_t v) { le16_t t = {v}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v) { le32_t t = {v}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v) { le64_t t = {v}; return t; }
+
+typedef size_t bitmap_t;
+
+#elif EXFAT_BYTE_ORDER == EXFAT_BIG_ENDIAN
+
+static inline uint16_t le16_to_cpu(le16_t v)
+ { return exfat_bswap16(v.__u16); }
+static inline uint32_t le32_to_cpu(le32_t v)
+ { return exfat_bswap32(v.__u32); }
+static inline uint64_t le64_to_cpu(le64_t v)
+ { return exfat_bswap64(v.__u64); }
+
+static inline le16_t cpu_to_le16(uint16_t v)
+ { le16_t t = {exfat_bswap16(v)}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v)
+ { le32_t t = {exfat_bswap32(v)}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v)
+ { le64_t t = {exfat_bswap64(v)}; return t; }
+
+typedef unsigned char bitmap_t;
+
+#else
+#error Wow! You have a PDP machine?!
+#endif
+
+#endif /* ifndef BYTEORDER_H_INCLUDED */
--- /dev/null
+/*
+ cluster.c (03.09.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+/*
+ * Sector to absolute offset.
+ */
+static off_t s2o(const struct exfat* ef, off_t sector)
+{
+ return sector << ef->sb->sector_bits;
+}
+
+/*
+ * Cluster to sector.
+ */
+static off_t c2s(const struct exfat* ef, cluster_t cluster)
+{
+ if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+ exfat_bug("invalid cluster number %u", cluster);
+ return le32_to_cpu(ef->sb->cluster_sector_start) +
+ ((off_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) << ef->sb->spc_bits);
+}
+
+/*
+ * Cluster to absolute offset.
+ */
+off_t exfat_c2o(const struct exfat* ef, cluster_t cluster)
+{
+ return s2o(ef, c2s(ef, cluster));
+}
+
+/*
+ * Sector to cluster.
+ */
+static cluster_t s2c(const struct exfat* ef, off_t sector)
+{
+ return ((sector - le32_to_cpu(ef->sb->cluster_sector_start)) >>
+ ef->sb->spc_bits) + EXFAT_FIRST_DATA_CLUSTER;
+}
+
+/*
+ * Size in bytes to size in clusters (rounded upwards).
+ */
+static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes)
+{
+ uint64_t cluster_size = CLUSTER_SIZE(*ef->sb);
+ return DIV_ROUND_UP(bytes, cluster_size);
+}
+
+cluster_t exfat_next_cluster(const struct exfat* ef,
+ const struct exfat_node* node, cluster_t cluster)
+{
+ le32_t next;
+ off_t fat_offset;
+
+ if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+ exfat_bug("bad cluster 0x%x", cluster);
+
+ if (node->is_contiguous)
+ return cluster + 1;
+ fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+ + cluster * sizeof(cluster_t);
+ if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0)
+ return EXFAT_CLUSTER_BAD; /* the caller should handle this and print
+ appropriate error message */
+ return le32_to_cpu(next);
+}
+
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+ struct exfat_node* node, uint32_t count)
+{
+ uint32_t i;
+
+ if (node->fptr_index > count)
+ {
+ node->fptr_index = 0;
+ node->fptr_cluster = node->start_cluster;
+ }
+
+ for (i = node->fptr_index; i < count; i++)
+ {
+ node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);
+ if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster))
+ break; /* the caller should handle this and print appropriate
+ error message */
+ }
+ node->fptr_index = count;
+ return node->fptr_cluster;
+}
+
+static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
+{
+ const size_t start_index = start / sizeof(bitmap_t) / 8;
+ const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8);
+ size_t i;
+ size_t start_bitindex;
+ size_t end_bitindex;
+ size_t c;
+
+ for (i = start_index; i < end_index; i++)
+ {
+ if (bitmap[i] == ~((bitmap_t) 0))
+ continue;
+ start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start);
+ end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end);
+ for (c = start_bitindex; c < end_bitindex; c++)
+ if (BMAP_GET(bitmap, c) == 0)
+ {
+ BMAP_SET(bitmap, c);
+ return c + EXFAT_FIRST_DATA_CLUSTER;
+ }
+ }
+ return EXFAT_CLUSTER_END;
+}
+
+static int flush_nodes(struct exfat* ef, struct exfat_node* node)
+{
+ struct exfat_node* p;
+
+ for (p = node->child; p != NULL; p = p->next)
+ {
+ int rc = flush_nodes(ef, p);
+ if (rc != 0)
+ return rc;
+ }
+ return exfat_flush_node(ef, node);
+}
+
+int exfat_flush_nodes(struct exfat* ef)
+{
+ return flush_nodes(ef, ef->root);
+}
+
+int exfat_flush(struct exfat* ef)
+{
+ if (ef->cmap.dirty)
+ {
+ if (exfat_pwrite(ef->dev, ef->cmap.chunk,
+ BMAP_SIZE(ef->cmap.chunk_size),
+ exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+ {
+ exfat_error("failed to write clusters bitmap");
+ return -EIO;
+ }
+ ef->cmap.dirty = false;
+ }
+
+ return 0;
+}
+
+static bool set_next_cluster(const struct exfat* ef, bool contiguous,
+ cluster_t current, cluster_t next)
+{
+ off_t fat_offset;
+ le32_t next_le32;
+
+ if (contiguous)
+ return true;
+ fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+ + current * sizeof(cluster_t);
+ next_le32 = cpu_to_le32(next);
+ if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0)
+ {
+ exfat_error("failed to write the next cluster %#x after %#x", next,
+ current);
+ return false;
+ }
+ return true;
+}
+
+static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint)
+{
+ cluster_t cluster;
+
+ hint -= EXFAT_FIRST_DATA_CLUSTER;
+ if (hint >= ef->cmap.chunk_size)
+ hint = 0;
+
+ cluster = find_bit_and_set(ef->cmap.chunk, hint, ef->cmap.chunk_size);
+ if (cluster == EXFAT_CLUSTER_END)
+ cluster = find_bit_and_set(ef->cmap.chunk, 0, hint);
+ if (cluster == EXFAT_CLUSTER_END)
+ {
+ exfat_error("no free space left");
+ return EXFAT_CLUSTER_END;
+ }
+
+ ef->cmap.dirty = true;
+ return cluster;
+}
+
+static void free_cluster(struct exfat* ef, cluster_t cluster)
+{
+ if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size)
+ exfat_bug("caller must check cluster validity (%#x, %#x)", cluster,
+ ef->cmap.size);
+
+ BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER);
+ ef->cmap.dirty = true;
+}
+
+static bool make_noncontiguous(const struct exfat* ef, cluster_t first,
+ cluster_t last)
+{
+ cluster_t c;
+
+ for (c = first; c < last; c++)
+ if (!set_next_cluster(ef, false, c, c + 1))
+ return false;
+ return true;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+ uint32_t current, uint32_t difference);
+
+static int grow_file(struct exfat* ef, struct exfat_node* node,
+ uint32_t current, uint32_t difference)
+{
+ cluster_t previous;
+ cluster_t next;
+ uint32_t allocated = 0;
+
+ if (difference == 0)
+ exfat_bug("zero clusters count passed");
+
+ if (node->start_cluster != EXFAT_CLUSTER_FREE)
+ {
+ /* get the last cluster of the file */
+ previous = exfat_advance_cluster(ef, node, current - 1);
+ if (CLUSTER_INVALID(*ef->sb, previous))
+ {
+ exfat_error("invalid cluster 0x%x while growing", previous);
+ return -EIO;
+ }
+ }
+ else
+ {
+ if (node->fptr_index != 0)
+ exfat_bug("non-zero pointer index (%u)", node->fptr_index);
+ /* file does not have clusters (i.e. is empty), allocate
+ the first one for it */
+ previous = allocate_cluster(ef, 0);
+ if (CLUSTER_INVALID(*ef->sb, previous))
+ return -ENOSPC;
+ node->fptr_cluster = node->start_cluster = previous;
+ allocated = 1;
+ /* file consists of only one cluster, so it's contiguous */
+ node->is_contiguous = true;
+ }
+
+ while (allocated < difference)
+ {
+ next = allocate_cluster(ef, previous + 1);
+ if (CLUSTER_INVALID(*ef->sb, next))
+ {
+ if (allocated != 0)
+ shrink_file(ef, node, current + allocated, allocated);
+ return -ENOSPC;
+ }
+ if (next != previous - 1 && node->is_contiguous)
+ {
+ /* it's a pity, but we are not able to keep the file contiguous
+ anymore */
+ if (!make_noncontiguous(ef, node->start_cluster, previous))
+ return -EIO;
+ node->is_contiguous = false;
+ node->is_dirty = true;
+ }
+ if (!set_next_cluster(ef, node->is_contiguous, previous, next))
+ return -EIO;
+ previous = next;
+ allocated++;
+ }
+
+ if (!set_next_cluster(ef, node->is_contiguous, previous,
+ EXFAT_CLUSTER_END))
+ return -EIO;
+ return 0;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+ uint32_t current, uint32_t difference)
+{
+ cluster_t previous;
+ cluster_t next;
+
+ if (difference == 0)
+ exfat_bug("zero difference passed");
+ if (node->start_cluster == EXFAT_CLUSTER_FREE)
+ exfat_bug("unable to shrink empty file (%u clusters)", current);
+ if (current < difference)
+ exfat_bug("file underflow (%u < %u)", current, difference);
+
+ /* crop the file */
+ if (current > difference)
+ {
+ cluster_t last = exfat_advance_cluster(ef, node,
+ current - difference - 1);
+ if (CLUSTER_INVALID(*ef->sb, last))
+ {
+ exfat_error("invalid cluster 0x%x while shrinking", last);
+ return -EIO;
+ }
+ previous = exfat_next_cluster(ef, node, last);
+ if (!set_next_cluster(ef, node->is_contiguous, last,
+ EXFAT_CLUSTER_END))
+ return -EIO;
+ }
+ else
+ {
+ previous = node->start_cluster;
+ node->start_cluster = EXFAT_CLUSTER_FREE;
+ node->is_dirty = true;
+ }
+ node->fptr_index = 0;
+ node->fptr_cluster = node->start_cluster;
+
+ /* free remaining clusters */
+ while (difference--)
+ {
+ if (CLUSTER_INVALID(*ef->sb, previous))
+ {
+ exfat_error("invalid cluster 0x%x while freeing after shrink",
+ previous);
+ return -EIO;
+ }
+
+ next = exfat_next_cluster(ef, node, previous);
+ if (!set_next_cluster(ef, node->is_contiguous, previous,
+ EXFAT_CLUSTER_FREE))
+ return -EIO;
+ free_cluster(ef, previous);
+ previous = next;
+ }
+ return 0;
+}
+
+static bool erase_raw(struct exfat* ef, size_t size, off_t offset)
+{
+ if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0)
+ {
+ exfat_error("failed to erase %zu bytes at %"PRId64, size, offset);
+ return false;
+ }
+ return true;
+}
+
+static int erase_range(struct exfat* ef, struct exfat_node* node,
+ uint64_t begin, uint64_t end)
+{
+ uint64_t cluster_boundary;
+ cluster_t cluster;
+
+ if (begin >= end)
+ return 0;
+
+ cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1;
+ cluster = exfat_advance_cluster(ef, node,
+ begin / CLUSTER_SIZE(*ef->sb));
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ {
+ exfat_error("invalid cluster 0x%x while erasing", cluster);
+ return -EIO;
+ }
+ /* erase from the beginning to the closest cluster boundary */
+ if (!erase_raw(ef, MIN(cluster_boundary, end) - begin,
+ exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb)))
+ return -EIO;
+ /* erase whole clusters */
+ while (cluster_boundary < end)
+ {
+ cluster = exfat_next_cluster(ef, node, cluster);
+ /* the cluster cannot be invalid because we have just allocated it */
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ exfat_bug("invalid cluster 0x%x after allocation", cluster);
+ if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster)))
+ return -EIO;
+ cluster_boundary += CLUSTER_SIZE(*ef->sb);
+ }
+ return 0;
+}
+
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+ bool erase)
+{
+ uint32_t c1 = bytes2clusters(ef, node->size);
+ uint32_t c2 = bytes2clusters(ef, size);
+ int rc = 0;
+
+ if (node->references == 0 && node->parent)
+ exfat_bug("no references, node changes can be lost");
+
+ if (node->size == size)
+ return 0;
+
+ if (c1 < c2)
+ rc = grow_file(ef, node, c1, c2 - c1);
+ else if (c1 > c2)
+ rc = shrink_file(ef, node, c1, c1 - c2);
+
+ if (rc != 0)
+ return rc;
+
+ if (erase)
+ {
+ rc = erase_range(ef, node, node->size, size);
+ if (rc != 0)
+ return rc;
+ }
+
+ exfat_update_mtime(node);
+ node->size = size;
+ node->is_dirty = true;
+ return 0;
+}
+
+uint32_t exfat_count_free_clusters(const struct exfat* ef)
+{
+ uint32_t free_clusters = 0;
+ uint32_t i;
+
+ for (i = 0; i < ef->cmap.size; i++)
+ if (BMAP_GET(ef->cmap.chunk, i) == 0)
+ free_clusters++;
+ return free_clusters;
+}
+
+static int find_used_clusters(const struct exfat* ef,
+ cluster_t* a, cluster_t* b)
+{
+ const cluster_t end = le32_to_cpu(ef->sb->cluster_count);
+
+ /* find first used cluster */
+ for (*a = *b + 1; *a < end; (*a)++)
+ if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER))
+ break;
+ if (*a >= end)
+ return 1;
+
+ /* find last contiguous used cluster */
+ for (*b = *a; *b < end; (*b)++)
+ if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0)
+ {
+ (*b)--;
+ break;
+ }
+
+ return 0;
+}
+
+int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b)
+{
+ cluster_t ca, cb;
+
+ if (*a == 0 && *b == 0)
+ ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1;
+ else
+ {
+ ca = s2c(ef, *a);
+ cb = s2c(ef, *b);
+ }
+ if (find_used_clusters(ef, &ca, &cb) != 0)
+ return 1;
+ if (*a != 0 || *b != 0)
+ *a = c2s(ef, ca);
+ *b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb);
+ return 0;
+}
--- /dev/null
+/*
+ compiler.h (09.06.13)
+ Compiler-specific definitions. Note that unknown compiler is not a
+ showstopper.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef COMPILER_H_INCLUDED
+#define COMPILER_H_INCLUDED
+
+#if __STDC_VERSION__ < 199901L
+#error C99-compliant compiler is required
+#endif
+
+#if defined(__clang__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __has_extension(c_static_assert)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#elif defined(__GNUC__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#else
+
+#define PRINTF
+#define NORETURN
+#define PACKED
+
+#endif
+
+#ifdef USE_C11_STATIC_ASSERT
+#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
+#else
+#define CONCAT2(a, b) a ## b
+#define CONCAT1(a, b) CONCAT2(a, b)
+#define STATIC_ASSERT(cond) \
+ extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1])
+#endif
+
+#endif /* ifndef COMPILER_H_INCLUDED */
--- /dev/null
+/* libexfat/config.h. Generated from config.h.in by configure. */
+/* libexfat/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Name of package */
+#define PACKAGE "exfat"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "relan@users.noreply.github.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "Free exFAT implementation"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "Free exFAT implementation 1.3.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "exfat"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "https://github.com/relan/exfat"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.3.0"
+
+/* Define if block devices are not supported. */
+/* #undef USE_UBLIO */
+
+/* Version number of package */
+#define VERSION "1.3.0"
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
--- /dev/null
+/*
+ exfat.h (29.08.09)
+ Definitions of structures and constants used in exFAT file system
+ implementation.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFAT_H_INCLUDED
+#define EXFAT_H_INCLUDED
+
+#ifndef ANDROID
+/* Android.bp is used instead of autotools when targeting Android */
+#include "config.h"
+#endif
+#include "compiler.h"
+#include "exfatfs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define EXFAT_NAME_MAX 255
+/* UTF-16 encodes code points up to U+FFFF as single 16-bit code units.
+ UTF-8 uses up to 3 bytes (i.e. 8-bit code units) to encode code points
+ up to U+FFFF. One additional character is for null terminator. */
+#define EXFAT_UTF8_NAME_BUFFER_MAX (EXFAT_NAME_MAX * 3 + 1)
+#define EXFAT_UTF8_ENAME_BUFFER_MAX (EXFAT_ENAME_MAX * 3 + 1)
+
+#define SECTOR_SIZE(sb) (1 << (sb).sector_bits)
+#define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits)
+#define CLUSTER_INVALID(sb, c) ((c) < EXFAT_FIRST_DATA_CLUSTER || \
+ (c) - EXFAT_FIRST_DATA_CLUSTER >= le32_to_cpu((sb).cluster_count))
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d))
+#define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
+
+#define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8)
+#define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8)
+#define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8)))
+#define BMAP_GET(bitmap, index) \
+ ((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index))
+#define BMAP_SET(bitmap, index) \
+ ((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index))
+#define BMAP_CLR(bitmap, index) \
+ ((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index))
+
+#define EXFAT_REPAIR(hook, ef, ...) \
+ (exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__))
+
+/* The size of off_t type must be 64 bits. File systems larger than 2 GB will
+ be corrupted with 32-bit off_t. */
+STATIC_ASSERT(sizeof(off_t) == 8);
+
+struct exfat_node
+{
+ struct exfat_node* parent;
+ struct exfat_node* child;
+ struct exfat_node* next;
+ struct exfat_node* prev;
+
+ int references;
+ uint32_t fptr_index;
+ cluster_t fptr_cluster;
+ off_t entry_offset;
+ cluster_t start_cluster;
+ uint16_t attrib;
+ uint8_t continuations;
+ bool is_contiguous : 1;
+ bool is_cached : 1;
+ bool is_dirty : 1;
+ bool is_unlinked : 1;
+ uint64_t size;
+ time_t mtime, atime;
+ le16_t name[EXFAT_NAME_MAX + 1];
+};
+
+enum exfat_mode
+{
+ EXFAT_MODE_RO,
+ EXFAT_MODE_RW,
+ EXFAT_MODE_ANY,
+};
+
+struct exfat_dev;
+
+struct exfat
+{
+ struct exfat_dev* dev;
+ struct exfat_super_block* sb;
+ uint16_t* upcase;
+ struct exfat_node* root;
+ struct
+ {
+ cluster_t start_cluster;
+ uint32_t size; /* in bits */
+ bitmap_t* chunk;
+ uint32_t chunk_size; /* in bits */
+ bool dirty;
+ }
+ cmap;
+ char label[EXFAT_UTF8_ENAME_BUFFER_MAX];
+ void* zero_cluster;
+ int dmask, fmask;
+ uid_t uid;
+ gid_t gid;
+ int ro;
+ bool noatime;
+ enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair;
+};
+
+/* in-core nodes iterator */
+struct exfat_iterator
+{
+ struct exfat_node* parent;
+ struct exfat_node* current;
+};
+
+struct exfat_human_bytes
+{
+ uint64_t value;
+ const char* unit;
+};
+
+extern int exfat_errors;
+extern int exfat_errors_fixed;
+
+#define VLOG_LOG 1
+#define VLOG_DEBUG 2
+void ventoy_syslog_newline(int level, const char *Fmt, ...);
+#define exfat_bug(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_warn(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_debug(fmt, args...) ventoy_syslog_newline(VLOG_DEBUG, fmt, ##args)
+
+#if 0
+void exfat_bug(const char* format, ...) PRINTF NORETURN;
+void exfat_error(const char* format, ...) PRINTF;
+void exfat_warn(const char* format, ...) PRINTF;
+void exfat_debug(const char* format, ...) PRINTF;
+#endif /* #if 0 */
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode);
+int exfat_close(struct exfat_dev* dev);
+int exfat_fsync(struct exfat_dev* dev);
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev);
+off_t exfat_get_size(const struct exfat_dev* dev);
+off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence);
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size);
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size);
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+ off_t offset);
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+ off_t offset);
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+ void* buffer, size_t size, off_t offset);
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+ const void* buffer, size_t size, off_t offset);
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+ struct exfat_iterator* it);
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it);
+struct exfat_node* exfat_readdir(struct exfat_iterator* it);
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+ const char* path);
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+ struct exfat_node** node, le16_t* name, const char* path);
+
+off_t exfat_c2o(const struct exfat* ef, cluster_t cluster);
+cluster_t exfat_next_cluster(const struct exfat* ef,
+ const struct exfat_node* node, cluster_t cluster);
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+ struct exfat_node* node, uint32_t count);
+int exfat_flush_nodes(struct exfat* ef);
+int exfat_flush(struct exfat* ef);
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+ bool erase);
+uint32_t exfat_count_free_clusters(const struct exfat* ef);
+int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b);
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+ struct stat* stbuf);
+void exfat_get_name(const struct exfat_node* node,
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]);
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry);
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum);
+le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n);
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size);
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum);
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
+ size_t length);
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb);
+void exfat_print_info(const struct exfat_super_block* sb,
+ uint32_t free_clusters);
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+ size_t insize);
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+ size_t insize);
+size_t utf16_length(const le16_t* str);
+
+struct exfat_node* exfat_get_node(struct exfat_node* node);
+void exfat_put_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir);
+void exfat_reset_cache(struct exfat* ef);
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node);
+int exfat_unlink(struct exfat* ef, struct exfat_node* node);
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node);
+int exfat_mknod(struct exfat* ef, const char* path);
+int exfat_mkdir(struct exfat* ef, const char* path);
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path);
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]);
+void exfat_update_atime(struct exfat_node* node);
+void exfat_update_mtime(struct exfat_node* node);
+const char* exfat_get_label(struct exfat* ef);
+int exfat_set_label(struct exfat* ef, const char* label);
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options);
+void exfat_unmount(struct exfat* ef);
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec);
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+ uint8_t* centisec);
+void exfat_tzset(void);
+
+bool exfat_ask_to_fix(const struct exfat* ef);
+bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
+ uint32_t vbr_checksum);
+bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
+ struct exfat_node* node);
+bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
+ const struct exfat_entry* entry, off_t offset);
+
+#endif /* ifndef EXFAT_H_INCLUDED */
--- /dev/null
+/*
+ exfatfs.h (29.08.09)
+ Definitions of structures and constants used in exFAT file system.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFATFS_H_INCLUDED
+#define EXFATFS_H_INCLUDED
+
+#include "byteorder.h"
+#include "compiler.h"
+
+typedef uint32_t cluster_t; /* cluster number */
+
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xfffffff6
+
+#define EXFAT_CLUSTER_FREE 0 /* free cluster */
+#define EXFAT_CLUSTER_BAD 0xfffffff7 /* cluster contains bad sector */
+#define EXFAT_CLUSTER_END 0xffffffff /* final cluster of file or directory */
+
+#define EXFAT_STATE_MOUNTED 2
+
+struct exfat_super_block
+{
+ uint8_t jump[3]; /* 0x00 jmp and nop instructions */
+ uint8_t oem_name[8]; /* 0x03 "EXFAT " */
+ uint8_t __unused1[53]; /* 0x0B always 0 */
+ le64_t sector_start; /* 0x40 partition first sector */
+ le64_t sector_count; /* 0x48 partition sectors count */
+ le32_t fat_sector_start; /* 0x50 FAT first sector */
+ le32_t fat_sector_count; /* 0x54 FAT sectors count */
+ le32_t cluster_sector_start; /* 0x58 first cluster sector */
+ le32_t cluster_count; /* 0x5C total clusters count */
+ le32_t rootdir_cluster; /* 0x60 first cluster of the root dir */
+ le32_t volume_serial; /* 0x64 volume serial number */
+ struct /* 0x68 FS version */
+ {
+ uint8_t minor;
+ uint8_t major;
+ }
+ version;
+ le16_t volume_state; /* 0x6A volume state flags */
+ uint8_t sector_bits; /* 0x6C sector size as (1 << n) */
+ uint8_t spc_bits; /* 0x6D sectors per cluster as (1 << n) */
+ uint8_t fat_count; /* 0x6E always 1 */
+ uint8_t drive_no; /* 0x6F always 0x80 */
+ uint8_t allocated_percent; /* 0x70 percentage of allocated space */
+ uint8_t __unused2[397]; /* 0x71 always 0 */
+ le16_t boot_signature; /* the value of 0xAA55 */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_super_block) == 512);
+
+#define EXFAT_ENTRY_VALID 0x80
+#define EXFAT_ENTRY_CONTINUED 0x40
+#define EXFAT_ENTRY_OPTIONAL 0x20
+
+#define EXFAT_ENTRY_BITMAP (0x01 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_UPCASE (0x02 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_LABEL (0x03 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE (0x05 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE_INFO (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+#define EXFAT_ENTRY_FILE_NAME (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+#define EXFAT_ENTRY_FILE_TAIL (0x00 | EXFAT_ENTRY_VALID \
+ | EXFAT_ENTRY_CONTINUED \
+ | EXFAT_ENTRY_OPTIONAL)
+
+struct exfat_entry /* common container for all entries */
+{
+ uint8_t type; /* any of EXFAT_ENTRY_xxx */
+ uint8_t data[31];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry) == 32);
+
+#define EXFAT_ENAME_MAX 15
+
+struct exfat_entry_bitmap /* allocated clusters bitmap */
+{
+ uint8_t type; /* EXFAT_ENTRY_BITMAP */
+ uint8_t __unknown1[19];
+ le32_t start_cluster;
+ le64_t size; /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);
+
+#define EXFAT_UPCASE_CHARS 0x10000
+
+struct exfat_entry_upcase /* upper case translation table */
+{
+ uint8_t type; /* EXFAT_ENTRY_UPCASE */
+ uint8_t __unknown1[3];
+ le32_t checksum;
+ uint8_t __unknown2[12];
+ le32_t start_cluster;
+ le64_t size; /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32);
+
+struct exfat_entry_label /* volume label */
+{
+ uint8_t type; /* EXFAT_ENTRY_LABEL */
+ uint8_t length; /* number of characters */
+ le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_label) == 32);
+
+#define EXFAT_ATTRIB_RO 0x01
+#define EXFAT_ATTRIB_HIDDEN 0x02
+#define EXFAT_ATTRIB_SYSTEM 0x04
+#define EXFAT_ATTRIB_VOLUME 0x08
+#define EXFAT_ATTRIB_DIR 0x10
+#define EXFAT_ATTRIB_ARCH 0x20
+
+struct exfat_entry_meta1 /* file or directory info (part 1) */
+{
+ uint8_t type; /* EXFAT_ENTRY_FILE */
+ uint8_t continuations;
+ le16_t checksum;
+ le16_t attrib; /* combination of EXFAT_ATTRIB_xxx */
+ le16_t __unknown1;
+ le16_t crtime, crdate; /* creation date and time */
+ le16_t mtime, mdate; /* latest modification date and time */
+ le16_t atime, adate; /* latest access date and time */
+ uint8_t crtime_cs; /* creation time in cs (centiseconds) */
+ uint8_t mtime_cs; /* latest modification time in cs */
+ uint8_t __unknown2[10];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);
+
+#define EXFAT_FLAG_ALWAYS1 (1u << 0)
+#define EXFAT_FLAG_CONTIGUOUS (1u << 1)
+
+struct exfat_entry_meta2 /* file or directory info (part 2) */
+{
+ uint8_t type; /* EXFAT_ENTRY_FILE_INFO */
+ uint8_t flags; /* combination of EXFAT_FLAG_xxx */
+ uint8_t __unknown1;
+ uint8_t name_length;
+ le16_t name_hash;
+ le16_t __unknown2;
+ le64_t valid_size; /* in bytes, less or equal to size */
+ uint8_t __unknown3[4];
+ le32_t start_cluster;
+ le64_t size; /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32);
+
+struct exfat_entry_name /* file or directory name */
+{
+ uint8_t type; /* EXFAT_ENTRY_FILE_NAME */
+ uint8_t __unknown;
+ le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_name) == 32);
+
+#endif /* ifndef EXFATFS_H_INCLUDED */
--- /dev/null
+/*
+ io.c (02.09.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#if defined(__APPLE__)
+#include <sys/disk.h>
+#elif defined(__OpenBSD__)
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/ioctl.h>
+#elif __linux__
+#include <sys/mount.h>
+#endif
+#ifdef USE_UBLIO
+#include <sys/uio.h>
+#include <ublio.h>
+#endif
+
+struct exfat_dev
+{
+ int fd;
+ enum exfat_mode mode;
+ off_t size; /* in bytes */
+#ifdef USE_UBLIO
+ off_t pos;
+ ublio_filehandle_t ufh;
+#endif
+};
+
+int g_vtoy_exfat_disk_fd = -1;
+uint64_t g_vtoy_exfat_part_size = 0;
+
+static bool is_open(int fd)
+{
+ return fcntl(fd, F_GETFD) != -1;
+}
+
+static int open_ro(const char* spec)
+{
+ return open(spec, O_RDONLY);
+}
+
+static int open_rw(const char* spec)
+{
+ int fd = open(spec, O_RDWR);
+#ifdef __linux__
+ int ro = 0;
+
+ /*
+ This ioctl is needed because after "blockdev --setro" kernel still
+ allows to open the device in read-write mode but fails writes.
+ */
+ if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
+ {
+ close(fd);
+ errno = EROFS;
+ return -1;
+ }
+#endif
+ return fd;
+}
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
+{
+ struct exfat_dev* dev;
+ struct stat stbuf;
+#ifdef USE_UBLIO
+ struct ublio_param up;
+#endif
+
+ /* The system allocates file descriptors sequentially. If we have been
+ started with stdin (0), stdout (1) or stderr (2) closed, the system
+ will give us descriptor 0, 1 or 2 later when we open block device,
+ FUSE communication pipe, etc. As a result, functions using stdin,
+ stdout or stderr will actually work with a different thing and can
+ corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */
+ while (!is_open(STDIN_FILENO)
+ || !is_open(STDOUT_FILENO)
+ || !is_open(STDERR_FILENO))
+ {
+ /* we don't need those descriptors, let them leak */
+ if (open("/dev/null", O_RDWR) == -1)
+ {
+ exfat_error("failed to open /dev/null");
+ return NULL;
+ }
+ }
+
+ dev = malloc(sizeof(struct exfat_dev));
+ if (dev == NULL)
+ {
+ exfat_error("failed to allocate memory for device structure");
+ return NULL;
+ }
+
+ switch (mode)
+ {
+ case EXFAT_MODE_RO:
+ dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
+ if (dev->fd == -1)
+ {
+ free(dev);
+ exfat_error("failed to open '%s' in read-only mode: %s", spec,
+ strerror(errno));
+ return NULL;
+ }
+ dev->mode = EXFAT_MODE_RO;
+ break;
+ case EXFAT_MODE_RW:
+ dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
+ if (dev->fd == -1)
+ {
+ free(dev);
+ exfat_error("failed to open '%s' in read-write mode: %s", spec,
+ strerror(errno));
+ return NULL;
+ }
+ dev->mode = EXFAT_MODE_RW;
+ break;
+ case EXFAT_MODE_ANY:
+ dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
+ if (dev->fd != -1)
+ {
+ dev->mode = EXFAT_MODE_RW;
+ break;
+ }
+ dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
+ if (dev->fd != -1)
+ {
+ dev->mode = EXFAT_MODE_RO;
+ exfat_warn("'%s' is write-protected, mounting read-only", spec);
+ break;
+ }
+ free(dev);
+ exfat_error("failed to open '%s': %s", spec, strerror(errno));
+ return NULL;
+ }
+
+ if (fstat(dev->fd, &stbuf) != 0)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to fstat '%s'", spec);
+ return NULL;
+ }
+ if (!S_ISBLK(stbuf.st_mode) &&
+ !S_ISCHR(stbuf.st_mode) &&
+ !S_ISREG(stbuf.st_mode))
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("'%s' is neither a device, nor a regular file", spec);
+ return NULL;
+ }
+
+#if defined(__APPLE__)
+ if (!S_ISREG(stbuf.st_mode))
+ {
+ uint32_t block_size = 0;
+ uint64_t blocks = 0;
+
+ if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to get block size");
+ return NULL;
+ }
+ if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to get blocks count");
+ return NULL;
+ }
+ dev->size = blocks * block_size;
+ }
+ else
+#elif defined(__OpenBSD__)
+ if (!S_ISREG(stbuf.st_mode))
+ {
+ struct disklabel lab;
+ struct partition* pp;
+ char* partition;
+
+ if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to get disklabel");
+ return NULL;
+ }
+
+ /* Don't need to check that partition letter is valid as we won't get
+ this far otherwise. */
+ partition = strchr(spec, '\0') - 1;
+ pp = &(lab.d_partitions[*partition - 'a']);
+ dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
+
+ if (pp->p_fstype != FS_NTFS)
+ exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
+ "you can fix this with fdisk(8)");
+ }
+ else
+#endif
+ {
+ /* works for Linux, FreeBSD, Solaris */
+ dev->size = exfat_seek(dev, 0, SEEK_END);
+ if (dev->size <= 0)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to get size of '%s'", spec);
+ return NULL;
+ }
+ if (exfat_seek(dev, 0, SEEK_SET) == -1)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to seek to the beginning of '%s'", spec);
+ return NULL;
+ }
+ }
+
+#ifdef USE_UBLIO
+ memset(&up, 0, sizeof(struct ublio_param));
+ up.up_blocksize = 256 * 1024;
+ up.up_items = 64;
+ up.up_grace = 32;
+ up.up_priv = &dev->fd;
+
+ dev->pos = 0;
+ dev->ufh = ublio_open(&up);
+ if (dev->ufh == NULL)
+ {
+ close(dev->fd);
+ free(dev);
+ exfat_error("failed to initialize ublio");
+ return NULL;
+ }
+#endif
+
+ return dev;
+}
+
+int exfat_close(struct exfat_dev* dev)
+{
+ int rc = 0;
+
+#ifdef USE_UBLIO
+ if (ublio_close(dev->ufh) != 0)
+ {
+ exfat_error("failed to close ublio");
+ rc = -EIO;
+ }
+#endif
+ if (dev->fd != g_vtoy_exfat_disk_fd)
+ {
+ if (close(dev->fd) != 0)
+ {
+ exfat_error("failed to close device: %s", strerror(errno));
+ rc = -EIO;
+ }
+ }
+
+ free(dev);
+ return rc;
+}
+
+int exfat_fsync(struct exfat_dev* dev)
+{
+ int rc = 0;
+
+#ifdef USE_UBLIO
+ if (ublio_fsync(dev->ufh) != 0)
+ {
+ exfat_error("ublio fsync failed");
+ rc = -EIO;
+ }
+#endif
+ if (fsync(dev->fd) != 0)
+ {
+ exfat_error("fsync failed: %s", strerror(errno));
+ rc = -EIO;
+ }
+ return rc;
+}
+
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
+{
+ return dev->mode;
+}
+
+
+off_t exfat_get_size(const struct exfat_dev* dev)
+{
+ return dev->size;
+}
+
+off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
+{
+#ifdef USE_UBLIO
+ /* XXX SEEK_CUR will be handled incorrectly */
+ return dev->pos = lseek(dev->fd, offset, whence);
+#else
+
+ if (SEEK_SET == whence)
+ {
+ if (offset > g_vtoy_exfat_part_size)
+ {
+ return -1;
+ }
+
+ lseek(dev->fd, 512 * 2048 + offset, SEEK_SET);
+ return offset;
+ }
+ else if (SEEK_END == whence)
+ {
+ if (offset == 0)
+ {
+ offset = 512 * 2048 + g_vtoy_exfat_part_size;
+ lseek(dev->fd, offset, SEEK_SET);
+ return (off_t)g_vtoy_exfat_part_size;
+ }
+ else
+ {
+ exfat_error("Invalid SEEK_END offset %llu", (unsigned long long)offset);
+ return -1;
+ }
+ }
+ else
+ {
+ exfat_error("Invalid seek whence %d", whence);
+ return lseek(dev->fd, offset, whence);
+ }
+#endif
+}
+
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+ ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
+ if (result >= 0)
+ dev->pos += size;
+ return result;
+#else
+ return read(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+ ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
+ if (result >= 0)
+ dev->pos += size;
+ return result;
+#else
+ return write(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+ off_t offset)
+{
+#ifdef USE_UBLIO
+ return ublio_pread(dev->ufh, buffer, size, offset);
+#else
+ return pread(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+ off_t offset)
+{
+#ifdef USE_UBLIO
+ return ublio_pwrite(dev->ufh, buffer, size, offset);
+#else
+ return pwrite(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+ void* buffer, size_t size, off_t offset)
+{
+ cluster_t cluster;
+ char* bufp = buffer;
+ off_t lsize, loffset, remainder;
+
+ if (offset >= node->size)
+ return 0;
+ if (size == 0)
+ return 0;
+
+ cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ {
+ exfat_error("invalid cluster 0x%x while reading", cluster);
+ return -EIO;
+ }
+
+ loffset = offset % CLUSTER_SIZE(*ef->sb);
+ remainder = MIN(size, node->size - offset);
+ while (remainder > 0)
+ {
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ {
+ exfat_error("invalid cluster 0x%x while reading", cluster);
+ return -EIO;
+ }
+ lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+ if (exfat_pread(ef->dev, bufp, lsize,
+ exfat_c2o(ef, cluster) + loffset) < 0)
+ {
+ exfat_error("failed to read cluster %#x", cluster);
+ return -EIO;
+ }
+ bufp += lsize;
+ loffset = 0;
+ remainder -= lsize;
+ cluster = exfat_next_cluster(ef, node, cluster);
+ }
+ if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
+ exfat_update_atime(node);
+ return MIN(size, node->size - offset) - remainder;
+}
+
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+ const void* buffer, size_t size, off_t offset)
+{
+ int rc;
+ cluster_t cluster;
+ const char* bufp = buffer;
+ off_t lsize, loffset, remainder;
+
+ if (offset > node->size)
+ {
+ rc = exfat_truncate(ef, node, offset, true);
+ if (rc != 0)
+ return rc;
+ }
+ if (offset + size > node->size)
+ {
+ rc = exfat_truncate(ef, node, offset + size, false);
+ if (rc != 0)
+ return rc;
+ }
+ if (size == 0)
+ return 0;
+
+ cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ {
+ exfat_error("invalid cluster 0x%x while writing", cluster);
+ return -EIO;
+ }
+
+ loffset = offset % CLUSTER_SIZE(*ef->sb);
+ remainder = size;
+ while (remainder > 0)
+ {
+ if (CLUSTER_INVALID(*ef->sb, cluster))
+ {
+ exfat_error("invalid cluster 0x%x while writing", cluster);
+ return -EIO;
+ }
+ lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+ if (exfat_pwrite(ef->dev, bufp, lsize,
+ exfat_c2o(ef, cluster) + loffset) < 0)
+ {
+ exfat_error("failed to write cluster %#x", cluster);
+ return -EIO;
+ }
+ bufp += lsize;
+ loffset = 0;
+ remainder -= lsize;
+ cluster = exfat_next_cluster(ef, node, cluster);
+ }
+ if (!(node->attrib & EXFAT_ATTRIB_DIR))
+ /* directory's mtime should be updated by the caller only when it
+ creates or removes something in this directory */
+ exfat_update_mtime(node);
+ return size - remainder;
+}
--- /dev/null
+/*
+ lookup.c (02.09.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+ struct exfat_iterator* it)
+{
+ int rc;
+
+ exfat_get_node(dir);
+ it->parent = dir;
+ it->current = NULL;
+ rc = exfat_cache_directory(ef, dir);
+ if (rc != 0)
+ exfat_put_node(ef, dir);
+ return rc;
+}
+
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it)
+{
+ exfat_put_node(ef, it->parent);
+ it->parent = NULL;
+ it->current = NULL;
+}
+
+struct exfat_node* exfat_readdir(struct exfat_iterator* it)
+{
+ if (it->current == NULL)
+ it->current = it->parent->child;
+ else
+ it->current = it->current->next;
+
+ if (it->current != NULL)
+ return exfat_get_node(it->current);
+ else
+ return NULL;
+}
+
+static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
+{
+ return (int) ef->upcase[a] - (int) ef->upcase[b];
+}
+
+static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
+{
+ while (le16_to_cpu(*a) && le16_to_cpu(*b))
+ {
+ int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+ if (rc != 0)
+ return rc;
+ a++;
+ b++;
+ }
+ return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+}
+
+static int lookup_name(struct exfat* ef, struct exfat_node* parent,
+ struct exfat_node** node, const char* name, size_t n)
+{
+ struct exfat_iterator it;
+ le16_t buffer[EXFAT_NAME_MAX + 1];
+ int rc;
+
+ *node = NULL;
+
+ rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX + 1, n);
+ if (rc != 0)
+ return rc;
+
+ rc = exfat_opendir(ef, parent, &it);
+ if (rc != 0)
+ return rc;
+ while ((*node = exfat_readdir(&it)))
+ {
+ if (compare_name(ef, buffer, (*node)->name) == 0)
+ {
+ exfat_closedir(ef, &it);
+ return 0;
+ }
+ exfat_put_node(ef, *node);
+ }
+ exfat_closedir(ef, &it);
+ return -ENOENT;
+}
+
+static size_t get_comp(const char* path, const char** comp)
+{
+ const char* end;
+
+ *comp = path + strspn(path, "/"); /* skip leading slashes */
+ end = strchr(*comp, '/');
+ if (end == NULL)
+ return strlen(*comp);
+ else
+ return end - *comp;
+}
+
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+ const char* path)
+{
+ struct exfat_node* parent;
+ const char* p;
+ size_t n;
+ int rc;
+
+ /* start from the root directory */
+ parent = *node = exfat_get_node(ef->root);
+ for (p = path; (n = get_comp(p, &p)); p += n)
+ {
+ if (n == 1 && *p == '.') /* skip "." component */
+ continue;
+ rc = lookup_name(ef, parent, node, p, n);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, parent);
+ return rc;
+ }
+ exfat_put_node(ef, parent);
+ parent = *node;
+ }
+ return 0;
+}
+
+static bool is_last_comp(const char* comp, size_t length)
+{
+ const char* p = comp + length;
+
+ return get_comp(p, &p) == 0;
+}
+
+static bool is_allowed(const char* comp, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; i++)
+ switch (comp[i])
+ {
+ case 0x01 ... 0x1f:
+ case '/':
+ case '\\':
+ case ':':
+ case '*':
+ case '?':
+ case '"':
+ case '<':
+ case '>':
+ case '|':
+ return false;
+ }
+ return true;
+}
+
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+ struct exfat_node** node, le16_t* name, const char* path)
+{
+ const char* p;
+ size_t n;
+ int rc;
+
+ memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+ *parent = *node = exfat_get_node(ef->root);
+ for (p = path; (n = get_comp(p, &p)); p += n)
+ {
+ if (n == 1 && *p == '.')
+ continue;
+ if (is_last_comp(p, n))
+ {
+ if (!is_allowed(p, n))
+ {
+ /* contains characters that are not allowed */
+ exfat_put_node(ef, *parent);
+ return -ENOENT;
+ }
+ rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX + 1, n);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, *parent);
+ return rc;
+ }
+
+ rc = lookup_name(ef, *parent, node, p, n);
+ if (rc != 0 && rc != -ENOENT)
+ {
+ exfat_put_node(ef, *parent);
+ return rc;
+ }
+ return 0;
+ }
+ rc = lookup_name(ef, *parent, node, p, n);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, *parent);
+ return rc;
+ }
+ exfat_put_node(ef, *parent);
+ *parent = *node;
+ }
+ exfat_bug("impossible");
+
+ return 1;
+}
--- /dev/null
+/*
+ mount.c (22.10.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+static uint64_t rootdir_size(const struct exfat* ef)
+{
+ uint32_t clusters = 0;
+ uint32_t clusters_max = le32_to_cpu(ef->sb->cluster_count);
+ cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+
+ /* Iterate all clusters of the root directory to calculate its size.
+ It can't be contiguous because there is no flag to indicate this. */
+ do
+ {
+ if (clusters == clusters_max) /* infinite loop detected */
+ {
+ exfat_error("root directory cannot occupy all %d clusters",
+ clusters);
+ return 0;
+ }
+ if (CLUSTER_INVALID(*ef->sb, rootdir_cluster))
+ {
+ exfat_error("bad cluster %#x while reading root directory",
+ rootdir_cluster);
+ return 0;
+ }
+ rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
+ clusters++;
+ }
+ while (rootdir_cluster != EXFAT_CLUSTER_END);
+
+ return (uint64_t) clusters * CLUSTER_SIZE(*ef->sb);
+}
+
+static const char* get_option(const char* options, const char* option_name)
+{
+ const char* p;
+ size_t length = strlen(option_name);
+
+ for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+ if ((p == options || p[-1] == ',') && p[length] == '=')
+ return p + length + 1;
+ return NULL;
+}
+
+static int get_int_option(const char* options, const char* option_name,
+ int base, int default_value)
+{
+ const char* p = get_option(options, option_name);
+
+ if (p == NULL)
+ return default_value;
+ return strtol(p, NULL, base);
+}
+
+static bool match_option(const char* options, const char* option_name)
+{
+ const char* p;
+ size_t length = strlen(option_name);
+
+ for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+ if ((p == options || p[-1] == ',') &&
+ (p[length] == ',' || p[length] == '\0'))
+ return true;
+ return false;
+}
+
+static void parse_options(struct exfat* ef, const char* options)
+{
+ int opt_umask;
+
+ opt_umask = get_int_option(options, "umask", 8, 0);
+ ef->dmask = get_int_option(options, "dmask", 8, opt_umask);
+ ef->fmask = get_int_option(options, "fmask", 8, opt_umask);
+
+ ef->uid = get_int_option(options, "uid", 10, geteuid());
+ ef->gid = get_int_option(options, "gid", 10, getegid());
+
+ ef->noatime = match_option(options, "noatime");
+
+ switch (get_int_option(options, "repair", 10, 0))
+ {
+ case 1:
+ ef->repair = EXFAT_REPAIR_ASK;
+ break;
+ case 2:
+ ef->repair = EXFAT_REPAIR_YES;
+ break;
+ default:
+ ef->repair = EXFAT_REPAIR_NO;
+ break;
+ }
+}
+
+static bool verify_vbr_checksum(const struct exfat* ef, void* sector)
+{
+ off_t sector_size = SECTOR_SIZE(*ef->sb);
+ uint32_t vbr_checksum;
+ int i;
+
+ if (exfat_pread(ef->dev, sector, sector_size, 0) < 0)
+ {
+ exfat_error("failed to read boot sector");
+ return false;
+ }
+ vbr_checksum = exfat_vbr_start_checksum(sector, sector_size);
+ for (i = 1; i < 11; i++)
+ {
+ if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
+ {
+ exfat_error("failed to read VBR sector");
+ return false;
+ }
+ vbr_checksum = exfat_vbr_add_checksum(sector, sector_size,
+ vbr_checksum);
+ }
+ if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
+ {
+ exfat_error("failed to read VBR checksum sector");
+ return false;
+ }
+ for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
+ if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum)
+ {
+ exfat_error("invalid VBR checksum 0x%x (expected 0x%x)",
+ le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum);
+ if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum))
+ return false;
+ }
+ return true;
+}
+
+static int commit_super_block(const struct exfat* ef)
+{
+ if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+ {
+ exfat_error("failed to write super block");
+ return 1;
+ }
+ return exfat_fsync(ef->dev);
+}
+
+static int prepare_super_block(const struct exfat* ef)
+{
+ if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED)
+ exfat_warn("volume was not unmounted cleanly");
+
+ if (ef->ro)
+ return 0;
+
+ ef->sb->volume_state = cpu_to_le16(
+ le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED);
+ return commit_super_block(ef);
+}
+
+static void exfat_free(struct exfat* ef)
+{
+ exfat_close(ef->dev); /* first of all, close the descriptor */
+ ef->dev = NULL; /* struct exfat_dev is freed by exfat_close() */
+ free(ef->root);
+ ef->root = NULL;
+ free(ef->zero_cluster);
+ ef->zero_cluster = NULL;
+ free(ef->cmap.chunk);
+ ef->cmap.chunk = NULL;
+ free(ef->upcase);
+ ef->upcase = NULL;
+ free(ef->sb);
+ ef->sb = NULL;
+}
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options)
+{
+ int rc;
+ enum exfat_mode mode;
+
+ exfat_tzset();
+ memset(ef, 0, sizeof(struct exfat));
+
+ parse_options(ef, options);
+
+ if (match_option(options, "ro"))
+ mode = EXFAT_MODE_RO;
+ else if (match_option(options, "ro_fallback"))
+ mode = EXFAT_MODE_ANY;
+ else
+ mode = EXFAT_MODE_RW;
+ ef->dev = exfat_open(spec, mode);
+ if (ef->dev == NULL)
+ return -EIO;
+ if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO)
+ {
+ if (mode == EXFAT_MODE_ANY)
+ ef->ro = -1;
+ else
+ ef->ro = 1;
+ }
+
+ ef->sb = malloc(sizeof(struct exfat_super_block));
+ if (ef->sb == NULL)
+ {
+ exfat_error("failed to allocate memory for the super block");
+ exfat_free(ef);
+ return -ENOMEM;
+ }
+ memset(ef->sb, 0, sizeof(struct exfat_super_block));
+
+ if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+ {
+ exfat_error("failed to read boot sector");
+ exfat_free(ef);
+ return -EIO;
+ }
+ if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0)
+ {
+ exfat_error("exFAT file system is not found");
+ exfat_free(ef);
+ return -EIO;
+ }
+ /* sector cannot be smaller than 512 bytes */
+ if (ef->sb->sector_bits < 9)
+ {
+ exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits);
+ exfat_free(ef);
+ return -EIO;
+ }
+ /* officially exFAT supports cluster size up to 32 MB */
+ if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25)
+ {
+ exfat_error("too big cluster size: 2^(%hhd+%hhd)",
+ ef->sb->sector_bits, ef->sb->spc_bits);
+ exfat_free(ef);
+ return -EIO;
+ }
+ ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb));
+ if (ef->zero_cluster == NULL)
+ {
+ exfat_error("failed to allocate zero sector");
+ exfat_free(ef);
+ return -ENOMEM;
+ }
+ /* use zero_cluster as a temporary buffer for VBR checksum verification */
+ if (!verify_vbr_checksum(ef, ef->zero_cluster))
+ {
+ exfat_free(ef);
+ return -EIO;
+ }
+ memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb));
+ if (ef->sb->version.major != 1 || ef->sb->version.minor != 0)
+ {
+ exfat_error("unsupported exFAT version: %hhu.%hhu",
+ ef->sb->version.major, ef->sb->version.minor);
+ exfat_free(ef);
+ return -EIO;
+ }
+ if (ef->sb->fat_count != 1)
+ {
+ exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count);
+ exfat_free(ef);
+ return -EIO;
+ }
+ if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) >
+ exfat_get_size(ef->dev))
+ {
+ /* this can cause I/O errors later but we don't fail mounting to let
+ user rescue data */
+ exfat_warn("file system in sectors is larger than device: "
+ "%"PRIu64" * %d > %"PRIu64,
+ le64_to_cpu(ef->sb->sector_count), SECTOR_SIZE(*ef->sb),
+ exfat_get_size(ef->dev));
+ }
+ if ((off_t) le32_to_cpu(ef->sb->cluster_count) * CLUSTER_SIZE(*ef->sb) >
+ exfat_get_size(ef->dev))
+ {
+ exfat_error("file system in clusters is larger than device: "
+ "%u * %d > %"PRIu64,
+ le32_to_cpu(ef->sb->cluster_count), CLUSTER_SIZE(*ef->sb),
+ exfat_get_size(ef->dev));
+ exfat_free(ef);
+ return -EIO;
+ }
+
+ ef->root = malloc(sizeof(struct exfat_node));
+ if (ef->root == NULL)
+ {
+ exfat_error("failed to allocate root node");
+ exfat_free(ef);
+ return -ENOMEM;
+ }
+ memset(ef->root, 0, sizeof(struct exfat_node));
+ ef->root->attrib = EXFAT_ATTRIB_DIR;
+ ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+ ef->root->fptr_cluster = ef->root->start_cluster;
+ ef->root->name[0] = cpu_to_le16('\0');
+ ef->root->size = rootdir_size(ef);
+ if (ef->root->size == 0)
+ {
+ exfat_free(ef);
+ return -EIO;
+ }
+ /* exFAT does not have time attributes for the root directory */
+ ef->root->mtime = 0;
+ ef->root->atime = 0;
+ /* always keep at least 1 reference to the root node */
+ exfat_get_node(ef->root);
+
+ rc = exfat_cache_directory(ef, ef->root);
+ if (rc != 0)
+ goto error;
+ if (ef->upcase == NULL)
+ {
+ exfat_error("upcase table is not found");
+ goto error;
+ }
+ if (ef->cmap.chunk == NULL)
+ {
+ exfat_error("clusters bitmap is not found");
+ goto error;
+ }
+
+ if (prepare_super_block(ef) != 0)
+ goto error;
+
+ return 0;
+
+error:
+ exfat_put_node(ef, ef->root);
+ exfat_reset_cache(ef);
+ exfat_free(ef);
+ return -EIO;
+}
+
+static void finalize_super_block(struct exfat* ef)
+{
+ if (ef->ro)
+ return;
+
+ ef->sb->volume_state = cpu_to_le16(
+ le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED);
+
+ /* Some implementations set the percentage of allocated space to 0xff
+ on FS creation and never update it. In this case leave it as is. */
+ if (ef->sb->allocated_percent != 0xff)
+ {
+ uint32_t free, total;
+
+ free = exfat_count_free_clusters(ef);
+ total = le32_to_cpu(ef->sb->cluster_count);
+ ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total;
+ }
+
+ commit_super_block(ef); /* ignore return code */
+}
+
+void exfat_unmount(struct exfat* ef)
+{
+ exfat_flush_nodes(ef); /* ignore return code */
+ exfat_flush(ef); /* ignore return code */
+ exfat_put_node(ef, ef->root);
+ exfat_reset_cache(ef);
+ finalize_super_block(ef);
+ exfat_free(ef); /* will close the descriptor */
+}
--- /dev/null
+/*
+ node.c (09.10.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define EXFAT_ENTRY_NONE (-1)
+
+struct exfat_node* exfat_get_node(struct exfat_node* node)
+{
+ /* if we switch to multi-threaded mode we will need atomic
+ increment here and atomic decrement in exfat_put_node() */
+ node->references++;
+ return node;
+}
+
+void exfat_put_node(struct exfat* ef, struct exfat_node* node)
+{
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+
+ --node->references;
+ if (node->references < 0)
+ {
+ exfat_get_name(node, buffer);
+ exfat_bug("reference counter of '%s' is below zero", buffer);
+ }
+ else if (node->references == 0 && node != ef->root)
+ {
+ if (node->is_dirty)
+ {
+ exfat_get_name(node, buffer);
+ exfat_warn("dirty node '%s' with zero references", buffer);
+ }
+ }
+}
+
+/**
+ * This function must be called on rmdir and unlink (after the last
+ * exfat_put_node()) to free clusters.
+ */
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node)
+{
+ int rc = 0;
+
+ if (node->references != 0)
+ exfat_bug("unable to cleanup a node with %d references",
+ node->references);
+
+ if (node->is_unlinked)
+ {
+ /* free all clusters and node structure itself */
+ rc = exfat_truncate(ef, node, 0, true);
+ /* free the node even in case of error or its memory will be lost */
+ free(node);
+ }
+ return rc;
+}
+
+static int read_entries(struct exfat* ef, struct exfat_node* dir,
+ struct exfat_entry* entries, int n, off_t offset)
+{
+ ssize_t size;
+
+ if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+ exfat_bug("attempted to read entries from a file");
+
+ size = exfat_generic_pread(ef, dir, entries,
+ sizeof(struct exfat_entry[n]), offset);
+ if (size == sizeof(struct exfat_entry[n]))
+ return 0; /* success */
+ if (size == 0)
+ return -ENOENT;
+ if (size < 0)
+ return -EIO;
+ exfat_error("read %zd bytes instead of %zu bytes", size,
+ sizeof(struct exfat_entry[n]));
+ return -EIO;
+}
+
+static int write_entries(struct exfat* ef, struct exfat_node* dir,
+ const struct exfat_entry* entries, int n, off_t offset)
+{
+ ssize_t size;
+
+ if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+ exfat_bug("attempted to write entries into a file");
+
+ size = exfat_generic_pwrite(ef, dir, entries,
+ sizeof(struct exfat_entry[n]), offset);
+ if (size == sizeof(struct exfat_entry[n]))
+ return 0; /* success */
+ if (size < 0)
+ return -EIO;
+ exfat_error("wrote %zd bytes instead of %zu bytes", size,
+ sizeof(struct exfat_entry[n]));
+ return -EIO;
+}
+
+static struct exfat_node* allocate_node(void)
+{
+ struct exfat_node* node = malloc(sizeof(struct exfat_node));
+ if (node == NULL)
+ {
+ exfat_error("failed to allocate node");
+ return NULL;
+ }
+ memset(node, 0, sizeof(struct exfat_node));
+ return node;
+}
+
+static void init_node_meta1(struct exfat_node* node,
+ const struct exfat_entry_meta1* meta1)
+{
+ node->attrib = le16_to_cpu(meta1->attrib);
+ node->continuations = meta1->continuations;
+ node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime,
+ meta1->mtime_cs);
+ /* there is no centiseconds field for atime */
+ node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0);
+}
+
+static void init_node_meta2(struct exfat_node* node,
+ const struct exfat_entry_meta2* meta2)
+{
+ node->size = le64_to_cpu(meta2->size);
+ node->start_cluster = le32_to_cpu(meta2->start_cluster);
+ node->fptr_cluster = node->start_cluster;
+ node->is_contiguous = ((meta2->flags & EXFAT_FLAG_CONTIGUOUS) != 0);
+}
+
+static void init_node_name(struct exfat_node* node,
+ const struct exfat_entry* entries, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ memcpy(node->name + i * EXFAT_ENAME_MAX,
+ ((const struct exfat_entry_name*) &entries[i])->name,
+ EXFAT_ENAME_MAX * sizeof(le16_t));
+}
+
+static bool check_entries(const struct exfat_entry* entry, int n)
+{
+ int previous = EXFAT_ENTRY_NONE;
+ int current;
+ int i;
+
+ /* check transitions between entries types */
+ for (i = 0; i < n + 1; previous = current, i++)
+ {
+ bool valid = false;
+
+ current = (i < n) ? entry[i].type : EXFAT_ENTRY_NONE;
+ switch (previous)
+ {
+ case EXFAT_ENTRY_NONE:
+ valid = (current == EXFAT_ENTRY_FILE);
+ break;
+ case EXFAT_ENTRY_FILE:
+ valid = (current == EXFAT_ENTRY_FILE_INFO);
+ break;
+ case EXFAT_ENTRY_FILE_INFO:
+ valid = (current == EXFAT_ENTRY_FILE_NAME);
+ break;
+ case EXFAT_ENTRY_FILE_NAME:
+ valid = (current == EXFAT_ENTRY_FILE_NAME ||
+ current == EXFAT_ENTRY_NONE ||
+ current >= EXFAT_ENTRY_FILE_TAIL);
+ break;
+ case EXFAT_ENTRY_FILE_TAIL ... 0xff:
+ valid = (current >= EXFAT_ENTRY_FILE_TAIL ||
+ current == EXFAT_ENTRY_NONE);
+ break;
+ }
+
+ if (!valid)
+ {
+ exfat_error("unexpected entry type %#x after %#x at %d/%d",
+ current, previous, i, n);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool check_node(const struct exfat* ef, struct exfat_node* node,
+ le16_t actual_checksum, const struct exfat_entry_meta1* meta1,
+ const struct exfat_entry_meta2* meta2)
+{
+ int cluster_size = CLUSTER_SIZE(*ef->sb);
+ uint64_t clusters_heap_size =
+ (uint64_t) le32_to_cpu(ef->sb->cluster_count) * cluster_size;
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+ bool ret = true;
+
+ /*
+ Validate checksum first. If it's invalid all other fields probably
+ contain just garbage.
+ */
+ if (le16_to_cpu(actual_checksum) != le16_to_cpu(meta1->checksum))
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer,
+ le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum));
+ if (!EXFAT_REPAIR(invalid_node_checksum, ef, node))
+ ret = false;
+ }
+
+ /*
+ exFAT does not support sparse files but allows files with uninitialized
+ clusters. For such files valid_size means initialized data size and
+ cannot be greater than file size. See SetFileValidData() function
+ description in MSDN.
+ */
+ if (le64_to_cpu(meta2->valid_size) > node->size)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' has valid size (%"PRIu64") greater than size "
+ "(%"PRIu64")", buffer, le64_to_cpu(meta2->valid_size),
+ node->size);
+ ret = false;
+ }
+
+ /*
+ Empty file must have zero start cluster. Non-empty file must start
+ with a valid cluster. Directories cannot be empty (i.e. must always
+ have a valid start cluster), but we will check this later while
+ reading that directory to give user a chance to read this directory.
+ */
+ if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is empty but start cluster is %#x", buffer,
+ node->start_cluster);
+ ret = false;
+ }
+ if (node->size > 0 && CLUSTER_INVALID(*ef->sb, node->start_cluster))
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' points to invalid cluster %#x", buffer,
+ node->start_cluster);
+ ret = false;
+ }
+
+ /* File or directory cannot be larger than clusters heap. */
+ if (node->size > clusters_heap_size)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is larger than clusters heap: %"PRIu64" > %"PRIu64,
+ buffer, node->size, clusters_heap_size);
+ ret = false;
+ }
+
+ /* Empty file or directory must be marked as non-contiguous. */
+ if (node->size == 0 && node->is_contiguous)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is empty but marked as contiguous (%#hx)", buffer,
+ node->attrib);
+ ret = false;
+ }
+
+ /* Directory size must be aligned on at cluster boundary. */
+ if ((node->attrib & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer,
+ node->size, cluster_size);
+ ret = false;
+ }
+
+ return ret;
+}
+
+static int parse_file_entries(struct exfat* ef, struct exfat_node* node,
+ const struct exfat_entry* entries, int n)
+{
+ const struct exfat_entry_meta1* meta1;
+ const struct exfat_entry_meta2* meta2;
+ int mandatory_entries;
+
+ if (!check_entries(entries, n))
+ return -EIO;
+
+ meta1 = (const struct exfat_entry_meta1*) &entries[0];
+ if (meta1->continuations < 2)
+ {
+ exfat_error("too few continuations (%hhu)", meta1->continuations);
+ return -EIO;
+ }
+ meta2 = (const struct exfat_entry_meta2*) &entries[1];
+ if (meta2->flags & ~(EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS))
+ {
+ exfat_error("unknown flags in meta2 (%#hhx)", meta2->flags);
+ return -EIO;
+ }
+ mandatory_entries = 2 + DIV_ROUND_UP(meta2->name_length, EXFAT_ENAME_MAX);
+ if (meta1->continuations < mandatory_entries - 1)
+ {
+ exfat_error("too few continuations (%hhu < %d)",
+ meta1->continuations, mandatory_entries - 1);
+ return -EIO;
+ }
+
+ init_node_meta1(node, meta1);
+ init_node_meta2(node, meta2);
+ init_node_name(node, entries + 2, mandatory_entries - 2);
+
+ if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2))
+ return -EIO;
+
+ return 0;
+}
+
+static int parse_file_entry(struct exfat* ef, struct exfat_node* parent,
+ struct exfat_node** node, off_t* offset, int n)
+{
+ struct exfat_entry entries[n];
+ int rc;
+
+ rc = read_entries(ef, parent, entries, n, *offset);
+ if (rc != 0)
+ return rc;
+
+ /* a new node has zero references */
+ *node = allocate_node();
+ if (*node == NULL)
+ return -ENOMEM;
+ (*node)->entry_offset = *offset;
+
+ rc = parse_file_entries(ef, *node, entries, n);
+ if (rc != 0)
+ {
+ free(*node);
+ return rc;
+ }
+
+ *offset += sizeof(struct exfat_entry[n]);
+ return 0;
+}
+
+static void decompress_upcase(uint16_t* output, const le16_t* source,
+ size_t size)
+{
+ size_t si;
+ size_t oi;
+
+ for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++)
+ output[oi] = oi;
+
+ for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++)
+ {
+ uint16_t ch = le16_to_cpu(source[si]);
+
+ if (ch == 0xffff && si + 1 < size) /* indicates a run */
+ oi += le16_to_cpu(source[++si]);
+ else
+ output[oi++] = ch;
+ }
+}
+
+/*
+ * Read one entry in a directory at offset position and build a new node
+ * structure.
+ */
+static int readdir(struct exfat* ef, struct exfat_node* parent,
+ struct exfat_node** node, off_t* offset)
+{
+ int rc;
+ struct exfat_entry entry;
+ const struct exfat_entry_meta1* meta1;
+ const struct exfat_entry_upcase* upcase;
+ const struct exfat_entry_bitmap* bitmap;
+ const struct exfat_entry_label* label;
+ uint64_t upcase_size = 0;
+ le16_t* upcase_comp = NULL;
+
+ for (;;)
+ {
+ rc = read_entries(ef, parent, &entry, 1, *offset);
+ if (rc != 0)
+ return rc;
+
+ switch (entry.type)
+ {
+ case EXFAT_ENTRY_FILE:
+ meta1 = (const struct exfat_entry_meta1*) &entry;
+ return parse_file_entry(ef, parent, node, offset,
+ 1 + meta1->continuations);
+
+ case EXFAT_ENTRY_UPCASE:
+ if (ef->upcase != NULL)
+ break;
+ upcase = (const struct exfat_entry_upcase*) &entry;
+ if (CLUSTER_INVALID(*ef->sb, le32_to_cpu(upcase->start_cluster)))
+ {
+ exfat_error("invalid cluster 0x%x in upcase table",
+ le32_to_cpu(upcase->start_cluster));
+ return -EIO;
+ }
+ upcase_size = le64_to_cpu(upcase->size);
+ if (upcase_size == 0 ||
+ upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) ||
+ upcase_size % sizeof(uint16_t) != 0)
+ {
+ exfat_error("bad upcase table size (%"PRIu64" bytes)",
+ upcase_size);
+ return -EIO;
+ }
+ upcase_comp = malloc(upcase_size);
+ if (upcase_comp == NULL)
+ {
+ exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
+ upcase_size);
+ return -ENOMEM;
+ }
+
+ /* read compressed upcase table */
+ if (exfat_pread(ef->dev, upcase_comp, upcase_size,
+ exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0)
+ {
+ free(upcase_comp);
+ exfat_error("failed to read upper case table "
+ "(%"PRIu64" bytes starting at cluster %#x)",
+ upcase_size,
+ le32_to_cpu(upcase->start_cluster));
+ return -EIO;
+ }
+
+ /* decompress upcase table */
+ ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t));
+ if (ef->upcase == NULL)
+ {
+ free(upcase_comp);
+ exfat_error("failed to allocate decompressed upcase table");
+ return -ENOMEM;
+ }
+ decompress_upcase(ef->upcase, upcase_comp,
+ upcase_size / sizeof(uint16_t));
+ free(upcase_comp);
+ break;
+
+ case EXFAT_ENTRY_BITMAP:
+ bitmap = (const struct exfat_entry_bitmap*) &entry;
+ ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster);
+ if (CLUSTER_INVALID(*ef->sb, ef->cmap.start_cluster))
+ {
+ exfat_error("invalid cluster 0x%x in clusters bitmap",
+ ef->cmap.start_cluster);
+ return -EIO;
+ }
+ ef->cmap.size = le32_to_cpu(ef->sb->cluster_count);
+ if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8))
+ {
+ exfat_error("invalid clusters bitmap size: %"PRIu64
+ " (expected at least %u)",
+ le64_to_cpu(bitmap->size),
+ DIV_ROUND_UP(ef->cmap.size, 8));
+ return -EIO;
+ }
+ /* FIXME bitmap can be rather big, up to 512 MB */
+ ef->cmap.chunk_size = ef->cmap.size;
+ ef->cmap.chunk = malloc(BMAP_SIZE(ef->cmap.chunk_size));
+ if (ef->cmap.chunk == NULL)
+ {
+ exfat_error("failed to allocate clusters bitmap chunk "
+ "(%"PRIu64" bytes)", le64_to_cpu(bitmap->size));
+ return -ENOMEM;
+ }
+
+ if (exfat_pread(ef->dev, ef->cmap.chunk,
+ BMAP_SIZE(ef->cmap.chunk_size),
+ exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+ {
+ exfat_error("failed to read clusters bitmap "
+ "(%"PRIu64" bytes starting at cluster %#x)",
+ le64_to_cpu(bitmap->size), ef->cmap.start_cluster);
+ return -EIO;
+ }
+ break;
+
+ case EXFAT_ENTRY_LABEL:
+ label = (const struct exfat_entry_label*) &entry;
+ if (label->length > EXFAT_ENAME_MAX)
+ {
+ exfat_error("too long label (%hhu chars)", label->length);
+ return -EIO;
+ }
+ if (utf16_to_utf8(ef->label, label->name,
+ sizeof(ef->label), EXFAT_ENAME_MAX) != 0)
+ return -EIO;
+ break;
+
+ default:
+ if (!(entry.type & EXFAT_ENTRY_VALID))
+ break; /* deleted entry, ignore it */
+
+ exfat_error("unknown entry type %#hhx", entry.type);
+ if (!EXFAT_REPAIR(unknown_entry, ef, parent, &entry, *offset))
+ return -EIO;
+ }
+ *offset += sizeof(entry);
+ }
+ /* we never reach here */
+}
+
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir)
+{
+ off_t offset = 0;
+ int rc;
+ struct exfat_node* node;
+ struct exfat_node* current = NULL;
+
+ if (dir->is_cached)
+ return 0; /* already cached */
+
+ while ((rc = readdir(ef, dir, &node, &offset)) == 0)
+ {
+ node->parent = dir;
+ if (current != NULL)
+ {
+ current->next = node;
+ node->prev = current;
+ }
+ else
+ dir->child = node;
+
+ current = node;
+ }
+
+ if (rc != -ENOENT)
+ {
+ /* rollback */
+ for (current = dir->child; current; current = node)
+ {
+ node = current->next;
+ free(current);
+ }
+ dir->child = NULL;
+ return rc;
+ }
+
+ dir->is_cached = true;
+ return 0;
+}
+
+static void tree_attach(struct exfat_node* dir, struct exfat_node* node)
+{
+ node->parent = dir;
+ if (dir->child)
+ {
+ dir->child->prev = node;
+ node->next = dir->child;
+ }
+ dir->child = node;
+}
+
+static void tree_detach(struct exfat_node* node)
+{
+ if (node->prev)
+ node->prev->next = node->next;
+ else /* this is the first node in the list */
+ node->parent->child = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+ node->parent = NULL;
+ node->prev = NULL;
+ node->next = NULL;
+}
+
+static void reset_cache(struct exfat* ef, struct exfat_node* node)
+{
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+
+ while (node->child)
+ {
+ struct exfat_node* p = node->child;
+ reset_cache(ef, p);
+ tree_detach(p);
+ free(p);
+ }
+ node->is_cached = false;
+ if (node->references != 0)
+ {
+ exfat_get_name(node, buffer);
+ exfat_warn("non-zero reference counter (%d) for '%s'",
+ node->references, buffer);
+ }
+ if (node != ef->root && node->is_dirty)
+ {
+ exfat_get_name(node, buffer);
+ exfat_bug("node '%s' is dirty", buffer);
+ }
+ while (node->references)
+ exfat_put_node(ef, node);
+}
+
+void exfat_reset_cache(struct exfat* ef)
+{
+ reset_cache(ef, ef->root);
+}
+
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
+{
+ struct exfat_entry entries[1 + node->continuations];
+ struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+ struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+ int rc;
+
+ if (!node->is_dirty)
+ return 0; /* no need to flush */
+
+ if (ef->ro)
+ exfat_bug("unable to flush node to read-only FS");
+
+ if (node->parent == NULL)
+ return 0; /* do not flush unlinked node */
+
+ rc = read_entries(ef, node->parent, entries, 1 + node->continuations,
+ node->entry_offset);
+ if (rc != 0)
+ return rc;
+ if (!check_entries(entries, 1 + node->continuations))
+ return -EIO;
+
+ meta1->attrib = cpu_to_le16(node->attrib);
+ exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime,
+ &meta1->mtime_cs);
+ exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL);
+ meta2->size = meta2->valid_size = cpu_to_le64(node->size);
+ meta2->start_cluster = cpu_to_le32(node->start_cluster);
+ meta2->flags = EXFAT_FLAG_ALWAYS1;
+ /* empty files must not be marked as contiguous */
+ if (node->size != 0 && node->is_contiguous)
+ meta2->flags |= EXFAT_FLAG_CONTIGUOUS;
+ /* name hash remains unchanged, no need to recalculate it */
+
+ meta1->checksum = exfat_calc_checksum(entries, 1 + node->continuations);
+ rc = write_entries(ef, node->parent, entries, 1 + node->continuations,
+ node->entry_offset);
+ if (rc != 0)
+ return rc;
+
+ node->is_dirty = false;
+ return exfat_flush(ef);
+}
+
+static int erase_entries(struct exfat* ef, struct exfat_node* dir, int n,
+ off_t offset)
+{
+ struct exfat_entry entries[n];
+ int rc;
+ int i;
+
+ rc = read_entries(ef, dir, entries, n, offset);
+ if (rc != 0)
+ return rc;
+ for (i = 0; i < n; i++)
+ entries[i].type &= ~EXFAT_ENTRY_VALID;
+ return write_entries(ef, dir, entries, n, offset);
+}
+
+static int erase_node(struct exfat* ef, struct exfat_node* node)
+{
+ int rc;
+
+ exfat_get_node(node->parent);
+ rc = erase_entries(ef, node->parent, 1 + node->continuations,
+ node->entry_offset);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, node->parent);
+ return rc;
+ }
+ rc = exfat_flush_node(ef, node->parent);
+ exfat_put_node(ef, node->parent);
+ return rc;
+}
+
+static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
+ off_t deleted_offset)
+{
+ const struct exfat_node* node;
+ const struct exfat_node* last_node;
+ uint64_t entries = 0;
+ uint64_t new_size;
+
+ if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+ exfat_bug("attempted to shrink a file");
+ if (!dir->is_cached)
+ exfat_bug("attempted to shrink uncached directory");
+
+ for (last_node = node = dir->child; node; node = node->next)
+ {
+ if (deleted_offset < node->entry_offset)
+ {
+ /* there are other entries after the removed one, no way to shrink
+ this directory */
+ return 0;
+ }
+ if (last_node->entry_offset < node->entry_offset)
+ last_node = node;
+ }
+
+ if (last_node)
+ {
+ /* offset of the last entry */
+ entries += last_node->entry_offset / sizeof(struct exfat_entry);
+ /* two subentries with meta info */
+ entries += 2;
+ /* subentries with file name */
+ entries += DIV_ROUND_UP(utf16_length(last_node->name),
+ EXFAT_ENAME_MAX);
+ }
+
+ new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry),
+ CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb);
+ if (new_size == 0) /* directory always has at least 1 cluster */
+ new_size = CLUSTER_SIZE(*ef->sb);
+ if (new_size == dir->size)
+ return 0;
+ return exfat_truncate(ef, dir, new_size, true);
+}
+
+static int delete(struct exfat* ef, struct exfat_node* node)
+{
+ struct exfat_node* parent = node->parent;
+ off_t deleted_offset = node->entry_offset;
+ int rc;
+
+ exfat_get_node(parent);
+ rc = erase_node(ef, node);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, parent);
+ return rc;
+ }
+ tree_detach(node);
+ rc = shrink_directory(ef, parent, deleted_offset);
+ node->is_unlinked = true;
+ if (rc != 0)
+ {
+ exfat_flush_node(ef, parent);
+ exfat_put_node(ef, parent);
+ return rc;
+ }
+ exfat_update_mtime(parent);
+ rc = exfat_flush_node(ef, parent);
+ exfat_put_node(ef, parent);
+ return rc;
+}
+
+int exfat_unlink(struct exfat* ef, struct exfat_node* node)
+{
+ if (node->attrib & EXFAT_ATTRIB_DIR)
+ return -EISDIR;
+ return delete(ef, node);
+}
+
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node)
+{
+ int rc;
+
+ if (!(node->attrib & EXFAT_ATTRIB_DIR))
+ return -ENOTDIR;
+ /* check that directory is empty */
+ rc = exfat_cache_directory(ef, node);
+ if (rc != 0)
+ return rc;
+ if (node->child)
+ return -ENOTEMPTY;
+ return delete(ef, node);
+}
+
+static int check_slot(struct exfat* ef, struct exfat_node* dir, off_t offset,
+ int n)
+{
+ struct exfat_entry entries[n];
+ int rc;
+ size_t i;
+
+ /* Root directory contains entries, that don't have any nodes associated
+ with them (clusters bitmap, upper case table, label). We need to be
+ careful not to overwrite them. */
+ if (dir != ef->root)
+ return 0;
+
+ rc = read_entries(ef, dir, entries, n, offset);
+ if (rc != 0)
+ return rc;
+ for (i = 0; i < n; i++)
+ if (entries[i].type & EXFAT_ENTRY_VALID)
+ return -EINVAL;
+ return 0;
+}
+
+static int find_slot(struct exfat* ef, struct exfat_node* dir,
+ off_t* offset, int n)
+{
+ bitmap_t* dmap;
+ struct exfat_node* p;
+ size_t i;
+ int contiguous = 0;
+
+ if (!dir->is_cached)
+ exfat_bug("directory is not cached");
+
+ /* build a bitmap of valid entries in the directory */
+ dmap = calloc(BMAP_SIZE(dir->size / sizeof(struct exfat_entry)),
+ sizeof(bitmap_t));
+ if (dmap == NULL)
+ {
+ exfat_error("failed to allocate directory bitmap (%"PRIu64")",
+ dir->size / sizeof(struct exfat_entry));
+ return -ENOMEM;
+ }
+ for (p = dir->child; p != NULL; p = p->next)
+ for (i = 0; i < 1 + p->continuations; i++)
+ BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i);
+
+ /* find a slot in the directory entries bitmap */
+ for (i = 0; i < dir->size / sizeof(struct exfat_entry); i++)
+ {
+ if (BMAP_GET(dmap, i) == 0)
+ {
+ if (contiguous++ == 0)
+ *offset = (off_t) i * sizeof(struct exfat_entry);
+ if (contiguous == n)
+ /* suitable slot is found, check that it's not occupied */
+ switch (check_slot(ef, dir, *offset, n))
+ {
+ case 0:
+ free(dmap);
+ return 0;
+ case -EIO:
+ free(dmap);
+ return -EIO;
+ case -EINVAL:
+ /* slot at (i-n) is occupied, go back and check (i-n+1) */
+ i -= contiguous - 1;
+ contiguous = 0;
+ break;
+ }
+ }
+ else
+ contiguous = 0;
+ }
+ free(dmap);
+
+ /* no suitable slots found, extend the directory */
+ if (contiguous == 0)
+ *offset = dir->size;
+ return exfat_truncate(ef, dir,
+ ROUND_UP(dir->size + sizeof(struct exfat_entry[n - contiguous]),
+ CLUSTER_SIZE(*ef->sb)),
+ true);
+}
+
+static int commit_entry(struct exfat* ef, struct exfat_node* dir,
+ const le16_t* name, off_t offset, uint16_t attrib)
+{
+ struct exfat_node* node;
+ const size_t name_length = utf16_length(name);
+ const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+ struct exfat_entry entries[2 + name_entries];
+ struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+ struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+ int i;
+ int rc;
+
+ memset(entries, 0, sizeof(struct exfat_entry[2]));
+
+ meta1->type = EXFAT_ENTRY_FILE;
+ meta1->continuations = 1 + name_entries;
+ meta1->attrib = cpu_to_le16(attrib);
+ exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime,
+ &meta1->crtime_cs);
+ meta1->adate = meta1->mdate = meta1->crdate;
+ meta1->atime = meta1->mtime = meta1->crtime;
+ meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */
+
+ meta2->type = EXFAT_ENTRY_FILE_INFO;
+ meta2->flags = EXFAT_FLAG_ALWAYS1;
+ meta2->name_length = name_length;
+ meta2->name_hash = exfat_calc_name_hash(ef, name, name_length);
+ meta2->start_cluster = cpu_to_le32(EXFAT_CLUSTER_FREE);
+
+ for (i = 0; i < name_entries; i++)
+ {
+ struct exfat_entry_name* name_entry;
+
+ name_entry = (struct exfat_entry_name*) &entries[2 + i];
+ name_entry->type = EXFAT_ENTRY_FILE_NAME;
+ name_entry->__unknown = 0;
+ memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX,
+ EXFAT_ENAME_MAX * sizeof(le16_t));
+ }
+
+ meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries);
+ rc = write_entries(ef, dir, entries, 2 + name_entries, offset);
+ if (rc != 0)
+ return rc;
+
+ node = allocate_node();
+ if (node == NULL)
+ return -ENOMEM;
+ node->entry_offset = offset;
+ memcpy(node->name, name, name_length * sizeof(le16_t));
+ init_node_meta1(node, meta1);
+ init_node_meta2(node, meta2);
+
+ tree_attach(dir, node);
+ return 0;
+}
+
+static int create(struct exfat* ef, const char* path, uint16_t attrib)
+{
+ struct exfat_node* dir;
+ struct exfat_node* existing;
+ off_t offset = -1;
+ le16_t name[EXFAT_NAME_MAX + 1];
+ int rc;
+
+ rc = exfat_split(ef, &dir, &existing, name, path);
+ if (rc != 0)
+ return rc;
+ if (existing != NULL)
+ {
+ exfat_put_node(ef, existing);
+ exfat_put_node(ef, dir);
+ return -EEXIST;
+ }
+
+ rc = find_slot(ef, dir, &offset,
+ 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+ if (rc != 0)
+ {
+ exfat_put_node(ef, dir);
+ return rc;
+ }
+ rc = commit_entry(ef, dir, name, offset, attrib);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, dir);
+ return rc;
+ }
+ exfat_update_mtime(dir);
+ rc = exfat_flush_node(ef, dir);
+ exfat_put_node(ef, dir);
+ return rc;
+}
+
+int exfat_mknod(struct exfat* ef, const char* path)
+{
+ return create(ef, path, EXFAT_ATTRIB_ARCH);
+}
+
+int exfat_mkdir(struct exfat* ef, const char* path)
+{
+ int rc;
+ struct exfat_node* node;
+
+ rc = create(ef, path, EXFAT_ATTRIB_DIR);
+ if (rc != 0)
+ return rc;
+ rc = exfat_lookup(ef, &node, path);
+ if (rc != 0)
+ return 0;
+ /* directories always have at least one cluster */
+ rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true);
+ if (rc != 0)
+ {
+ delete(ef, node);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ rc = exfat_flush_node(ef, node);
+ if (rc != 0)
+ {
+ delete(ef, node);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ exfat_put_node(ef, node);
+ return 0;
+}
+
+static int rename_entry(struct exfat* ef, struct exfat_node* dir,
+ struct exfat_node* node, const le16_t* name, off_t new_offset)
+{
+ const size_t name_length = utf16_length(name);
+ const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+ struct exfat_entry entries[2 + name_entries];
+ struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+ struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+ int rc;
+ int i;
+
+ rc = read_entries(ef, node->parent, entries, 2, node->entry_offset);
+ if (rc != 0)
+ return rc;
+
+ meta1->continuations = 1 + name_entries;
+ meta2->name_length = name_length;
+ meta2->name_hash = exfat_calc_name_hash(ef, name, name_length);
+
+ rc = erase_node(ef, node);
+ if (rc != 0)
+ return rc;
+
+ node->entry_offset = new_offset;
+ node->continuations = 1 + name_entries;
+
+ for (i = 0; i < name_entries; i++)
+ {
+ struct exfat_entry_name* name_entry;
+
+ name_entry = (struct exfat_entry_name*) &entries[2 + i];
+ name_entry->type = EXFAT_ENTRY_FILE_NAME;
+ name_entry->__unknown = 0;
+ memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX,
+ EXFAT_ENAME_MAX * sizeof(le16_t));
+ }
+
+ meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries);
+ rc = write_entries(ef, dir, entries, 2 + name_entries, new_offset);
+ if (rc != 0)
+ return rc;
+
+ memcpy(node->name, name, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+ tree_detach(node);
+ tree_attach(dir, node);
+ return 0;
+}
+
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
+{
+ struct exfat_node* node;
+ struct exfat_node* existing;
+ struct exfat_node* dir;
+ off_t offset = -1;
+ le16_t name[EXFAT_NAME_MAX + 1];
+ int rc;
+
+ rc = exfat_lookup(ef, &node, old_path);
+ if (rc != 0)
+ return rc;
+
+ rc = exfat_split(ef, &dir, &existing, name, new_path);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, node);
+ return rc;
+ }
+
+ /* check that target is not a subdirectory of the source */
+ if (node->attrib & EXFAT_ATTRIB_DIR)
+ {
+ struct exfat_node* p;
+
+ for (p = dir; p; p = p->parent)
+ if (node == p)
+ {
+ if (existing != NULL)
+ exfat_put_node(ef, existing);
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ return -EINVAL;
+ }
+ }
+
+ if (existing != NULL)
+ {
+ /* remove target if it's not the same node as source */
+ if (existing != node)
+ {
+ if (existing->attrib & EXFAT_ATTRIB_DIR)
+ {
+ if (node->attrib & EXFAT_ATTRIB_DIR)
+ rc = exfat_rmdir(ef, existing);
+ else
+ rc = -ENOTDIR;
+ }
+ else
+ {
+ if (!(node->attrib & EXFAT_ATTRIB_DIR))
+ rc = exfat_unlink(ef, existing);
+ else
+ rc = -EISDIR;
+ }
+ exfat_put_node(ef, existing);
+ if (rc != 0)
+ {
+ /* free clusters even if something went wrong; overwise they
+ will be just lost */
+ exfat_cleanup_node(ef, existing);
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ rc = exfat_cleanup_node(ef, existing);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ }
+ else
+ exfat_put_node(ef, existing);
+ }
+
+ rc = find_slot(ef, dir, &offset,
+ 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+ if (rc != 0)
+ {
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ rc = rename_entry(ef, dir, node, name, offset);
+ if (rc != 0)
+ {
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ return rc;
+ }
+ rc = exfat_flush_node(ef, dir);
+ exfat_put_node(ef, dir);
+ exfat_put_node(ef, node);
+ /* node itself is not marked as dirty, no need to flush it */
+ return rc;
+}
+
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2])
+{
+ node->atime = tv[0].tv_sec;
+ node->mtime = tv[1].tv_sec;
+ node->is_dirty = true;
+}
+
+void exfat_update_atime(struct exfat_node* node)
+{
+ node->atime = time(NULL);
+ node->is_dirty = true;
+}
+
+void exfat_update_mtime(struct exfat_node* node)
+{
+ node->mtime = time(NULL);
+ node->is_dirty = true;
+}
+
+const char* exfat_get_label(struct exfat* ef)
+{
+ return ef->label;
+}
+
+static int find_label(struct exfat* ef, off_t* offset)
+{
+ struct exfat_entry entry;
+ int rc;
+
+ for (*offset = 0; ; *offset += sizeof(entry))
+ {
+ rc = read_entries(ef, ef->root, &entry, 1, *offset);
+ if (rc != 0)
+ return rc;
+
+ if (entry.type == EXFAT_ENTRY_LABEL)
+ return 0;
+ }
+}
+
+int exfat_set_label(struct exfat* ef, const char* label)
+{
+ le16_t label_utf16[EXFAT_ENAME_MAX + 1];
+ int rc;
+ off_t offset;
+ struct exfat_entry_label entry;
+
+ memset(label_utf16, 0, sizeof(label_utf16));
+ rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, strlen(label));
+ if (rc != 0)
+ return rc;
+
+ rc = find_label(ef, &offset);
+ if (rc == -ENOENT)
+ rc = find_slot(ef, ef->root, &offset, 1);
+ if (rc != 0)
+ return rc;
+
+ entry.type = EXFAT_ENTRY_LABEL;
+ entry.length = utf16_length(label_utf16);
+ memcpy(entry.name, label_utf16, sizeof(entry.name));
+ if (entry.length == 0)
+ entry.type ^= EXFAT_ENTRY_VALID;
+
+ rc = write_entries(ef, ef->root, (struct exfat_entry*) &entry, 1, offset);
+ if (rc != 0)
+ return rc;
+
+ strcpy(ef->label, label);
+ return 0;
+}
--- /dev/null
+/*
+ platform.h (14.05.13)
+ OS-specific code (libc-specific in fact). Note that systems with the
+ same kernel can use different libc implementations.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef PLATFORM_H_INCLUDED
+#define PLATFORM_H_INCLUDED
+
+#if defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)
+
+#include <endian.h>
+#include <byteswap.h>
+#define exfat_bswap16(x) bswap_16(x)
+#define exfat_bswap32(x) bswap_32(x)
+#define exfat_bswap64(x) bswap_64(x)
+#define EXFAT_BYTE_ORDER __BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN __LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN __BIG_ENDIAN
+
+#elif defined(__APPLE__)
+
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
+#define exfat_bswap16(x) OSSwapInt16(x)
+#define exfat_bswap32(x) OSSwapInt32(x)
+#define exfat_bswap64(x) OSSwapInt64(x)
+#define EXFAT_BYTE_ORDER BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN BIG_ENDIAN
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
+
+#include <sys/endian.h>
+#define exfat_bswap16(x) bswap16(x)
+#define exfat_bswap32(x) bswap32(x)
+#define exfat_bswap64(x) bswap64(x)
+#define EXFAT_BYTE_ORDER _BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN _LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN _BIG_ENDIAN
+
+#else
+#error Unknown platform
+#endif
+
+#endif /* ifndef PLATFORM_H_INCLUDED */
--- /dev/null
+/*
+ repair.c (09.03.17)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <strings.h>
+
+int exfat_errors_fixed;
+
+bool exfat_ask_to_fix(const struct exfat* ef)
+{
+ const char* question = "Fix (Y/N)?";
+ char answer[8];
+ bool yeah, nope;
+
+ switch (ef->repair)
+ {
+ case EXFAT_REPAIR_NO:
+ return false;
+ case EXFAT_REPAIR_YES:
+ printf("%s %s", question, "Y\n");
+ return true;
+ case EXFAT_REPAIR_ASK:
+ do
+ {
+ printf("%s ", question);
+ fflush(stdout);
+ if (fgets(answer, sizeof(answer), stdin))
+ {
+ yeah = strcasecmp(answer, "Y\n") == 0;
+ nope = strcasecmp(answer, "N\n") == 0;
+ }
+ else
+ {
+ yeah = false;
+ nope = true;
+ }
+ }
+ while (!yeah && !nope);
+ return yeah;
+ }
+ exfat_bug("invalid repair option value: %d", ef->repair);
+ return false;
+}
+
+bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
+ uint32_t vbr_checksum)
+{
+ size_t i;
+ off_t sector_size = SECTOR_SIZE(*ef->sb);
+
+ for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
+ ((le32_t*) sector)[i] = cpu_to_le32(vbr_checksum);
+ if (exfat_pwrite(ef->dev, sector, sector_size, 11 * sector_size) < 0)
+ {
+ exfat_error("failed to write correct VBR checksum");
+ return false;
+ }
+ exfat_errors_fixed++;
+ return true;
+}
+
+bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
+ struct exfat_node* node)
+{
+ /* checksum will be rewritten by exfat_flush_node() */
+ node->is_dirty = true;
+
+ exfat_errors_fixed++;
+ return true;
+}
+
+bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
+ const struct exfat_entry* entry, off_t offset)
+{
+ struct exfat_entry deleted = *entry;
+
+ deleted.type &= ~EXFAT_ENTRY_VALID;
+ if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry),
+ offset) != sizeof(struct exfat_entry))
+ return false;
+
+ exfat_errors_fixed++;
+ return true;
+}
--- /dev/null
+/*
+ time.c (03.02.12)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+
+/* timezone offset from UTC in seconds; positive for western timezones,
+ negative for eastern ones */
+static long exfat_timezone;
+
+#define SEC_IN_MIN 60ll
+#define SEC_IN_HOUR (60 * SEC_IN_MIN)
+#define SEC_IN_DAY (24 * SEC_IN_HOUR)
+#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */
+/* Unix epoch started at 0:00:00 UTC 1 January 1970 */
+#define UNIX_EPOCH_YEAR 1970
+/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */
+#define EXFAT_EPOCH_YEAR 1980
+/* number of years from Unix epoch to exFAT epoch */
+#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR)
+/* number of days from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4)
+/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY)
+/* number of leap years passed from exFAT epoch to the specified year
+ (excluding the specified year itself) */
+#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \
+ - (EXFAT_EPOCH_YEAR - 1) / 4)
+/* checks whether the specified year is leap */
+#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0)
+
+static const time_t days_in_year[] =
+{
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
+{
+ time_t unix_time = EPOCH_DIFF_SEC;
+ uint16_t ndate = le16_to_cpu(date);
+ uint16_t ntime = le16_to_cpu(time);
+
+ uint16_t day = ndate & 0x1f; /* 5 bits, 1-31 */
+ uint16_t month = ndate >> 5 & 0xf; /* 4 bits, 1-12 */
+ uint16_t year = ndate >> 9; /* 7 bits, 1-127 (+1980) */
+
+ uint16_t twosec = ntime & 0x1f; /* 5 bits, 0-29 (2 sec granularity) */
+ uint16_t min = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */
+ uint16_t hour = ntime >> 11; /* 5 bits, 0-23 */
+
+ if (day == 0 || month == 0 || month > 12)
+ {
+ exfat_error("bad date %u-%02hu-%02hu",
+ year + EXFAT_EPOCH_YEAR, month, day);
+ return 0;
+ }
+ if (hour > 23 || min > 59 || twosec > 29)
+ {
+ exfat_error("bad time %hu:%02hu:%02u",
+ hour, min, twosec * 2);
+ return 0;
+ }
+ if (centisec > 199)
+ {
+ exfat_error("bad centiseconds count %hhu", centisec);
+ return 0;
+ }
+
+ /* every 4th year between 1904 and 2096 is leap */
+ unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY;
+ unix_time += days_in_year[month] * SEC_IN_DAY;
+ /* if it's leap year and February has passed we should add 1 day */
+ if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2)
+ unix_time += SEC_IN_DAY;
+ unix_time += (day - 1) * SEC_IN_DAY;
+
+ unix_time += hour * SEC_IN_HOUR;
+ unix_time += min * SEC_IN_MIN;
+ /* exFAT represents time with 2 sec granularity */
+ unix_time += twosec * 2;
+ unix_time += centisec / 100;
+
+ /* exFAT stores timestamps in local time, so we correct it to UTC */
+ unix_time += exfat_timezone;
+
+ return unix_time;
+}
+
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+ uint8_t* centisec)
+{
+ time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
+ uint16_t day, month, year;
+ uint16_t twosec, min, hour;
+ int days;
+ int i;
+
+ /* time before exFAT epoch cannot be represented */
+ if (unix_time < shift)
+ unix_time = shift;
+
+ unix_time -= shift;
+
+ days = unix_time / SEC_IN_DAY;
+ year = (4 * days) / (4 * 365 + 1);
+ days -= year * 365 + LEAP_YEARS(year);
+ month = 0;
+ for (i = 1; i <= 12; i++)
+ {
+ int leap_day = (IS_LEAP_YEAR(year) && i == 2);
+ int leap_sub = (IS_LEAP_YEAR(year) && i >= 3);
+
+ if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day)
+ {
+ month = i;
+ days -= days_in_year[i] + leap_sub;
+ break;
+ }
+ }
+ day = days + 1;
+
+ hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR;
+ min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN;
+ twosec = (unix_time % SEC_IN_MIN) / 2;
+
+ *date = cpu_to_le16(day | (month << 5) | (year << 9));
+ *time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
+ if (centisec)
+ *centisec = (unix_time % 2) * 100;
+}
+
+void exfat_tzset(void)
+{
+ time_t now;
+ struct tm* utc;
+
+ tzset();
+ now = time(NULL);
+ utc = gmtime(&now);
+ /* gmtime() always sets tm_isdst to 0 because daylight savings never
+ affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether
+ summer time is in effect. */
+ utc->tm_isdst = -1;
+ exfat_timezone = mktime(utc) - now;
+}
--- /dev/null
+/*
+ utf.c (13.09.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+
+static char* wchar_to_utf8(char* output, wchar_t wc, size_t outsize)
+{
+ if (wc <= 0x7f)
+ {
+ if (outsize < 1)
+ return NULL;
+ *output++ = (char) wc;
+ }
+ else if (wc <= 0x7ff)
+ {
+ if (outsize < 2)
+ return NULL;
+ *output++ = 0xc0 | (wc >> 6);
+ *output++ = 0x80 | (wc & 0x3f);
+ }
+ else if (wc <= 0xffff)
+ {
+ if (outsize < 3)
+ return NULL;
+ *output++ = 0xe0 | (wc >> 12);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ }
+ else if (wc <= 0x1fffff)
+ {
+ if (outsize < 4)
+ return NULL;
+ *output++ = 0xf0 | (wc >> 18);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ }
+ else if (wc <= 0x3ffffff)
+ {
+ if (outsize < 5)
+ return NULL;
+ *output++ = 0xf8 | (wc >> 24);
+ *output++ = 0x80 | ((wc >> 18) & 0x3f);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ }
+ else if (wc <= 0x7fffffff)
+ {
+ if (outsize < 6)
+ return NULL;
+ *output++ = 0xfc | (wc >> 30);
+ *output++ = 0x80 | ((wc >> 24) & 0x3f);
+ *output++ = 0x80 | ((wc >> 18) & 0x3f);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ }
+ else
+ return NULL;
+
+ return output;
+}
+
+static const le16_t* utf16_to_wchar(const le16_t* input, wchar_t* wc,
+ size_t insize)
+{
+ if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800)
+ {
+ if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
+ return NULL;
+ *wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
+ *wc |= (le16_to_cpu(input[1]) & 0x3ff);
+ *wc += 0x10000;
+ return input + 2;
+ }
+ else
+ {
+ *wc = le16_to_cpu(*input);
+ return input + 1;
+ }
+}
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+ size_t insize)
+{
+ const le16_t* inp = input;
+ char* outp = output;
+ wchar_t wc;
+
+ while (inp - input < insize)
+ {
+ inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
+ if (inp == NULL)
+ {
+ exfat_error("illegal UTF-16 sequence");
+ return -EILSEQ;
+ }
+ outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
+ if (outp == NULL)
+ {
+ exfat_error("name is too long");
+ return -ENAMETOOLONG;
+ }
+ if (wc == 0)
+ return 0;
+ }
+ if (outp - output >= outsize)
+ {
+ exfat_error("name is too long");
+ return -ENAMETOOLONG;
+ }
+ *outp = '\0';
+ return 0;
+}
+
+static const char* utf8_to_wchar(const char* input, wchar_t* wc,
+ size_t insize)
+{
+ if ((input[0] & 0x80) == 0 && insize >= 1)
+ {
+ *wc = (wchar_t) input[0];
+ return input + 1;
+ }
+ if ((input[0] & 0xe0) == 0xc0 && insize >= 2)
+ {
+ *wc = (((wchar_t) input[0] & 0x1f) << 6) |
+ ((wchar_t) input[1] & 0x3f);
+ return input + 2;
+ }
+ if ((input[0] & 0xf0) == 0xe0 && insize >= 3)
+ {
+ *wc = (((wchar_t) input[0] & 0x0f) << 12) |
+ (((wchar_t) input[1] & 0x3f) << 6) |
+ ((wchar_t) input[2] & 0x3f);
+ return input + 3;
+ }
+ if ((input[0] & 0xf8) == 0xf0 && insize >= 4)
+ {
+ *wc = (((wchar_t) input[0] & 0x07) << 18) |
+ (((wchar_t) input[1] & 0x3f) << 12) |
+ (((wchar_t) input[2] & 0x3f) << 6) |
+ ((wchar_t) input[3] & 0x3f);
+ return input + 4;
+ }
+ if ((input[0] & 0xfc) == 0xf8 && insize >= 5)
+ {
+ *wc = (((wchar_t) input[0] & 0x03) << 24) |
+ (((wchar_t) input[1] & 0x3f) << 18) |
+ (((wchar_t) input[2] & 0x3f) << 12) |
+ (((wchar_t) input[3] & 0x3f) << 6) |
+ ((wchar_t) input[4] & 0x3f);
+ return input + 5;
+ }
+ if ((input[0] & 0xfe) == 0xfc && insize >= 6)
+ {
+ *wc = (((wchar_t) input[0] & 0x01) << 30) |
+ (((wchar_t) input[1] & 0x3f) << 24) |
+ (((wchar_t) input[2] & 0x3f) << 18) |
+ (((wchar_t) input[3] & 0x3f) << 12) |
+ (((wchar_t) input[4] & 0x3f) << 6) |
+ ((wchar_t) input[5] & 0x3f);
+ return input + 6;
+ }
+ return NULL;
+}
+
+static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize)
+{
+ if (wc <= 0xffff) /* if character is from BMP */
+ {
+ if (outsize == 0)
+ return NULL;
+ output[0] = cpu_to_le16(wc);
+ return output + 1;
+ }
+ if (outsize < 2)
+ return NULL;
+ wc -= 0x10000;
+ output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
+ output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
+ return output + 2;
+}
+
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+ size_t insize)
+{
+ const char* inp = input;
+ le16_t* outp = output;
+ wchar_t wc;
+
+ while (inp - input < insize)
+ {
+ inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
+ if (inp == NULL)
+ {
+ exfat_error("illegal UTF-8 sequence");
+ return -EILSEQ;
+ }
+ outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
+ if (outp == NULL)
+ {
+ exfat_error("name is too long");
+ return -ENAMETOOLONG;
+ }
+ if (wc == 0)
+ break;
+ }
+ if (outp - output >= outsize)
+ {
+ exfat_error("name is too long");
+ return -ENAMETOOLONG;
+ }
+ *outp = cpu_to_le16(0);
+ return 0;
+}
+
+size_t utf16_length(const le16_t* str)
+{
+ size_t i = 0;
+
+ while (le16_to_cpu(str[i]))
+ i++;
+ return i;
+}
--- /dev/null
+/*
+ utils.c (04.09.09)
+ exFAT file system implementation library.
+
+ Free exFAT implementation.
+ Copyright (C) 2010-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+ struct stat* stbuf)
+{
+ memset(stbuf, 0, sizeof(struct stat));
+ if (node->attrib & EXFAT_ATTRIB_DIR)
+ stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask);
+ else
+ stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask);
+ stbuf->st_nlink = 1;
+ stbuf->st_uid = ef->uid;
+ stbuf->st_gid = ef->gid;
+ stbuf->st_size = node->size;
+ stbuf->st_blocks = ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) / 512;
+ stbuf->st_mtime = node->mtime;
+ stbuf->st_atime = node->atime;
+ /* set ctime to mtime to ensure we don't break programs that rely on ctime
+ (e.g. rsync) */
+ stbuf->st_ctime = node->mtime;
+}
+
+void exfat_get_name(const struct exfat_node* node,
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX])
+{
+ if (utf16_to_utf8(buffer, node->name, EXFAT_UTF8_NAME_BUFFER_MAX,
+ EXFAT_NAME_MAX) != 0)
+ exfat_bug("failed to convert name to UTF-8");
+}
+
+static uint16_t add_checksum_byte(uint16_t sum, uint8_t byte)
+{
+ return ((sum << 15) | (sum >> 1)) + byte;
+}
+
+static uint16_t add_checksum_bytes(uint16_t sum, const void* buffer, size_t n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ sum = add_checksum_byte(sum, ((const uint8_t*) buffer)[i]);
+ return sum;
+}
+
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry)
+{
+ uint16_t sum = 0;
+ int i;
+
+ for (i = 0; i < sizeof(struct exfat_entry); i++)
+ if (i != 2 && i != 3) /* skip checksum field itself */
+ sum = add_checksum_byte(sum, ((const uint8_t*) entry)[i]);
+ return sum;
+}
+
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum)
+{
+ return add_checksum_bytes(sum, entry, sizeof(struct exfat_entry));
+}
+
+le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n)
+{
+ uint16_t checksum;
+ int i;
+
+ checksum = exfat_start_checksum((const struct exfat_entry_meta1*) entries);
+ for (i = 1; i < n; i++)
+ checksum = exfat_add_checksum(entries + i, checksum);
+ return cpu_to_le16(checksum);
+}
+
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size)
+{
+ size_t i;
+ uint32_t sum = 0;
+
+ for (i = 0; i < size; i++)
+ /* skip volume_state and allocated_percent fields */
+ if (i != 0x6a && i != 0x6b && i != 0x70)
+ sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+ return sum;
+}
+
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+ return sum;
+}
+
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
+ size_t length)
+{
+ size_t i;
+ uint16_t hash = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ uint16_t c = le16_to_cpu(name[i]);
+
+ /* convert to upper case */
+ c = ef->upcase[c];
+
+ hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
+ hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+ }
+ return cpu_to_le16(hash);
+}
+
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb)
+{
+ size_t i;
+ /* 16 EB (minus 1 byte) is the largest size that can be represented by
+ uint64_t */
+ const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
+ uint64_t divisor = 1;
+ uint64_t temp = 0;
+
+ for (i = 0; ; i++, divisor *= 1024)
+ {
+ temp = (value + divisor / 2) / divisor;
+
+ if (temp == 0)
+ break;
+ if (temp / 1024 * 1024 == temp)
+ continue;
+ if (temp < 10240)
+ break;
+ }
+ hb->value = temp;
+ hb->unit = units[i];
+}
+
+void exfat_print_info(const struct exfat_super_block* sb,
+ uint32_t free_clusters)
+{
+ struct exfat_human_bytes hb;
+ off_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb);
+ off_t avail_space = (off_t) free_clusters * CLUSTER_SIZE(*sb);
+
+ printf("File system version %hhu.%hhu\n",
+ sb->version.major, sb->version.minor);
+ exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb);
+ printf("Sector size %10"PRIu64" %s\n", hb.value, hb.unit);
+ exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb);
+ printf("Cluster size %10"PRIu64" %s\n", hb.value, hb.unit);
+ exfat_humanize_bytes(total_space, &hb);
+ printf("Volume size %10"PRIu64" %s\n", hb.value, hb.unit);
+ exfat_humanize_bytes(total_space - avail_space, &hb);
+ printf("Used space %10"PRIu64" %s\n", hb.value, hb.unit);
+ exfat_humanize_bytes(avail_space, &hb);
+ printf("Available space %10"PRIu64" %s\n", hb.value, hb.unit);
+}
--- /dev/null
+/*
+ cbm.c (09.11.10)
+ Clusters Bitmap creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cbm.h"
+#include "fat.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <limits.h>
+#include <string.h>
+
+static off_t cbm_alignment(void)
+{
+ return get_cluster_size();
+}
+
+static off_t cbm_size(void)
+{
+ return DIV_ROUND_UP(
+ (get_volume_size() - get_position(&cbm)) / get_cluster_size(),
+ CHAR_BIT);
+}
+
+static int cbm_write(struct exfat_dev* dev)
+{
+ uint32_t allocated_clusters =
+ DIV_ROUND_UP(cbm.get_size(), get_cluster_size()) +
+ DIV_ROUND_UP(uct.get_size(), get_cluster_size()) +
+ DIV_ROUND_UP(rootdir.get_size(), get_cluster_size());
+ size_t bitmap_size = ROUND_UP(allocated_clusters, CHAR_BIT);
+ bitmap_t* bitmap = malloc(BMAP_SIZE(bitmap_size));
+ size_t i;
+
+ if (bitmap == NULL)
+ {
+ exfat_error("failed to allocate bitmap of %zu bytes",
+ BMAP_SIZE(bitmap_size));
+ return 1;
+ }
+ memset(bitmap, 0, BMAP_SIZE(bitmap_size));
+
+ for (i = 0; i < bitmap_size; i++)
+ if (i < allocated_clusters)
+ BMAP_SET(bitmap, i);
+ if (exfat_write(dev, bitmap, bitmap_size / CHAR_BIT) < 0)
+ {
+ free(bitmap);
+ exfat_error("failed to write bitmap of %zu bytes",
+ bitmap_size / CHAR_BIT);
+ return 1;
+ }
+ free(bitmap);
+ return 0;
+}
+
+const struct fs_object cbm =
+{
+ .get_alignment = cbm_alignment,
+ .get_size = cbm_size,
+ .write = cbm_write,
+};
--- /dev/null
+/*
+ cbm.h (09.11.10)
+ Clusters Bitmap creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_CBM_H_INCLUDED
+#define MKFS_CBM_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object cbm;
+
+#endif /* ifndef MKFS_CBM_H_INCLUDED */
--- /dev/null
+/*
+ fat.c (09.11.10)
+ File Allocation Table creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <unistd.h>
+
+static off_t fat_alignment(void)
+{
+ return (off_t) 128 * get_sector_size();
+}
+
+static off_t fat_size(void)
+{
+ return get_volume_size() / get_cluster_size() * sizeof(cluster_t);
+}
+
+static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster,
+ cluster_t value)
+{
+ le32_t fat_entry = cpu_to_le32(value);
+ if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0)
+ {
+ exfat_error("failed to write FAT entry 0x%x", value);
+ return 0;
+ }
+ return cluster + 1;
+}
+
+static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster,
+ uint64_t length)
+{
+ cluster_t end = cluster + DIV_ROUND_UP(length, get_cluster_size());
+
+ while (cluster < end - 1)
+ {
+ cluster = fat_write_entry(dev, cluster, cluster + 1);
+ if (cluster == 0)
+ return 0;
+ }
+ return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END);
+}
+
+static int fat_write(struct exfat_dev* dev)
+{
+ cluster_t c = 0;
+
+ if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */
+ return 1;
+ if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */
+ return 1;
+ if (!(c = fat_write_entries(dev, c, cbm.get_size())))
+ return 1;
+ if (!(c = fat_write_entries(dev, c, uct.get_size())))
+ return 1;
+ if (!(c = fat_write_entries(dev, c, rootdir.get_size())))
+ return 1;
+
+ return 0;
+}
+
+const struct fs_object fat =
+{
+ .get_alignment = fat_alignment,
+ .get_size = fat_size,
+ .write = fat_write,
+};
--- /dev/null
+/*
+ fat.h (09.11.10)
+ File Allocation Table creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_FAT_H_INCLUDED
+#define MKFS_FAT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object fat;
+
+#endif /* ifndef MKFS_FAT_H_INCLUDED */
--- /dev/null
+/*
+ mkexfat.c (22.04.12)
+ FS creation engine.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "mkexfat.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_size(off_t volume_size)
+{
+ const struct fs_object** pp;
+ off_t position = 0;
+
+ for (pp = objects; *pp; pp++)
+ {
+ position = ROUND_UP(position, (*pp)->get_alignment());
+ position += (*pp)->get_size();
+ }
+
+ if (position > volume_size)
+ {
+ struct exfat_human_bytes vhb;
+
+ exfat_humanize_bytes(volume_size, &vhb);
+ exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit);
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int erase_object(struct exfat_dev* dev, const void* block,
+ size_t block_size, off_t start, off_t size)
+{
+ const off_t block_count = DIV_ROUND_UP(size, block_size);
+ off_t i;
+
+ if (exfat_seek(dev, start, SEEK_SET) == (off_t) -1)
+ {
+ exfat_error("seek to 0x%"PRIx64" failed", start);
+ return 1;
+ }
+ for (i = 0; i < size; i += block_size)
+ {
+ if (exfat_write(dev, block, MIN(size - i, block_size)) < 0)
+ {
+ exfat_error("failed to erase block %"PRIu64"/%"PRIu64
+ " at 0x%"PRIx64, i + 1, block_count, start);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int erase(struct exfat_dev* dev)
+{
+ const struct fs_object** pp;
+ off_t position = 0;
+ const size_t block_size = 1024 * 1024;
+ void* block = malloc(block_size);
+
+ if (block == NULL)
+ {
+ exfat_error("failed to allocate erase block of %zu bytes", block_size);
+ return 1;
+ }
+ memset(block, 0, block_size);
+
+ for (pp = objects; *pp; pp++)
+ {
+ position = ROUND_UP(position, (*pp)->get_alignment());
+ if (erase_object(dev, block, block_size, position,
+ (*pp)->get_size()) != 0)
+ {
+ free(block);
+ return 1;
+ }
+ position += (*pp)->get_size();
+ }
+
+ free(block);
+ return 0;
+}
+
+static int create(struct exfat_dev* dev)
+{
+ const struct fs_object** pp;
+ off_t position = 0;
+
+ for (pp = objects; *pp; pp++)
+ {
+ position = ROUND_UP(position, (*pp)->get_alignment());
+ if (exfat_seek(dev, position, SEEK_SET) == (off_t) -1)
+ {
+ exfat_error("seek to 0x%"PRIx64" failed", position);
+ return 1;
+ }
+ if ((*pp)->write(dev) != 0)
+ return 1;
+ position += (*pp)->get_size();
+ }
+ return 0;
+}
+
+int mkfs(struct exfat_dev* dev, off_t volume_size)
+{
+ if (check_size(volume_size) != 0)
+ return 1;
+
+ exfat_debug("Creating... ");
+ //fputs("Creating... ", stdout);
+ //fflush(stdout);
+ if (erase(dev) != 0)
+ return 1;
+ if (create(dev) != 0)
+ return 1;
+ //puts("done.");
+
+ //fputs("Flushing... ", stdout);
+ //fflush(stdout);
+ exfat_debug("Flushing... ");
+ if (exfat_fsync(dev) != 0)
+ return 1;
+ //puts("done.");
+
+ return 0;
+}
+
+off_t get_position(const struct fs_object* object)
+{
+ const struct fs_object** pp;
+ off_t position = 0;
+
+ for (pp = objects; *pp; pp++)
+ {
+ position = ROUND_UP(position, (*pp)->get_alignment());
+ if (*pp == object)
+ return position;
+ position += (*pp)->get_size();
+ }
+ exfat_bug("unknown object");
+
+ return 0;
+}
+
--- /dev/null
+/*
+ mkexfat.h (09.11.10)
+ FS creation engine.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_MKEXFAT_H_INCLUDED
+#define MKFS_MKEXFAT_H_INCLUDED
+
+#include "exfat.h"
+
+struct fs_object
+{
+ off_t (*get_alignment)(void);
+ off_t (*get_size)(void);
+ int (*write)(struct exfat_dev* dev);
+};
+
+extern const struct fs_object* objects[];
+
+int get_sector_bits(void);
+int get_spc_bits(void);
+off_t get_volume_size(void);
+const le16_t* get_volume_label(void);
+uint32_t get_volume_serial(void);
+uint64_t get_first_sector(void);
+int get_sector_size(void);
+int get_cluster_size(void);
+
+int mkfs(struct exfat_dev* dev, off_t volume_size);
+off_t get_position(const struct fs_object* object);
+
+#endif /* ifndef MKFS_MKEXFAT_H_INCLUDED */
--- /dev/null
+/*
+ main.c (15.08.10)
+ Creates exFAT file system.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "mkexfat.h"
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include "exfat.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+const struct fs_object* objects[] =
+{
+ &vbr,
+ &vbr,
+ &fat,
+ /* clusters heap */
+ &cbm,
+ &uct,
+ &rootdir,
+ NULL,
+};
+
+static struct
+{
+ int sector_bits;
+ int spc_bits;
+ off_t volume_size;
+ le16_t volume_label[EXFAT_ENAME_MAX + 1];
+ uint32_t volume_serial;
+ uint64_t first_sector;
+}
+param;
+
+extern int g_vtoy_exfat_disk_fd;
+extern uint64_t g_vtoy_exfat_part_size;
+
+int get_sector_bits(void)
+{
+ return param.sector_bits;
+}
+
+int get_spc_bits(void)
+{
+ return param.spc_bits;
+}
+
+off_t get_volume_size(void)
+{
+ return param.volume_size;
+}
+
+const le16_t* get_volume_label(void)
+{
+ return param.volume_label;
+}
+
+uint32_t get_volume_serial(void)
+{
+ return param.volume_serial;
+}
+
+uint64_t get_first_sector(void)
+{
+ return param.first_sector;
+}
+
+int get_sector_size(void)
+{
+ return 1 << get_sector_bits();
+}
+
+int get_cluster_size(void)
+{
+ return get_sector_size() << get_spc_bits();
+}
+
+static int setup_spc_bits(int sector_bits, int user_defined, off_t volume_size)
+{
+ int i;
+
+ if (user_defined != -1)
+ {
+ off_t cluster_size = 1 << sector_bits << user_defined;
+ if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER)
+ {
+ struct exfat_human_bytes chb, vhb;
+
+ exfat_humanize_bytes(cluster_size, &chb);
+ exfat_humanize_bytes(volume_size, &vhb);
+ exfat_error("cluster size %"PRIu64" %s is too small for "
+ "%"PRIu64" %s volume, try -s %d",
+ chb.value, chb.unit,
+ vhb.value, vhb.unit,
+ 1 << setup_spc_bits(sector_bits, -1, volume_size));
+ return -1;
+ }
+ return user_defined;
+ }
+
+ if (volume_size < 256ull * 1024 * 1024)
+ return MAX(0, 12 - sector_bits); /* 4 KB */
+ if (volume_size < 32ull * 1024 * 1024 * 1024)
+ return MAX(0, 15 - sector_bits); /* 32 KB */
+
+ for (i = 17; ; i++) /* 128 KB or more */
+ if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
+ return MAX(0, i - sector_bits);
+}
+
+static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s)
+{
+ memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t));
+ if (s == NULL)
+ return 0;
+ return utf8_to_utf16(label, s, EXFAT_ENAME_MAX + 1, strlen(s));
+}
+
+static uint32_t setup_volume_serial(uint32_t user_defined)
+{
+ struct timeval now;
+
+ if (user_defined != 0)
+ return user_defined;
+
+ if (gettimeofday(&now, NULL) != 0)
+ {
+ exfat_error("failed to form volume id");
+ return 0;
+ }
+ return (now.tv_sec << 20) | now.tv_usec;
+}
+
+static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits,
+ const char* volume_label, uint32_t volume_serial,
+ uint64_t first_sector)
+{
+ param.sector_bits = sector_bits;
+ param.first_sector = first_sector;
+ param.volume_size = exfat_get_size(dev);
+
+ param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size);
+ if (param.spc_bits == -1)
+ return 1;
+
+ if (setup_volume_label(param.volume_label, volume_label) != 0)
+ return 1;
+
+ param.volume_serial = setup_volume_serial(volume_serial);
+ if (param.volume_serial == 0)
+ return 1;
+
+ return mkfs(dev, param.volume_size);
+}
+
+static int logarithm2(int n)
+{
+ int i;
+
+ for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
+ if ((1 << i) == n)
+ return i;
+ return -1;
+}
+
+static void usage(const char* prog)
+{
+ fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
+ "[-p partition-first-sector] "
+ "[-s sectors-per-cluster] [-V] <device>\n", prog);
+ exit(1);
+}
+
+int mkexfat_main(const char *devpath, int fd, uint64_t part_sector_count)
+{
+ int spc_bits = -1;
+ uint32_t volume_serial = 0;
+ uint64_t first_sector = 0;
+ struct exfat_dev* dev;
+
+#if 0
+ while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1)
+ {
+ switch (opt)
+ {
+ case 'i':
+ volume_serial = strtol(optarg, NULL, 16);
+ break;
+ case 'n':
+ volume_label = optarg;
+ break;
+ case 'p':
+ first_sector = strtoll(optarg, NULL, 10);
+ break;
+ case 's':
+ spc_bits = logarithm2(atoi(optarg));
+ if (spc_bits < 0)
+ {
+ exfat_error("invalid option value: '%s'", optarg);
+ return 1;
+ }
+ break;
+ case 'V':
+ puts("Copyright (C) 2011-2018 Andrew Nayenko");
+ return 0;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+#endif /* #if 0 */
+
+ /*
+ * DiskSize > 32GB Cluster Size use 128KB
+ * DiskSize < 32GB Cluster Size use 32KB
+ */
+ if ((part_sector_count / 2097152) > 32)
+ {
+ spc_bits = logarithm2(256);
+ }
+ else
+ {
+ spc_bits = logarithm2(64);
+ }
+
+ g_vtoy_exfat_disk_fd = fd;
+ g_vtoy_exfat_part_size = part_sector_count * 512;
+
+ dev = exfat_open(devpath, EXFAT_MODE_RW);
+ if (dev == NULL)
+ return 1;
+ if (setup(dev, 9, spc_bits, "Ventoy", volume_serial, first_sector) != 0)
+ {
+ exfat_close(dev);
+ return 1;
+ }
+ if (exfat_close(dev) != 0)
+ return 1;
+
+ return 0;
+}
+
--- /dev/null
+/*
+ rootdir.c (09.11.10)
+ Root directory creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "rootdir.h"
+#include "uct.h"
+#include "cbm.h"
+#include "uctc.h"
+#include <string.h>
+
+static off_t rootdir_alignment(void)
+{
+ return get_cluster_size();
+}
+
+static off_t rootdir_size(void)
+{
+ return get_cluster_size();
+}
+
+static void init_label_entry(struct exfat_entry_label* label_entry)
+{
+ memset(label_entry, 0, sizeof(struct exfat_entry_label));
+ label_entry->type = EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID;
+
+ if (utf16_length(get_volume_label()) == 0)
+ return;
+
+ memcpy(label_entry->name, get_volume_label(),
+ EXFAT_ENAME_MAX * sizeof(le16_t));
+ label_entry->length = utf16_length(get_volume_label());
+ label_entry->type |= EXFAT_ENTRY_VALID;
+}
+
+static void init_bitmap_entry(struct exfat_entry_bitmap* bitmap_entry)
+{
+ memset(bitmap_entry, 0, sizeof(struct exfat_entry_bitmap));
+ bitmap_entry->type = EXFAT_ENTRY_BITMAP;
+ bitmap_entry->start_cluster = cpu_to_le32(EXFAT_FIRST_DATA_CLUSTER);
+ bitmap_entry->size = cpu_to_le64(cbm.get_size());
+}
+
+static void init_upcase_entry(struct exfat_entry_upcase* upcase_entry)
+{
+ size_t i;
+ uint32_t sum = 0;
+
+ for (i = 0; i < sizeof(upcase_table); i++)
+ sum = ((sum << 31) | (sum >> 1)) + upcase_table[i];
+
+ memset(upcase_entry, 0, sizeof(struct exfat_entry_upcase));
+ upcase_entry->type = EXFAT_ENTRY_UPCASE;
+ upcase_entry->checksum = cpu_to_le32(sum);
+ upcase_entry->start_cluster = cpu_to_le32(
+ (get_position(&uct) - get_position(&cbm)) / get_cluster_size() +
+ EXFAT_FIRST_DATA_CLUSTER);
+ upcase_entry->size = cpu_to_le64(sizeof(upcase_table));
+}
+
+static int rootdir_write(struct exfat_dev* dev)
+{
+ struct exfat_entry_label label_entry;
+ struct exfat_entry_bitmap bitmap_entry;
+ struct exfat_entry_upcase upcase_entry;
+
+ init_label_entry(&label_entry);
+ init_bitmap_entry(&bitmap_entry);
+ init_upcase_entry(&upcase_entry);
+
+ if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0)
+ return 1;
+ if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0)
+ return 1;
+ if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0)
+ return 1;
+ return 0;
+}
+
+const struct fs_object rootdir =
+{
+ .get_alignment = rootdir_alignment,
+ .get_size = rootdir_size,
+ .write = rootdir_write,
+};
--- /dev/null
+/*
+ rootdir.h (09.11.10)
+ Root directory creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_ROOTDIR_H_INCLUDED
+#define MKFS_ROOTDIR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object rootdir;
+
+#endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */
--- /dev/null
+/*
+ uct.c (09.11.10)
+ Upper Case Table creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uct.h"
+#include "uctc.h"
+
+static off_t uct_alignment(void)
+{
+ return get_cluster_size();
+}
+
+static off_t uct_size(void)
+{
+ return sizeof(upcase_table);
+}
+
+static int uct_write(struct exfat_dev* dev)
+{
+ if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0)
+ {
+ exfat_error("failed to write upcase table of %zu bytes",
+ sizeof(upcase_table));
+ return 1;
+ }
+ return 0;
+}
+
+const struct fs_object uct =
+{
+ .get_alignment = uct_alignment,
+ .get_size = uct_size,
+ .write = uct_write,
+};
--- /dev/null
+/*
+ uct.h (09.11.10)
+ Upper Case Table creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCT_H_INCLUDED
+#define MKFS_UCT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object uct;
+
+#endif /* ifndef MKFS_UCT_H_INCLUDED */
--- /dev/null
+/*
+ uctc.c (30.04.12)
+ Upper Case Table contents.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uctc.h"
+
+uint8_t upcase_table[5836] =
+{
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
+ 0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00,
+ 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00,
+ 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00,
+ 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+ 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00,
+ 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00,
+ 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00,
+ 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
+ 0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00,
+ 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00,
+ 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,
+ 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
+ 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
+ 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
+ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+ 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+ 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+ 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+ 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+ 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00,
+ 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00,
+ 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+ 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+ 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+ 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+ 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+ 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x7b, 0x00,
+ 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00,
+ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
+ 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
+ 0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x8b, 0x00,
+ 0x8c, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00,
+ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
+ 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00,
+ 0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00,
+ 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00,
+ 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00,
+ 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00,
+ 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00,
+ 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00,
+ 0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00,
+ 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00,
+ 0xbc, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00,
+ 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+ 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+ 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+ 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+ 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+ 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00,
+ 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+ 0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00,
+ 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+ 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+ 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+ 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+ 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+ 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xf7, 0x00,
+ 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+ 0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0x78, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01,
+ 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
+ 0x08, 0x01, 0x08, 0x01, 0x0a, 0x01, 0x0a, 0x01,
+ 0x0c, 0x01, 0x0c, 0x01, 0x0e, 0x01, 0x0e, 0x01,
+ 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01,
+ 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
+ 0x18, 0x01, 0x18, 0x01, 0x1a, 0x01, 0x1a, 0x01,
+ 0x1c, 0x01, 0x1c, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+ 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01,
+ 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
+ 0x28, 0x01, 0x28, 0x01, 0x2a, 0x01, 0x2a, 0x01,
+ 0x2c, 0x01, 0x2c, 0x01, 0x2e, 0x01, 0x2e, 0x01,
+ 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01,
+ 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
+ 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3b, 0x01,
+ 0x3b, 0x01, 0x3d, 0x01, 0x3d, 0x01, 0x3f, 0x01,
+ 0x3f, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01,
+ 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
+ 0x47, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4a, 0x01,
+ 0x4c, 0x01, 0x4c, 0x01, 0x4e, 0x01, 0x4e, 0x01,
+ 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01,
+ 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
+ 0x58, 0x01, 0x58, 0x01, 0x5a, 0x01, 0x5a, 0x01,
+ 0x5c, 0x01, 0x5c, 0x01, 0x5e, 0x01, 0x5e, 0x01,
+ 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01,
+ 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
+ 0x68, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x6a, 0x01,
+ 0x6c, 0x01, 0x6c, 0x01, 0x6e, 0x01, 0x6e, 0x01,
+ 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01,
+ 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
+ 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7b, 0x01,
+ 0x7b, 0x01, 0x7d, 0x01, 0x7d, 0x01, 0x7f, 0x01,
+ 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01,
+ 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
+ 0x87, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01,
+ 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01,
+ 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01,
+ 0x94, 0x01, 0xf6, 0x01, 0x96, 0x01, 0x97, 0x01,
+ 0x98, 0x01, 0x98, 0x01, 0x3d, 0x02, 0x9b, 0x01,
+ 0x9c, 0x01, 0x9d, 0x01, 0x20, 0x02, 0x9f, 0x01,
+ 0xa0, 0x01, 0xa0, 0x01, 0xa2, 0x01, 0xa2, 0x01,
+ 0xa4, 0x01, 0xa4, 0x01, 0xa6, 0x01, 0xa7, 0x01,
+ 0xa7, 0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01,
+ 0xac, 0x01, 0xac, 0x01, 0xae, 0x01, 0xaf, 0x01,
+ 0xaf, 0x01, 0xb1, 0x01, 0xb2, 0x01, 0xb3, 0x01,
+ 0xb3, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0xb7, 0x01,
+ 0xb8, 0x01, 0xb8, 0x01, 0xba, 0x01, 0xbb, 0x01,
+ 0xbc, 0x01, 0xbc, 0x01, 0xbe, 0x01, 0xf7, 0x01,
+ 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0xc3, 0x01,
+ 0xc4, 0x01, 0xc5, 0x01, 0xc4, 0x01, 0xc7, 0x01,
+ 0xc8, 0x01, 0xc7, 0x01, 0xca, 0x01, 0xcb, 0x01,
+ 0xca, 0x01, 0xcd, 0x01, 0xcd, 0x01, 0xcf, 0x01,
+ 0xcf, 0x01, 0xd1, 0x01, 0xd1, 0x01, 0xd3, 0x01,
+ 0xd3, 0x01, 0xd5, 0x01, 0xd5, 0x01, 0xd7, 0x01,
+ 0xd7, 0x01, 0xd9, 0x01, 0xd9, 0x01, 0xdb, 0x01,
+ 0xdb, 0x01, 0x8e, 0x01, 0xde, 0x01, 0xde, 0x01,
+ 0xe0, 0x01, 0xe0, 0x01, 0xe2, 0x01, 0xe2, 0x01,
+ 0xe4, 0x01, 0xe4, 0x01, 0xe6, 0x01, 0xe6, 0x01,
+ 0xe8, 0x01, 0xe8, 0x01, 0xea, 0x01, 0xea, 0x01,
+ 0xec, 0x01, 0xec, 0x01, 0xee, 0x01, 0xee, 0x01,
+ 0xf0, 0x01, 0xf1, 0x01, 0xf2, 0x01, 0xf1, 0x01,
+ 0xf4, 0x01, 0xf4, 0x01, 0xf6, 0x01, 0xf7, 0x01,
+ 0xf8, 0x01, 0xf8, 0x01, 0xfa, 0x01, 0xfa, 0x01,
+ 0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x01, 0xfe, 0x01,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
+ 0x08, 0x02, 0x08, 0x02, 0x0a, 0x02, 0x0a, 0x02,
+ 0x0c, 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x0e, 0x02,
+ 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02,
+ 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
+ 0x18, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1a, 0x02,
+ 0x1c, 0x02, 0x1c, 0x02, 0x1e, 0x02, 0x1e, 0x02,
+ 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02,
+ 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
+ 0x28, 0x02, 0x28, 0x02, 0x2a, 0x02, 0x2a, 0x02,
+ 0x2c, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x2e, 0x02,
+ 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02,
+ 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
+ 0x38, 0x02, 0x39, 0x02, 0x65, 0x2c, 0x3b, 0x02,
+ 0x3b, 0x02, 0x3d, 0x02, 0x66, 0x2c, 0x3f, 0x02,
+ 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02,
+ 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
+ 0x48, 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4a, 0x02,
+ 0x4c, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x4e, 0x02,
+ 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01,
+ 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8a, 0x01,
+ 0x58, 0x02, 0x8f, 0x01, 0x5a, 0x02, 0x90, 0x01,
+ 0x5c, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x5f, 0x02,
+ 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01,
+ 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
+ 0x97, 0x01, 0x96, 0x01, 0x6a, 0x02, 0x62, 0x2c,
+ 0x6c, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x9c, 0x01,
+ 0x70, 0x02, 0x71, 0x02, 0x9d, 0x01, 0x73, 0x02,
+ 0x74, 0x02, 0x9f, 0x01, 0x76, 0x02, 0x77, 0x02,
+ 0x78, 0x02, 0x79, 0x02, 0x7a, 0x02, 0x7b, 0x02,
+ 0x7c, 0x02, 0x64, 0x2c, 0x7e, 0x02, 0x7f, 0x02,
+ 0xa6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xa9, 0x01,
+ 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
+ 0xae, 0x01, 0x44, 0x02, 0xb1, 0x01, 0xb2, 0x01,
+ 0x45, 0x02, 0x8d, 0x02, 0x8e, 0x02, 0x8f, 0x02,
+ 0x90, 0x02, 0x91, 0x02, 0xb7, 0x01, 0x93, 0x02,
+ 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
+ 0x98, 0x02, 0x99, 0x02, 0x9a, 0x02, 0x9b, 0x02,
+ 0x9c, 0x02, 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x02,
+ 0xa0, 0x02, 0xa1, 0x02, 0xa2, 0x02, 0xa3, 0x02,
+ 0xa4, 0x02, 0xa5, 0x02, 0xa6, 0x02, 0xa7, 0x02,
+ 0xa8, 0x02, 0xa9, 0x02, 0xaa, 0x02, 0xab, 0x02,
+ 0xac, 0x02, 0xad, 0x02, 0xae, 0x02, 0xaf, 0x02,
+ 0xb0, 0x02, 0xb1, 0x02, 0xb2, 0x02, 0xb3, 0x02,
+ 0xb4, 0x02, 0xb5, 0x02, 0xb6, 0x02, 0xb7, 0x02,
+ 0xb8, 0x02, 0xb9, 0x02, 0xba, 0x02, 0xbb, 0x02,
+ 0xbc, 0x02, 0xbd, 0x02, 0xbe, 0x02, 0xbf, 0x02,
+ 0xc0, 0x02, 0xc1, 0x02, 0xc2, 0x02, 0xc3, 0x02,
+ 0xc4, 0x02, 0xc5, 0x02, 0xc6, 0x02, 0xc7, 0x02,
+ 0xc8, 0x02, 0xc9, 0x02, 0xca, 0x02, 0xcb, 0x02,
+ 0xcc, 0x02, 0xcd, 0x02, 0xce, 0x02, 0xcf, 0x02,
+ 0xd0, 0x02, 0xd1, 0x02, 0xd2, 0x02, 0xd3, 0x02,
+ 0xd4, 0x02, 0xd5, 0x02, 0xd6, 0x02, 0xd7, 0x02,
+ 0xd8, 0x02, 0xd9, 0x02, 0xda, 0x02, 0xdb, 0x02,
+ 0xdc, 0x02, 0xdd, 0x02, 0xde, 0x02, 0xdf, 0x02,
+ 0xe0, 0x02, 0xe1, 0x02, 0xe2, 0x02, 0xe3, 0x02,
+ 0xe4, 0x02, 0xe5, 0x02, 0xe6, 0x02, 0xe7, 0x02,
+ 0xe8, 0x02, 0xe9, 0x02, 0xea, 0x02, 0xeb, 0x02,
+ 0xec, 0x02, 0xed, 0x02, 0xee, 0x02, 0xef, 0x02,
+ 0xf0, 0x02, 0xf1, 0x02, 0xf2, 0x02, 0xf3, 0x02,
+ 0xf4, 0x02, 0xf5, 0x02, 0xf6, 0x02, 0xf7, 0x02,
+ 0xf8, 0x02, 0xf9, 0x02, 0xfa, 0x02, 0xfb, 0x02,
+ 0xfc, 0x02, 0xfd, 0x02, 0xfe, 0x02, 0xff, 0x02,
+ 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
+ 0x08, 0x03, 0x09, 0x03, 0x0a, 0x03, 0x0b, 0x03,
+ 0x0c, 0x03, 0x0d, 0x03, 0x0e, 0x03, 0x0f, 0x03,
+ 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03,
+ 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
+ 0x18, 0x03, 0x19, 0x03, 0x1a, 0x03, 0x1b, 0x03,
+ 0x1c, 0x03, 0x1d, 0x03, 0x1e, 0x03, 0x1f, 0x03,
+ 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03,
+ 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
+ 0x28, 0x03, 0x29, 0x03, 0x2a, 0x03, 0x2b, 0x03,
+ 0x2c, 0x03, 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03,
+ 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03,
+ 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
+ 0x38, 0x03, 0x39, 0x03, 0x3a, 0x03, 0x3b, 0x03,
+ 0x3c, 0x03, 0x3d, 0x03, 0x3e, 0x03, 0x3f, 0x03,
+ 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03,
+ 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
+ 0x48, 0x03, 0x49, 0x03, 0x4a, 0x03, 0x4b, 0x03,
+ 0x4c, 0x03, 0x4d, 0x03, 0x4e, 0x03, 0x4f, 0x03,
+ 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03,
+ 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
+ 0x58, 0x03, 0x59, 0x03, 0x5a, 0x03, 0x5b, 0x03,
+ 0x5c, 0x03, 0x5d, 0x03, 0x5e, 0x03, 0x5f, 0x03,
+ 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03,
+ 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
+ 0x68, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x03,
+ 0x6c, 0x03, 0x6d, 0x03, 0x6e, 0x03, 0x6f, 0x03,
+ 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03,
+ 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
+ 0x78, 0x03, 0x79, 0x03, 0x7a, 0x03, 0xfd, 0x03,
+ 0xfe, 0x03, 0xff, 0x03, 0x7e, 0x03, 0x7f, 0x03,
+ 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03,
+ 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
+ 0x88, 0x03, 0x89, 0x03, 0x8a, 0x03, 0x8b, 0x03,
+ 0x8c, 0x03, 0x8d, 0x03, 0x8e, 0x03, 0x8f, 0x03,
+ 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+ 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+ 0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+ 0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+ 0xa0, 0x03, 0xa1, 0x03, 0xa2, 0x03, 0xa3, 0x03,
+ 0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+ 0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+ 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8a, 0x03,
+ 0xb0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+ 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+ 0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+ 0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+ 0xa0, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa3, 0x03,
+ 0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+ 0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+ 0x8c, 0x03, 0x8e, 0x03, 0x8f, 0x03, 0xcf, 0x03,
+ 0xd0, 0x03, 0xd1, 0x03, 0xd2, 0x03, 0xd3, 0x03,
+ 0xd4, 0x03, 0xd5, 0x03, 0xd6, 0x03, 0xd7, 0x03,
+ 0xd8, 0x03, 0xd8, 0x03, 0xda, 0x03, 0xda, 0x03,
+ 0xdc, 0x03, 0xdc, 0x03, 0xde, 0x03, 0xde, 0x03,
+ 0xe0, 0x03, 0xe0, 0x03, 0xe2, 0x03, 0xe2, 0x03,
+ 0xe4, 0x03, 0xe4, 0x03, 0xe6, 0x03, 0xe6, 0x03,
+ 0xe8, 0x03, 0xe8, 0x03, 0xea, 0x03, 0xea, 0x03,
+ 0xec, 0x03, 0xec, 0x03, 0xee, 0x03, 0xee, 0x03,
+ 0xf0, 0x03, 0xf1, 0x03, 0xf9, 0x03, 0xf3, 0x03,
+ 0xf4, 0x03, 0xf5, 0x03, 0xf6, 0x03, 0xf7, 0x03,
+ 0xf7, 0x03, 0xf9, 0x03, 0xfa, 0x03, 0xfa, 0x03,
+ 0xfc, 0x03, 0xfd, 0x03, 0xfe, 0x03, 0xff, 0x03,
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+ 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+ 0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+ 0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+ 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+ 0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+ 0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+ 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+ 0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+ 0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+ 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+ 0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+ 0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+ 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+ 0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+ 0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+ 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+ 0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+ 0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+ 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04,
+ 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
+ 0x68, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6a, 0x04,
+ 0x6c, 0x04, 0x6c, 0x04, 0x6e, 0x04, 0x6e, 0x04,
+ 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04,
+ 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
+ 0x78, 0x04, 0x78, 0x04, 0x7a, 0x04, 0x7a, 0x04,
+ 0x7c, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x7e, 0x04,
+ 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04,
+ 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
+ 0x88, 0x04, 0x89, 0x04, 0x8a, 0x04, 0x8a, 0x04,
+ 0x8c, 0x04, 0x8c, 0x04, 0x8e, 0x04, 0x8e, 0x04,
+ 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04,
+ 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
+ 0x98, 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9a, 0x04,
+ 0x9c, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0x9e, 0x04,
+ 0xa0, 0x04, 0xa0, 0x04, 0xa2, 0x04, 0xa2, 0x04,
+ 0xa4, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa6, 0x04,
+ 0xa8, 0x04, 0xa8, 0x04, 0xaa, 0x04, 0xaa, 0x04,
+ 0xac, 0x04, 0xac, 0x04, 0xae, 0x04, 0xae, 0x04,
+ 0xb0, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb2, 0x04,
+ 0xb4, 0x04, 0xb4, 0x04, 0xb6, 0x04, 0xb6, 0x04,
+ 0xb8, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xba, 0x04,
+ 0xbc, 0x04, 0xbc, 0x04, 0xbe, 0x04, 0xbe, 0x04,
+ 0xc0, 0x04, 0xc1, 0x04, 0xc1, 0x04, 0xc3, 0x04,
+ 0xc3, 0x04, 0xc5, 0x04, 0xc5, 0x04, 0xc7, 0x04,
+ 0xc7, 0x04, 0xc9, 0x04, 0xc9, 0x04, 0xcb, 0x04,
+ 0xcb, 0x04, 0xcd, 0x04, 0xcd, 0x04, 0xc0, 0x04,
+ 0xd0, 0x04, 0xd0, 0x04, 0xd2, 0x04, 0xd2, 0x04,
+ 0xd4, 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd6, 0x04,
+ 0xd8, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xda, 0x04,
+ 0xdc, 0x04, 0xdc, 0x04, 0xde, 0x04, 0xde, 0x04,
+ 0xe0, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe2, 0x04,
+ 0xe4, 0x04, 0xe4, 0x04, 0xe6, 0x04, 0xe6, 0x04,
+ 0xe8, 0x04, 0xe8, 0x04, 0xea, 0x04, 0xea, 0x04,
+ 0xec, 0x04, 0xec, 0x04, 0xee, 0x04, 0xee, 0x04,
+ 0xf0, 0x04, 0xf0, 0x04, 0xf2, 0x04, 0xf2, 0x04,
+ 0xf4, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf6, 0x04,
+ 0xf8, 0x04, 0xf8, 0x04, 0xfa, 0x04, 0xfa, 0x04,
+ 0xfc, 0x04, 0xfc, 0x04, 0xfe, 0x04, 0xfe, 0x04,
+ 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05,
+ 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
+ 0x08, 0x05, 0x08, 0x05, 0x0a, 0x05, 0x0a, 0x05,
+ 0x0c, 0x05, 0x0c, 0x05, 0x0e, 0x05, 0x0e, 0x05,
+ 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05,
+ 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
+ 0x18, 0x05, 0x19, 0x05, 0x1a, 0x05, 0x1b, 0x05,
+ 0x1c, 0x05, 0x1d, 0x05, 0x1e, 0x05, 0x1f, 0x05,
+ 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05,
+ 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
+ 0x28, 0x05, 0x29, 0x05, 0x2a, 0x05, 0x2b, 0x05,
+ 0x2c, 0x05, 0x2d, 0x05, 0x2e, 0x05, 0x2f, 0x05,
+ 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+ 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+ 0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+ 0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+ 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+ 0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+ 0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+ 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
+ 0x58, 0x05, 0x59, 0x05, 0x5a, 0x05, 0x5b, 0x05,
+ 0x5c, 0x05, 0x5d, 0x05, 0x5e, 0x05, 0x5f, 0x05,
+ 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+ 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+ 0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+ 0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+ 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+ 0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+ 0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+ 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xff, 0xff,
+ 0xf6, 0x17, 0x63, 0x2c, 0x7e, 0x1d, 0x7f, 0x1d,
+ 0x80, 0x1d, 0x81, 0x1d, 0x82, 0x1d, 0x83, 0x1d,
+ 0x84, 0x1d, 0x85, 0x1d, 0x86, 0x1d, 0x87, 0x1d,
+ 0x88, 0x1d, 0x89, 0x1d, 0x8a, 0x1d, 0x8b, 0x1d,
+ 0x8c, 0x1d, 0x8d, 0x1d, 0x8e, 0x1d, 0x8f, 0x1d,
+ 0x90, 0x1d, 0x91, 0x1d, 0x92, 0x1d, 0x93, 0x1d,
+ 0x94, 0x1d, 0x95, 0x1d, 0x96, 0x1d, 0x97, 0x1d,
+ 0x98, 0x1d, 0x99, 0x1d, 0x9a, 0x1d, 0x9b, 0x1d,
+ 0x9c, 0x1d, 0x9d, 0x1d, 0x9e, 0x1d, 0x9f, 0x1d,
+ 0xa0, 0x1d, 0xa1, 0x1d, 0xa2, 0x1d, 0xa3, 0x1d,
+ 0xa4, 0x1d, 0xa5, 0x1d, 0xa6, 0x1d, 0xa7, 0x1d,
+ 0xa8, 0x1d, 0xa9, 0x1d, 0xaa, 0x1d, 0xab, 0x1d,
+ 0xac, 0x1d, 0xad, 0x1d, 0xae, 0x1d, 0xaf, 0x1d,
+ 0xb0, 0x1d, 0xb1, 0x1d, 0xb2, 0x1d, 0xb3, 0x1d,
+ 0xb4, 0x1d, 0xb5, 0x1d, 0xb6, 0x1d, 0xb7, 0x1d,
+ 0xb8, 0x1d, 0xb9, 0x1d, 0xba, 0x1d, 0xbb, 0x1d,
+ 0xbc, 0x1d, 0xbd, 0x1d, 0xbe, 0x1d, 0xbf, 0x1d,
+ 0xc0, 0x1d, 0xc1, 0x1d, 0xc2, 0x1d, 0xc3, 0x1d,
+ 0xc4, 0x1d, 0xc5, 0x1d, 0xc6, 0x1d, 0xc7, 0x1d,
+ 0xc8, 0x1d, 0xc9, 0x1d, 0xca, 0x1d, 0xcb, 0x1d,
+ 0xcc, 0x1d, 0xcd, 0x1d, 0xce, 0x1d, 0xcf, 0x1d,
+ 0xd0, 0x1d, 0xd1, 0x1d, 0xd2, 0x1d, 0xd3, 0x1d,
+ 0xd4, 0x1d, 0xd5, 0x1d, 0xd6, 0x1d, 0xd7, 0x1d,
+ 0xd8, 0x1d, 0xd9, 0x1d, 0xda, 0x1d, 0xdb, 0x1d,
+ 0xdc, 0x1d, 0xdd, 0x1d, 0xde, 0x1d, 0xdf, 0x1d,
+ 0xe0, 0x1d, 0xe1, 0x1d, 0xe2, 0x1d, 0xe3, 0x1d,
+ 0xe4, 0x1d, 0xe5, 0x1d, 0xe6, 0x1d, 0xe7, 0x1d,
+ 0xe8, 0x1d, 0xe9, 0x1d, 0xea, 0x1d, 0xeb, 0x1d,
+ 0xec, 0x1d, 0xed, 0x1d, 0xee, 0x1d, 0xef, 0x1d,
+ 0xf0, 0x1d, 0xf1, 0x1d, 0xf2, 0x1d, 0xf3, 0x1d,
+ 0xf4, 0x1d, 0xf5, 0x1d, 0xf6, 0x1d, 0xf7, 0x1d,
+ 0xf8, 0x1d, 0xf9, 0x1d, 0xfa, 0x1d, 0xfb, 0x1d,
+ 0xfc, 0x1d, 0xfd, 0x1d, 0xfe, 0x1d, 0xff, 0x1d,
+ 0x00, 0x1e, 0x00, 0x1e, 0x02, 0x1e, 0x02, 0x1e,
+ 0x04, 0x1e, 0x04, 0x1e, 0x06, 0x1e, 0x06, 0x1e,
+ 0x08, 0x1e, 0x08, 0x1e, 0x0a, 0x1e, 0x0a, 0x1e,
+ 0x0c, 0x1e, 0x0c, 0x1e, 0x0e, 0x1e, 0x0e, 0x1e,
+ 0x10, 0x1e, 0x10, 0x1e, 0x12, 0x1e, 0x12, 0x1e,
+ 0x14, 0x1e, 0x14, 0x1e, 0x16, 0x1e, 0x16, 0x1e,
+ 0x18, 0x1e, 0x18, 0x1e, 0x1a, 0x1e, 0x1a, 0x1e,
+ 0x1c, 0x1e, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x20, 0x1e, 0x20, 0x1e, 0x22, 0x1e, 0x22, 0x1e,
+ 0x24, 0x1e, 0x24, 0x1e, 0x26, 0x1e, 0x26, 0x1e,
+ 0x28, 0x1e, 0x28, 0x1e, 0x2a, 0x1e, 0x2a, 0x1e,
+ 0x2c, 0x1e, 0x2c, 0x1e, 0x2e, 0x1e, 0x2e, 0x1e,
+ 0x30, 0x1e, 0x30, 0x1e, 0x32, 0x1e, 0x32, 0x1e,
+ 0x34, 0x1e, 0x34, 0x1e, 0x36, 0x1e, 0x36, 0x1e,
+ 0x38, 0x1e, 0x38, 0x1e, 0x3a, 0x1e, 0x3a, 0x1e,
+ 0x3c, 0x1e, 0x3c, 0x1e, 0x3e, 0x1e, 0x3e, 0x1e,
+ 0x40, 0x1e, 0x40, 0x1e, 0x42, 0x1e, 0x42, 0x1e,
+ 0x44, 0x1e, 0x44, 0x1e, 0x46, 0x1e, 0x46, 0x1e,
+ 0x48, 0x1e, 0x48, 0x1e, 0x4a, 0x1e, 0x4a, 0x1e,
+ 0x4c, 0x1e, 0x4c, 0x1e, 0x4e, 0x1e, 0x4e, 0x1e,
+ 0x50, 0x1e, 0x50, 0x1e, 0x52, 0x1e, 0x52, 0x1e,
+ 0x54, 0x1e, 0x54, 0x1e, 0x56, 0x1e, 0x56, 0x1e,
+ 0x58, 0x1e, 0x58, 0x1e, 0x5a, 0x1e, 0x5a, 0x1e,
+ 0x5c, 0x1e, 0x5c, 0x1e, 0x5e, 0x1e, 0x5e, 0x1e,
+ 0x60, 0x1e, 0x60, 0x1e, 0x62, 0x1e, 0x62, 0x1e,
+ 0x64, 0x1e, 0x64, 0x1e, 0x66, 0x1e, 0x66, 0x1e,
+ 0x68, 0x1e, 0x68, 0x1e, 0x6a, 0x1e, 0x6a, 0x1e,
+ 0x6c, 0x1e, 0x6c, 0x1e, 0x6e, 0x1e, 0x6e, 0x1e,
+ 0x70, 0x1e, 0x70, 0x1e, 0x72, 0x1e, 0x72, 0x1e,
+ 0x74, 0x1e, 0x74, 0x1e, 0x76, 0x1e, 0x76, 0x1e,
+ 0x78, 0x1e, 0x78, 0x1e, 0x7a, 0x1e, 0x7a, 0x1e,
+ 0x7c, 0x1e, 0x7c, 0x1e, 0x7e, 0x1e, 0x7e, 0x1e,
+ 0x80, 0x1e, 0x80, 0x1e, 0x82, 0x1e, 0x82, 0x1e,
+ 0x84, 0x1e, 0x84, 0x1e, 0x86, 0x1e, 0x86, 0x1e,
+ 0x88, 0x1e, 0x88, 0x1e, 0x8a, 0x1e, 0x8a, 0x1e,
+ 0x8c, 0x1e, 0x8c, 0x1e, 0x8e, 0x1e, 0x8e, 0x1e,
+ 0x90, 0x1e, 0x90, 0x1e, 0x92, 0x1e, 0x92, 0x1e,
+ 0x94, 0x1e, 0x94, 0x1e, 0x96, 0x1e, 0x97, 0x1e,
+ 0x98, 0x1e, 0x99, 0x1e, 0x9a, 0x1e, 0x9b, 0x1e,
+ 0x9c, 0x1e, 0x9d, 0x1e, 0x9e, 0x1e, 0x9f, 0x1e,
+ 0xa0, 0x1e, 0xa0, 0x1e, 0xa2, 0x1e, 0xa2, 0x1e,
+ 0xa4, 0x1e, 0xa4, 0x1e, 0xa6, 0x1e, 0xa6, 0x1e,
+ 0xa8, 0x1e, 0xa8, 0x1e, 0xaa, 0x1e, 0xaa, 0x1e,
+ 0xac, 0x1e, 0xac, 0x1e, 0xae, 0x1e, 0xae, 0x1e,
+ 0xb0, 0x1e, 0xb0, 0x1e, 0xb2, 0x1e, 0xb2, 0x1e,
+ 0xb4, 0x1e, 0xb4, 0x1e, 0xb6, 0x1e, 0xb6, 0x1e,
+ 0xb8, 0x1e, 0xb8, 0x1e, 0xba, 0x1e, 0xba, 0x1e,
+ 0xbc, 0x1e, 0xbc, 0x1e, 0xbe, 0x1e, 0xbe, 0x1e,
+ 0xc0, 0x1e, 0xc0, 0x1e, 0xc2, 0x1e, 0xc2, 0x1e,
+ 0xc4, 0x1e, 0xc4, 0x1e, 0xc6, 0x1e, 0xc6, 0x1e,
+ 0xc8, 0x1e, 0xc8, 0x1e, 0xca, 0x1e, 0xca, 0x1e,
+ 0xcc, 0x1e, 0xcc, 0x1e, 0xce, 0x1e, 0xce, 0x1e,
+ 0xd0, 0x1e, 0xd0, 0x1e, 0xd2, 0x1e, 0xd2, 0x1e,
+ 0xd4, 0x1e, 0xd4, 0x1e, 0xd6, 0x1e, 0xd6, 0x1e,
+ 0xd8, 0x1e, 0xd8, 0x1e, 0xda, 0x1e, 0xda, 0x1e,
+ 0xdc, 0x1e, 0xdc, 0x1e, 0xde, 0x1e, 0xde, 0x1e,
+ 0xe0, 0x1e, 0xe0, 0x1e, 0xe2, 0x1e, 0xe2, 0x1e,
+ 0xe4, 0x1e, 0xe4, 0x1e, 0xe6, 0x1e, 0xe6, 0x1e,
+ 0xe8, 0x1e, 0xe8, 0x1e, 0xea, 0x1e, 0xea, 0x1e,
+ 0xec, 0x1e, 0xec, 0x1e, 0xee, 0x1e, 0xee, 0x1e,
+ 0xf0, 0x1e, 0xf0, 0x1e, 0xf2, 0x1e, 0xf2, 0x1e,
+ 0xf4, 0x1e, 0xf4, 0x1e, 0xf6, 0x1e, 0xf6, 0x1e,
+ 0xf8, 0x1e, 0xf8, 0x1e, 0xfa, 0x1e, 0xfb, 0x1e,
+ 0xfc, 0x1e, 0xfd, 0x1e, 0xfe, 0x1e, 0xff, 0x1e,
+ 0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+ 0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+ 0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+ 0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+ 0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+ 0x1c, 0x1f, 0x1d, 0x1f, 0x16, 0x1f, 0x17, 0x1f,
+ 0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+ 0x1c, 0x1f, 0x1d, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f,
+ 0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+ 0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+ 0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+ 0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+ 0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+ 0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+ 0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+ 0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+ 0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+ 0x4c, 0x1f, 0x4d, 0x1f, 0x46, 0x1f, 0x47, 0x1f,
+ 0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+ 0x4c, 0x1f, 0x4d, 0x1f, 0x4e, 0x1f, 0x4f, 0x1f,
+ 0x50, 0x1f, 0x59, 0x1f, 0x52, 0x1f, 0x5b, 0x1f,
+ 0x54, 0x1f, 0x5d, 0x1f, 0x56, 0x1f, 0x5f, 0x1f,
+ 0x58, 0x1f, 0x59, 0x1f, 0x5a, 0x1f, 0x5b, 0x1f,
+ 0x5c, 0x1f, 0x5d, 0x1f, 0x5e, 0x1f, 0x5f, 0x1f,
+ 0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+ 0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+ 0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+ 0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+ 0xba, 0x1f, 0xbb, 0x1f, 0xc8, 0x1f, 0xc9, 0x1f,
+ 0xca, 0x1f, 0xcb, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+ 0xf8, 0x1f, 0xf9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+ 0xfa, 0x1f, 0xfb, 0x1f, 0x7e, 0x1f, 0x7f, 0x1f,
+ 0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+ 0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+ 0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+ 0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+ 0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+ 0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+ 0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+ 0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+ 0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+ 0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+ 0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+ 0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+ 0xb8, 0x1f, 0xb9, 0x1f, 0xb2, 0x1f, 0xbc, 0x1f,
+ 0xb4, 0x1f, 0xb5, 0x1f, 0xb6, 0x1f, 0xb7, 0x1f,
+ 0xb8, 0x1f, 0xb9, 0x1f, 0xba, 0x1f, 0xbb, 0x1f,
+ 0xbc, 0x1f, 0xbd, 0x1f, 0xbe, 0x1f, 0xbf, 0x1f,
+ 0xc0, 0x1f, 0xc1, 0x1f, 0xc2, 0x1f, 0xc3, 0x1f,
+ 0xc4, 0x1f, 0xc5, 0x1f, 0xc6, 0x1f, 0xc7, 0x1f,
+ 0xc8, 0x1f, 0xc9, 0x1f, 0xca, 0x1f, 0xcb, 0x1f,
+ 0xc3, 0x1f, 0xcd, 0x1f, 0xce, 0x1f, 0xcf, 0x1f,
+ 0xd8, 0x1f, 0xd9, 0x1f, 0xd2, 0x1f, 0xd3, 0x1f,
+ 0xd4, 0x1f, 0xd5, 0x1f, 0xd6, 0x1f, 0xd7, 0x1f,
+ 0xd8, 0x1f, 0xd9, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+ 0xdc, 0x1f, 0xdd, 0x1f, 0xde, 0x1f, 0xdf, 0x1f,
+ 0xe8, 0x1f, 0xe9, 0x1f, 0xe2, 0x1f, 0xe3, 0x1f,
+ 0xe4, 0x1f, 0xec, 0x1f, 0xe6, 0x1f, 0xe7, 0x1f,
+ 0xe8, 0x1f, 0xe9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+ 0xec, 0x1f, 0xed, 0x1f, 0xee, 0x1f, 0xef, 0x1f,
+ 0xf0, 0x1f, 0xf1, 0x1f, 0xf2, 0x1f, 0xf3, 0x1f,
+ 0xf4, 0x1f, 0xf5, 0x1f, 0xf6, 0x1f, 0xf7, 0x1f,
+ 0xf8, 0x1f, 0xf9, 0x1f, 0xfa, 0x1f, 0xfb, 0x1f,
+ 0xf3, 0x1f, 0xfd, 0x1f, 0xfe, 0x1f, 0xff, 0x1f,
+ 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
+ 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20,
+ 0x08, 0x20, 0x09, 0x20, 0x0a, 0x20, 0x0b, 0x20,
+ 0x0c, 0x20, 0x0d, 0x20, 0x0e, 0x20, 0x0f, 0x20,
+ 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
+ 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20,
+ 0x18, 0x20, 0x19, 0x20, 0x1a, 0x20, 0x1b, 0x20,
+ 0x1c, 0x20, 0x1d, 0x20, 0x1e, 0x20, 0x1f, 0x20,
+ 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
+ 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20,
+ 0x28, 0x20, 0x29, 0x20, 0x2a, 0x20, 0x2b, 0x20,
+ 0x2c, 0x20, 0x2d, 0x20, 0x2e, 0x20, 0x2f, 0x20,
+ 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
+ 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20,
+ 0x38, 0x20, 0x39, 0x20, 0x3a, 0x20, 0x3b, 0x20,
+ 0x3c, 0x20, 0x3d, 0x20, 0x3e, 0x20, 0x3f, 0x20,
+ 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
+ 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20,
+ 0x48, 0x20, 0x49, 0x20, 0x4a, 0x20, 0x4b, 0x20,
+ 0x4c, 0x20, 0x4d, 0x20, 0x4e, 0x20, 0x4f, 0x20,
+ 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
+ 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20,
+ 0x58, 0x20, 0x59, 0x20, 0x5a, 0x20, 0x5b, 0x20,
+ 0x5c, 0x20, 0x5d, 0x20, 0x5e, 0x20, 0x5f, 0x20,
+ 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
+ 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20,
+ 0x68, 0x20, 0x69, 0x20, 0x6a, 0x20, 0x6b, 0x20,
+ 0x6c, 0x20, 0x6d, 0x20, 0x6e, 0x20, 0x6f, 0x20,
+ 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
+ 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20,
+ 0x78, 0x20, 0x79, 0x20, 0x7a, 0x20, 0x7b, 0x20,
+ 0x7c, 0x20, 0x7d, 0x20, 0x7e, 0x20, 0x7f, 0x20,
+ 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
+ 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20,
+ 0x88, 0x20, 0x89, 0x20, 0x8a, 0x20, 0x8b, 0x20,
+ 0x8c, 0x20, 0x8d, 0x20, 0x8e, 0x20, 0x8f, 0x20,
+ 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
+ 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20,
+ 0x98, 0x20, 0x99, 0x20, 0x9a, 0x20, 0x9b, 0x20,
+ 0x9c, 0x20, 0x9d, 0x20, 0x9e, 0x20, 0x9f, 0x20,
+ 0xa0, 0x20, 0xa1, 0x20, 0xa2, 0x20, 0xa3, 0x20,
+ 0xa4, 0x20, 0xa5, 0x20, 0xa6, 0x20, 0xa7, 0x20,
+ 0xa8, 0x20, 0xa9, 0x20, 0xaa, 0x20, 0xab, 0x20,
+ 0xac, 0x20, 0xad, 0x20, 0xae, 0x20, 0xaf, 0x20,
+ 0xb0, 0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20,
+ 0xb4, 0x20, 0xb5, 0x20, 0xb6, 0x20, 0xb7, 0x20,
+ 0xb8, 0x20, 0xb9, 0x20, 0xba, 0x20, 0xbb, 0x20,
+ 0xbc, 0x20, 0xbd, 0x20, 0xbe, 0x20, 0xbf, 0x20,
+ 0xc0, 0x20, 0xc1, 0x20, 0xc2, 0x20, 0xc3, 0x20,
+ 0xc4, 0x20, 0xc5, 0x20, 0xc6, 0x20, 0xc7, 0x20,
+ 0xc8, 0x20, 0xc9, 0x20, 0xca, 0x20, 0xcb, 0x20,
+ 0xcc, 0x20, 0xcd, 0x20, 0xce, 0x20, 0xcf, 0x20,
+ 0xd0, 0x20, 0xd1, 0x20, 0xd2, 0x20, 0xd3, 0x20,
+ 0xd4, 0x20, 0xd5, 0x20, 0xd6, 0x20, 0xd7, 0x20,
+ 0xd8, 0x20, 0xd9, 0x20, 0xda, 0x20, 0xdb, 0x20,
+ 0xdc, 0x20, 0xdd, 0x20, 0xde, 0x20, 0xdf, 0x20,
+ 0xe0, 0x20, 0xe1, 0x20, 0xe2, 0x20, 0xe3, 0x20,
+ 0xe4, 0x20, 0xe5, 0x20, 0xe6, 0x20, 0xe7, 0x20,
+ 0xe8, 0x20, 0xe9, 0x20, 0xea, 0x20, 0xeb, 0x20,
+ 0xec, 0x20, 0xed, 0x20, 0xee, 0x20, 0xef, 0x20,
+ 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, 0x20,
+ 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20,
+ 0xf8, 0x20, 0xf9, 0x20, 0xfa, 0x20, 0xfb, 0x20,
+ 0xfc, 0x20, 0xfd, 0x20, 0xfe, 0x20, 0xff, 0x20,
+ 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
+ 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21,
+ 0x08, 0x21, 0x09, 0x21, 0x0a, 0x21, 0x0b, 0x21,
+ 0x0c, 0x21, 0x0d, 0x21, 0x0e, 0x21, 0x0f, 0x21,
+ 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
+ 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21,
+ 0x18, 0x21, 0x19, 0x21, 0x1a, 0x21, 0x1b, 0x21,
+ 0x1c, 0x21, 0x1d, 0x21, 0x1e, 0x21, 0x1f, 0x21,
+ 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
+ 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21,
+ 0x28, 0x21, 0x29, 0x21, 0x2a, 0x21, 0x2b, 0x21,
+ 0x2c, 0x21, 0x2d, 0x21, 0x2e, 0x21, 0x2f, 0x21,
+ 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
+ 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21,
+ 0x38, 0x21, 0x39, 0x21, 0x3a, 0x21, 0x3b, 0x21,
+ 0x3c, 0x21, 0x3d, 0x21, 0x3e, 0x21, 0x3f, 0x21,
+ 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
+ 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21,
+ 0x48, 0x21, 0x49, 0x21, 0x4a, 0x21, 0x4b, 0x21,
+ 0x4c, 0x21, 0x4d, 0x21, 0x32, 0x21, 0x4f, 0x21,
+ 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
+ 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21,
+ 0x58, 0x21, 0x59, 0x21, 0x5a, 0x21, 0x5b, 0x21,
+ 0x5c, 0x21, 0x5d, 0x21, 0x5e, 0x21, 0x5f, 0x21,
+ 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+ 0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+ 0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+ 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+ 0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+ 0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+ 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
+ 0x83, 0x21, 0xff, 0xff, 0x4b, 0x03, 0xb6, 0x24,
+ 0xb7, 0x24, 0xb8, 0x24, 0xb9, 0x24, 0xba, 0x24,
+ 0xbb, 0x24, 0xbc, 0x24, 0xbd, 0x24, 0xbe, 0x24,
+ 0xbf, 0x24, 0xc0, 0x24, 0xc1, 0x24, 0xc2, 0x24,
+ 0xc3, 0x24, 0xc4, 0x24, 0xc5, 0x24, 0xc6, 0x24,
+ 0xc7, 0x24, 0xc8, 0x24, 0xc9, 0x24, 0xca, 0x24,
+ 0xcb, 0x24, 0xcc, 0x24, 0xcd, 0x24, 0xce, 0x24,
+ 0xcf, 0x24, 0xff, 0xff, 0x46, 0x07, 0x00, 0x2c,
+ 0x01, 0x2c, 0x02, 0x2c, 0x03, 0x2c, 0x04, 0x2c,
+ 0x05, 0x2c, 0x06, 0x2c, 0x07, 0x2c, 0x08, 0x2c,
+ 0x09, 0x2c, 0x0a, 0x2c, 0x0b, 0x2c, 0x0c, 0x2c,
+ 0x0d, 0x2c, 0x0e, 0x2c, 0x0f, 0x2c, 0x10, 0x2c,
+ 0x11, 0x2c, 0x12, 0x2c, 0x13, 0x2c, 0x14, 0x2c,
+ 0x15, 0x2c, 0x16, 0x2c, 0x17, 0x2c, 0x18, 0x2c,
+ 0x19, 0x2c, 0x1a, 0x2c, 0x1b, 0x2c, 0x1c, 0x2c,
+ 0x1d, 0x2c, 0x1e, 0x2c, 0x1f, 0x2c, 0x20, 0x2c,
+ 0x21, 0x2c, 0x22, 0x2c, 0x23, 0x2c, 0x24, 0x2c,
+ 0x25, 0x2c, 0x26, 0x2c, 0x27, 0x2c, 0x28, 0x2c,
+ 0x29, 0x2c, 0x2a, 0x2c, 0x2b, 0x2c, 0x2c, 0x2c,
+ 0x2d, 0x2c, 0x2e, 0x2c, 0x5f, 0x2c, 0x60, 0x2c,
+ 0x60, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c,
+ 0x65, 0x2c, 0x66, 0x2c, 0x67, 0x2c, 0x67, 0x2c,
+ 0x69, 0x2c, 0x69, 0x2c, 0x6b, 0x2c, 0x6b, 0x2c,
+ 0x6d, 0x2c, 0x6e, 0x2c, 0x6f, 0x2c, 0x70, 0x2c,
+ 0x71, 0x2c, 0x72, 0x2c, 0x73, 0x2c, 0x74, 0x2c,
+ 0x75, 0x2c, 0x75, 0x2c, 0x77, 0x2c, 0x78, 0x2c,
+ 0x79, 0x2c, 0x7a, 0x2c, 0x7b, 0x2c, 0x7c, 0x2c,
+ 0x7d, 0x2c, 0x7e, 0x2c, 0x7f, 0x2c, 0x80, 0x2c,
+ 0x80, 0x2c, 0x82, 0x2c, 0x82, 0x2c, 0x84, 0x2c,
+ 0x84, 0x2c, 0x86, 0x2c, 0x86, 0x2c, 0x88, 0x2c,
+ 0x88, 0x2c, 0x8a, 0x2c, 0x8a, 0x2c, 0x8c, 0x2c,
+ 0x8c, 0x2c, 0x8e, 0x2c, 0x8e, 0x2c, 0x90, 0x2c,
+ 0x90, 0x2c, 0x92, 0x2c, 0x92, 0x2c, 0x94, 0x2c,
+ 0x94, 0x2c, 0x96, 0x2c, 0x96, 0x2c, 0x98, 0x2c,
+ 0x98, 0x2c, 0x9a, 0x2c, 0x9a, 0x2c, 0x9c, 0x2c,
+ 0x9c, 0x2c, 0x9e, 0x2c, 0x9e, 0x2c, 0xa0, 0x2c,
+ 0xa0, 0x2c, 0xa2, 0x2c, 0xa2, 0x2c, 0xa4, 0x2c,
+ 0xa4, 0x2c, 0xa6, 0x2c, 0xa6, 0x2c, 0xa8, 0x2c,
+ 0xa8, 0x2c, 0xaa, 0x2c, 0xaa, 0x2c, 0xac, 0x2c,
+ 0xac, 0x2c, 0xae, 0x2c, 0xae, 0x2c, 0xb0, 0x2c,
+ 0xb0, 0x2c, 0xb2, 0x2c, 0xb2, 0x2c, 0xb4, 0x2c,
+ 0xb4, 0x2c, 0xb6, 0x2c, 0xb6, 0x2c, 0xb8, 0x2c,
+ 0xb8, 0x2c, 0xba, 0x2c, 0xba, 0x2c, 0xbc, 0x2c,
+ 0xbc, 0x2c, 0xbe, 0x2c, 0xbe, 0x2c, 0xc0, 0x2c,
+ 0xc0, 0x2c, 0xc2, 0x2c, 0xc2, 0x2c, 0xc4, 0x2c,
+ 0xc4, 0x2c, 0xc6, 0x2c, 0xc6, 0x2c, 0xc8, 0x2c,
+ 0xc8, 0x2c, 0xca, 0x2c, 0xca, 0x2c, 0xcc, 0x2c,
+ 0xcc, 0x2c, 0xce, 0x2c, 0xce, 0x2c, 0xd0, 0x2c,
+ 0xd0, 0x2c, 0xd2, 0x2c, 0xd2, 0x2c, 0xd4, 0x2c,
+ 0xd4, 0x2c, 0xd6, 0x2c, 0xd6, 0x2c, 0xd8, 0x2c,
+ 0xd8, 0x2c, 0xda, 0x2c, 0xda, 0x2c, 0xdc, 0x2c,
+ 0xdc, 0x2c, 0xde, 0x2c, 0xde, 0x2c, 0xe0, 0x2c,
+ 0xe0, 0x2c, 0xe2, 0x2c, 0xe2, 0x2c, 0xe4, 0x2c,
+ 0xe5, 0x2c, 0xe6, 0x2c, 0xe7, 0x2c, 0xe8, 0x2c,
+ 0xe9, 0x2c, 0xea, 0x2c, 0xeb, 0x2c, 0xec, 0x2c,
+ 0xed, 0x2c, 0xee, 0x2c, 0xef, 0x2c, 0xf0, 0x2c,
+ 0xf1, 0x2c, 0xf2, 0x2c, 0xf3, 0x2c, 0xf4, 0x2c,
+ 0xf5, 0x2c, 0xf6, 0x2c, 0xf7, 0x2c, 0xf8, 0x2c,
+ 0xf9, 0x2c, 0xfa, 0x2c, 0xfb, 0x2c, 0xfc, 0x2c,
+ 0xfd, 0x2c, 0xfe, 0x2c, 0xff, 0x2c, 0xa0, 0x10,
+ 0xa1, 0x10, 0xa2, 0x10, 0xa3, 0x10, 0xa4, 0x10,
+ 0xa5, 0x10, 0xa6, 0x10, 0xa7, 0x10, 0xa8, 0x10,
+ 0xa9, 0x10, 0xaa, 0x10, 0xab, 0x10, 0xac, 0x10,
+ 0xad, 0x10, 0xae, 0x10, 0xaf, 0x10, 0xb0, 0x10,
+ 0xb1, 0x10, 0xb2, 0x10, 0xb3, 0x10, 0xb4, 0x10,
+ 0xb5, 0x10, 0xb6, 0x10, 0xb7, 0x10, 0xb8, 0x10,
+ 0xb9, 0x10, 0xba, 0x10, 0xbb, 0x10, 0xbc, 0x10,
+ 0xbd, 0x10, 0xbe, 0x10, 0xbf, 0x10, 0xc0, 0x10,
+ 0xc1, 0x10, 0xc2, 0x10, 0xc3, 0x10, 0xc4, 0x10,
+ 0xc5, 0x10, 0xff, 0xff, 0x1b, 0xd2, 0x21, 0xff,
+ 0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
+ 0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff,
+ 0x2a, 0xff, 0x2b, 0xff, 0x2c, 0xff, 0x2d, 0xff,
+ 0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
+ 0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff,
+ 0x36, 0xff, 0x37, 0xff, 0x38, 0xff, 0x39, 0xff,
+ 0x3a, 0xff, 0x5b, 0xff, 0x5c, 0xff, 0x5d, 0xff,
+ 0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
+ 0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff,
+ 0x66, 0xff, 0x67, 0xff, 0x68, 0xff, 0x69, 0xff,
+ 0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
+ 0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff,
+ 0x72, 0xff, 0x73, 0xff, 0x74, 0xff, 0x75, 0xff,
+ 0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
+ 0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff,
+ 0x7e, 0xff, 0x7f, 0xff, 0x80, 0xff, 0x81, 0xff,
+ 0x82, 0xff, 0x83, 0xff, 0x84, 0xff, 0x85, 0xff,
+ 0x86, 0xff, 0x87, 0xff, 0x88, 0xff, 0x89, 0xff,
+ 0x8a, 0xff, 0x8b, 0xff, 0x8c, 0xff, 0x8d, 0xff,
+ 0x8e, 0xff, 0x8f, 0xff, 0x90, 0xff, 0x91, 0xff,
+ 0x92, 0xff, 0x93, 0xff, 0x94, 0xff, 0x95, 0xff,
+ 0x96, 0xff, 0x97, 0xff, 0x98, 0xff, 0x99, 0xff,
+ 0x9a, 0xff, 0x9b, 0xff, 0x9c, 0xff, 0x9d, 0xff,
+ 0x9e, 0xff, 0x9f, 0xff, 0xa0, 0xff, 0xa1, 0xff,
+ 0xa2, 0xff, 0xa3, 0xff, 0xa4, 0xff, 0xa5, 0xff,
+ 0xa6, 0xff, 0xa7, 0xff, 0xa8, 0xff, 0xa9, 0xff,
+ 0xaa, 0xff, 0xab, 0xff, 0xac, 0xff, 0xad, 0xff,
+ 0xae, 0xff, 0xaf, 0xff, 0xb0, 0xff, 0xb1, 0xff,
+ 0xb2, 0xff, 0xb3, 0xff, 0xb4, 0xff, 0xb5, 0xff,
+ 0xb6, 0xff, 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xff,
+ 0xba, 0xff, 0xbb, 0xff, 0xbc, 0xff, 0xbd, 0xff,
+ 0xbe, 0xff, 0xbf, 0xff, 0xc0, 0xff, 0xc1, 0xff,
+ 0xc2, 0xff, 0xc3, 0xff, 0xc4, 0xff, 0xc5, 0xff,
+ 0xc6, 0xff, 0xc7, 0xff, 0xc8, 0xff, 0xc9, 0xff,
+ 0xca, 0xff, 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0xff,
+ 0xce, 0xff, 0xcf, 0xff, 0xd0, 0xff, 0xd1, 0xff,
+ 0xd2, 0xff, 0xd3, 0xff, 0xd4, 0xff, 0xd5, 0xff,
+ 0xd6, 0xff, 0xd7, 0xff, 0xd8, 0xff, 0xd9, 0xff,
+ 0xda, 0xff, 0xdb, 0xff, 0xdc, 0xff, 0xdd, 0xff,
+ 0xde, 0xff, 0xdf, 0xff, 0xe0, 0xff, 0xe1, 0xff,
+ 0xe2, 0xff, 0xe3, 0xff, 0xe4, 0xff, 0xe5, 0xff,
+ 0xe6, 0xff, 0xe7, 0xff, 0xe8, 0xff, 0xe9, 0xff,
+ 0xea, 0xff, 0xeb, 0xff, 0xec, 0xff, 0xed, 0xff,
+ 0xee, 0xff, 0xef, 0xff, 0xf0, 0xff, 0xf1, 0xff,
+ 0xf2, 0xff, 0xf3, 0xff, 0xf4, 0xff, 0xf5, 0xff,
+ 0xf6, 0xff, 0xf7, 0xff, 0xf8, 0xff, 0xf9, 0xff,
+ 0xfa, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xfd, 0xff,
+ 0xfe, 0xff, 0xff, 0xff
+};
--- /dev/null
+/*
+ uctc.h (30.10.10)
+ Upper Case Table declaration.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCTC_H_INCLUDED
+#define MKFS_UCTC_H_INCLUDED
+
+#include <stdint.h>
+
+extern uint8_t upcase_table[5836];
+
+#endif /* ifndef MKFS_UCTC_H_INCLUDED */
--- /dev/null
+/*
+ vbr.c (09.11.10)
+ Volume Boot Record creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <string.h>
+
+static off_t vbr_alignment(void)
+{
+ return get_sector_size();
+}
+
+static off_t vbr_size(void)
+{
+ return 12 * get_sector_size();
+}
+
+static void init_sb(struct exfat_super_block* sb)
+{
+ uint32_t clusters_max;
+ uint32_t fat_sectors;
+
+ clusters_max = get_volume_size() / get_cluster_size();
+ fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t),
+ get_sector_size());
+
+ memset(sb, 0, sizeof(struct exfat_super_block));
+ sb->jump[0] = 0xeb;
+ sb->jump[1] = 0x76;
+ sb->jump[2] = 0x90;
+ memcpy(sb->oem_name, "EXFAT ", sizeof(sb->oem_name));
+ sb->sector_start = cpu_to_le64(get_first_sector());
+ sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size());
+ sb->fat_sector_start = cpu_to_le32(
+ fat.get_alignment() / get_sector_size());
+ sb->fat_sector_count = cpu_to_le32(ROUND_UP(
+ le32_to_cpu(sb->fat_sector_start) + fat_sectors,
+ 1 << get_spc_bits()) -
+ le32_to_cpu(sb->fat_sector_start));
+ sb->cluster_sector_start = cpu_to_le32(
+ get_position(&cbm) / get_sector_size());
+ sb->cluster_count = cpu_to_le32(clusters_max -
+ ((le32_to_cpu(sb->fat_sector_start) +
+ le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits()));
+ sb->rootdir_cluster = cpu_to_le32(
+ (get_position(&rootdir) - get_position(&cbm)) / get_cluster_size()
+ + EXFAT_FIRST_DATA_CLUSTER);
+ sb->volume_serial = cpu_to_le32(get_volume_serial());
+ sb->version.major = 1;
+ sb->version.minor = 0;
+ sb->volume_state = cpu_to_le16(0);
+ sb->sector_bits = get_sector_bits();
+ sb->spc_bits = get_spc_bits();
+ sb->fat_count = 1;
+ sb->drive_no = 0x80;
+ sb->allocated_percent = 0;
+ sb->boot_signature = cpu_to_le16(0xaa55);
+}
+
+static int vbr_write(struct exfat_dev* dev)
+{
+ struct exfat_super_block sb;
+ uint32_t checksum;
+ le32_t* sector = malloc(get_sector_size());
+ size_t i;
+
+ if (sector == NULL)
+ {
+ exfat_error("failed to allocate sector-sized block of memory");
+ return 1;
+ }
+
+ init_sb(&sb);
+ if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0)
+ {
+ free(sector);
+ exfat_error("failed to write super block sector");
+ return 1;
+ }
+ checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block));
+
+ memset(sector, 0, get_sector_size());
+ sector[get_sector_size() / sizeof(sector[0]) - 1] =
+ cpu_to_le32(0xaa550000);
+ for (i = 0; i < 8; i++)
+ {
+ if (exfat_write(dev, sector, get_sector_size()) < 0)
+ {
+ free(sector);
+ exfat_error("failed to write a sector with boot signature");
+ return 1;
+ }
+ checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+ }
+
+ memset(sector, 0, get_sector_size());
+ for (i = 0; i < 2; i++)
+ {
+ if (exfat_write(dev, sector, get_sector_size()) < 0)
+ {
+ free(sector);
+ exfat_error("failed to write an empty sector");
+ return 1;
+ }
+ checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+ }
+
+ for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++)
+ sector[i] = cpu_to_le32(checksum);
+ if (exfat_write(dev, sector, get_sector_size()) < 0)
+ {
+ free(sector);
+ exfat_error("failed to write checksum sector");
+ return 1;
+ }
+
+ free(sector);
+ return 0;
+}
+
+const struct fs_object vbr =
+{
+ .get_alignment = vbr_alignment,
+ .get_size = vbr_size,
+ .write = vbr_write,
+};
--- /dev/null
+/*
+ vbr.h (09.11.10)
+ Volume Boot Record creation code.
+
+ Free exFAT implementation.
+ Copyright (C) 2011-2018 Andrew Nayenko
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_VBR_H_INCLUDED
+#define MKFS_VBR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object vbr;
+
+#endif /* ifndef MKFS_VBR_H_INCLUDED */
--- /dev/null
+#!/bin/sh
+
+rm -rf include
+rm -rf lib
+
+cd release
+gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_64.a *.o
+rm -f *.o
+
+
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_32.a *.o
+rm -f *.o
+
+
+aarch64-linux-gnu-gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_aa64.a *.o
+rm -f *.o
+
+
+cd -
+
+
+mkdir lib
+mkdir include
+
+mv release/*.a lib/
+cp -a release/*.h include/
+
--- /dev/null
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK 0
+#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
+#define FAT_INIT_INVALID_SIGNATURE (-3)
+#define FAT_INIT_ENDIAN_ERROR (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
+#define FAT_INIT_STRUCT_PACKING (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+ // User supplied function pointers for disk IO
+ fn_diskio_read read_media;
+ fn_diskio_write write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+ uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+ uint32 address;
+ int dirty;
+ uint8 * ptr;
+
+ // Next in chain of sector buffers
+ struct fat_buffer *next;
+};
+
+typedef enum eFatType
+{
+ FAT_TYPE_16,
+ FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+ // Filesystem globals
+ uint8 sectors_per_cluster;
+ uint32 cluster_begin_lba;
+ uint32 rootdir_first_cluster;
+ uint32 rootdir_first_sector;
+ uint32 rootdir_sectors;
+ uint32 fat_begin_lba;
+ uint16 fs_info_sector;
+ uint32 lba_begin;
+ uint32 fat_sectors;
+ uint32 next_free_cluster;
+ uint16 root_entry_count;
+ uint16 reserved_sectors;
+ uint8 num_of_fats;
+ tFatType fat_type;
+
+ // Disk/Media API
+ struct disk_if disk_io;
+
+ // [Optional] Thread Safety
+ void (*fl_lock)(void);
+ void (*fl_unlock)(void);
+
+ // Working buffer
+ struct fat_buffer currentsector;
+
+ // FAT Buffer
+ struct fat_buffer *fat_buffer_head;
+ struct fat_buffer fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+ uint32 sector;
+ uint32 cluster;
+ uint8 offset;
+};
+
+struct fs_dir_ent
+{
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 is_dir;
+ uint32 cluster;
+ uint32 size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ uint16 access_date;
+ uint16 write_time;
+ uint16 write_date;
+ uint16 create_date;
+ uint16 create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs);
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void fatfs_show_details(struct fatfs *fs);
+uint32 fatfs_get_root_cluster(struct fatfs *fs);
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
--- /dev/null
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
--- /dev/null
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+// FAT32 Offsets
+// Name Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT 0 // Length = 3
+#define BS_OEMNAME 3 // Length = 8
+#define BPB_BYTSPERSEC 11 // Length = 2
+#define BPB_SECPERCLUS 13 // Length = 1
+#define BPB_RSVDSECCNT 14 // Length = 2
+#define BPB_NUMFATS 16 // Length = 1
+#define BPB_ROOTENTCNT 17 // Length = 2
+#define BPB_TOTSEC16 19 // Length = 2
+#define BPB_MEDIA 21 // Length = 1
+#define BPB_FATSZ16 22 // Length = 2
+#define BPB_SECPERTRK 24 // Length = 2
+#define BPB_NUMHEADS 26 // Length = 2
+#define BPB_HIDDSEC 28 // Length = 4
+#define BPB_TOTSEC32 32 // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM 36 // Length = 1
+#define BS_FAT_BOOTSIG 38 // Length = 1
+#define BS_FAT_VOLID 39 // Length = 4
+#define BS_FAT_VOLLAB 43 // Length = 11
+#define BS_FAT_FILSYSTYPE 54 // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32 36 // Length = 4
+#define BPB_FAT32_EXTFLAGS 40 // Length = 2
+#define BPB_FAT32_FSVER 42 // Length = 2
+#define BPB_FAT32_ROOTCLUS 44 // Length = 4
+#define BPB_FAT32_FSINFO 48 // Length = 2
+#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
+#define BS_FAT32_DRVNUM 64 // Length = 1
+#define BS_FAT32_BOOTSIG 66 // Length = 1
+#define BS_FAT32_VOLID 67 // Length = 4
+#define BS_FAT32_VOLLAB 71 // Length = 11
+#define BS_FAT32_FILSYSTYPE 82 // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12 1
+#define FAT_TYPE_FAT16 2
+#define FAT_TYPE_FAT32 3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION 510
+#define SIGNATURE_VALUE 0xAA55
+#define PARTITION1_TYPECODE_LOCATION 450
+#define FAT32_TYPECODE1 0x0B
+#define FAT32_TYPECODE2 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION 454
+#define PARTITION1_SIZE_LOCATION 458
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define FAT_SFN_SIZE_FULL 11
+#define FAT_SFN_SIZE_PARTIAL 8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY 0x01
+#define FILE_ATTR_HIDDEN 0x02
+#define FILE_ATTR_SYSTEM 0x04
+#define FILE_ATTR_SYSHID 0x06
+#define FILE_ATTR_VOLUME_ID 0x08
+#define FILE_ATTR_DIRECTORY 0x10
+#define FILE_ATTR_ARCHIVE 0x20
+#define FILE_ATTR_LFN_TEXT 0x0F
+#define FILE_HEADER_BLANK 0x00
+#define FILE_HEADER_DELETED 0xE5
+#define FILE_TYPE_DIR 0x10
+#define FILE_TYPE_FILE 0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT 11
+#define FAT_TIME_HOURS_MASK 0x1F
+#define FAT_TIME_MINUTES_SHIFT 5
+#define FAT_TIME_MINUTES_MASK 0x3F
+#define FAT_TIME_SECONDS_SHIFT 0
+#define FAT_TIME_SECONDS_MASK 0x1F
+#define FAT_TIME_SECONDS_SCALE 2
+#define FAT_DATE_YEAR_SHIFT 9
+#define FAT_DATE_YEAR_MASK 0x7F
+#define FAT_DATE_MONTH_SHIFT 5
+#define FAT_DATE_MONTH_MASK 0xF
+#define FAT_DATE_DAY_SHIFT 0
+#define FAT_DATE_DAY_MASK 0x1F
+#define FAT_DATE_YEAR_OFFSET 1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER 0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+ uint8 Name[11];
+ uint8 Attr;
+ uint8 NTRes;
+ uint8 CrtTimeTenth;
+ uint8 CrtTime[2];
+ uint8 CrtDate[2];
+ uint8 LstAccDate[2];
+ uint16 FstClusHI;
+ uint8 WrtTime[2];
+ uint8 WrtDate[2];
+ uint16 FstClusLO;
+ uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
--- /dev/null
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+ #define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+ #define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+ #define SEEK_SET 0
+#endif
+
+#ifndef EOF
+ #define EOF (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+ uint32 ClusterIdx;
+ uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+ uint32 parentcluster;
+ uint32 startcluster;
+ uint32 bytenum;
+ uint32 filelength;
+ int filelength_changed;
+ char path[FATFS_MAX_LONG_FILENAME];
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+ uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+ // Cluster Lookup
+ struct cluster_lookup last_fat_lookup;
+
+ // Read/Write sector buffer
+ uint8 file_data_sector[FAT_SECTOR_SIZE];
+ uint32 file_data_address;
+ int file_data_dirty;
+
+ // File fopen flags
+ uint8 flags;
+#define FILE_READ (1 << 0)
+#define FILE_WRITE (1 << 1)
+#define FILE_APPEND (1 << 2)
+#define FILE_BINARY (1 << 3)
+#define FILE_ERASE (1 << 4)
+#define FILE_CREATE (1 << 5)
+
+ struct fat_node list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void fl_init(void);
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void fl_shutdown(void);
+
+// Standard API
+void* fl_fopen(const char *path, const char *modifiers);
+void fl_fclose(void *file);
+int fl_fflush(void *file);
+int fl_fgetc(void *file);
+char * fl_fgets(char *s, int n, void *f);
+int fl_fputc(int c, void *file);
+int fl_fputs(const char * str, void *file);
+int fl_fwrite(const void * data, int size, int count, void *file );
+int fl_fread(void * data, int size, int count, void *file );
+int fl_fseek(void *file , long offset , int origin );
+int fl_fgetpos(void *file , uint32 * position);
+long fl_ftell(void *f);
+int fl_feof(void *f);
+int fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status FL_DIR;
+typedef struct fs_dir_ent fl_dirent;
+
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir);
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int fl_closedir(FL_DIR* dir);
+
+// Extensions
+void fl_listdirectory(const char *path);
+int fl_createdirectory(const char *path);
+int fl_is_dir(const char *path);
+
+int fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE FL_FILE
+
+#define fopen(a,b) fl_fopen(a, b)
+#define fclose(a) fl_fclose(a)
+#define fflush(a) fl_fflush(a)
+#define fgetc(a) fl_fgetc(a)
+#define fgets(a,b,c) fl_fgets(a, b, c)
+#define fputc(a,b) fl_fputc(a, b)
+#define fputs(a,b) fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d) fl_fread(a, b, c, d)
+#define fseek(a,b,c) fl_fseek(a, b, c)
+#define fgetpos(a,b) fl_fgetpos(a, b)
+#define ftell(a) fl_ftell(a)
+#define feof(a) fl_feof(a)
+#define remove(a) fl_remove(a)
+#define mkdir(a) fl_createdirectory(a)
+#define rmdir(a) 0
+
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
--- /dev/null
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+ #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+ #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+ struct fat_node *previous;
+ struct fat_node *next;
+};
+
+struct fat_list
+{
+ struct fat_node *head;
+ struct fat_node *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p) (p)->next
+#define fat_list_prev(l, p) (p)->previous
+#define fat_list_first(l) (l)->head
+#define fat_list_last(l) (l)->tail
+#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if(!node->previous)
+ list->head = node->next;
+ else
+ node->previous->next = node->next;
+
+ if(!node->next)
+ list->tail = node->previous;
+ else
+ node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node;
+ new_node->next = node->next;
+ if (!node->next)
+ list->tail = new_node;
+ else
+ node->next->previous = new_node;
+ node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node->previous;
+ new_node->next = node;
+ if (!node->previous)
+ list->head = new_node;
+ else
+ node->previous->next = new_node;
+ node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ node->previous = 0;
+ node->next = 0;
+ }
+ else
+ fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->tail)
+ fat_list_insert_first(list, node);
+ else
+ fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+ struct fat_node * node;
+
+ FAT_ASSERT(list);
+
+ node = fat_list_first(list);
+ if (node)
+ fat_list_remove(list, node);
+
+ return node;
+}
+
+#endif
+
--- /dev/null
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES 20
+#define MAX_LFN_ENTRY_LENGTH 13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); \
+ buffer[location+2] = (uint8)((value>>16)&0xFF); \
+ buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Structure (max 260 LFN length)
+ uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+ uint8 Null;
+#endif
+ uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int fatfs_entry_is_file(struct fat_dir_entry *entry);
+int fatfs_lfn_entries_required(char *filename);
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16 fatfs_convert_to_fat_date(int day, int month, int year);
+void fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
--- /dev/null
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+ #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+ #define FATFS_IS_LITTLE_ENDIAN 1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+ #define FATFS_MAX_LONG_FILENAME 260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+ #define FATFS_MAX_OPEN_FILES 2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES 128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+ #define FATFS_INC_WRITE_SUPPORT 1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+ #define FATFS_INC_LFN_SUPPORT 1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+ #define FATFS_DIR_LIST_SUPPORT 1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+ #define FATFS_INC_TIME_DATE_SUPPORT 0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+ #define FATFS_INC_FORMAT_SUPPORT 1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE 512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+ // Don't include stdio, but there is a printf function available
+ #ifdef FAT_PRINTF_NOINC_STDIO
+ extern int printf(const char* ctrl1, ... );
+ #define FAT_PRINTF(a) printf a
+ // Include stdio to use printf
+ #else
+ #include <stdio.h>
+ #define FAT_PRINTF(a) printf a
+ #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+ #include <time.h>
+#endif
+
+#endif
--- /dev/null
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs);
+int fatfs_fat_purge(struct fatfs *fs);
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32 fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
--- /dev/null
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+ #if __SIZEOF_LONG__ == 8
+ #define FATFS_DEF_UINT32_AS_INT
+ #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+
+ // If compiling on a 64-bit machine, use int as 32-bits
+ #ifdef FATFS_DEF_UINT32_AS_INT
+ typedef unsigned int uint32;
+ // Else for 32-bit machines & embedded systems, use long...
+ #else
+ typedef unsigned long uint32;
+ #endif
+#endif
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+ #define FAT_HTONS(n) (n)
+ #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+ #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+ #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+ ((((uint32)(n) & 0xFF00)) << 8) | \
+ ((((uint32)(n) & 0xFF0000)) >> 8) | \
+ ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED __attribute__ ((packed))
+#else
+ // Other compilers may require other methods of packing structures
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
--- /dev/null
+File IO Lib API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+void fl_init(void)\r
+\r
+ Called to initialize FAT IO library.\r
+ This should be called prior to any other functions.\r
+\r
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))\r
+\r
+ [Optional] File system thread safety locking functions.\r
+ For thread safe operation, you should provide lock() and unlock() functions.\r
+ Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.\r
+\r
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+ This function is used to attach system specific disk/media access functions. \r
+ This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).\r
+\r
+void fl_shutdown(void)\r
+\r
+ Shutdown the FAT IO library. This purges any un-saved data back to disk.\r
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE\r
+ Version 2, June 1991\r
+\r
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\r
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+ Preamble\r
+\r
+ The licenses for most software are designed to take away your\r
+freedom to share and change it. By contrast, the GNU General Public\r
+License is intended to guarantee your freedom to share and change free\r
+software--to make sure the software is free for all its users. This\r
+General Public License applies to most of the Free Software\r
+Foundation's software and to any other program whose authors commit to\r
+using it. (Some other Free Software Foundation software is covered by\r
+the GNU Library General Public License instead.) You can apply it to\r
+your programs, too.\r
+\r
+ When we speak of free software, we are referring to freedom, not\r
+price. Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+this service if you wish), that you receive source code or can get it\r
+if you want it, that you can change the software or use pieces of it\r
+in new free programs; and that you know you can do these things.\r
+\r
+ To protect your rights, we need to make restrictions that forbid\r
+anyone to deny you these rights or to ask you to surrender the rights.\r
+These restrictions translate to certain responsibilities for you if you\r
+distribute copies of the software, or if you modify it.\r
+\r
+ For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must give the recipients all the rights that\r
+you have. You must make sure that they, too, receive or can get the\r
+source code. And you must show them these terms so they know their\r
+rights.\r
+\r
+ We protect your rights with two steps: (1) copyright the software, and\r
+(2) offer you this license which gives you legal permission to copy,\r
+distribute and/or modify the software.\r
+\r
+ Also, for each author's protection and ours, we want to make certain\r
+that everyone understands that there is no warranty for this free\r
+software. If the software is modified by someone else and passed on, we\r
+want its recipients to know that what they have is not the original, so\r
+that any problems introduced by others will not reflect on the original\r
+authors' reputations.\r
+\r
+ Finally, any free program is threatened constantly by software\r
+patents. We wish to avoid the danger that redistributors of a free\r
+program will individually obtain patent licenses, in effect making the\r
+program proprietary. To prevent this, we have made it clear that any\r
+patent must be licensed for everyone's free use or not licensed at all.\r
+\r
+ The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+\r
+ GNU GENERAL PUBLIC LICENSE\r
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+\r
+ 0. This License applies to any program or other work which contains\r
+a notice placed by the copyright holder saying it may be distributed\r
+under the terms of this General Public License. The "Program", below,\r
+refers to any such program or work, and a "work based on the Program"\r
+means either the Program or any derivative work under copyright law:\r
+that is to say, a work containing the Program or a portion of it,\r
+either verbatim or with modifications and/or translated into another\r
+language. (Hereinafter, translation is included without limitation in\r
+the term "modification".) Each licensee is addressed as "you".\r
+\r
+Activities other than copying, distribution and modification are not\r
+covered by this License; they are outside its scope. The act of\r
+running the Program is not restricted, and the output from the Program\r
+is covered only if its contents constitute a work based on the\r
+Program (independent of having been made by running the Program).\r
+Whether that is true depends on what the Program does.\r
+\r
+ 1. You may copy and distribute verbatim copies of the Program's\r
+source code as you receive it, in any medium, provided that you\r
+conspicuously and appropriately publish on each copy an appropriate\r
+copyright notice and disclaimer of warranty; keep intact all the\r
+notices that refer to this License and to the absence of any warranty;\r
+and give any other recipients of the Program a copy of this License\r
+along with the Program.\r
+\r
+You may charge a fee for the physical act of transferring a copy, and\r
+you may at your option offer warranty protection in exchange for a fee.\r
+\r
+ 2. You may modify your copy or copies of the Program or any portion\r
+of it, thus forming a work based on the Program, and copy and\r
+distribute such modifications or work under the terms of Section 1\r
+above, provided that you also meet all of these conditions:\r
+\r
+ a) You must cause the modified files to carry prominent notices\r
+ stating that you changed the files and the date of any change.\r
+\r
+ b) You must cause any work that you distribute or publish, that in\r
+ whole or in part contains or is derived from the Program or any\r
+ part thereof, to be licensed as a whole at no charge to all third\r
+ parties under the terms of this License.\r
+\r
+ c) If the modified program normally reads commands interactively\r
+ when run, you must cause it, when started running for such\r
+ interactive use in the most ordinary way, to print or display an\r
+ announcement including an appropriate copyright notice and a\r
+ notice that there is no warranty (or else, saying that you provide\r
+ a warranty) and that users may redistribute the program under\r
+ these conditions, and telling the user how to view a copy of this\r
+ License. (Exception: if the Program itself is interactive but\r
+ does not normally print such an announcement, your work based on\r
+ the Program is not required to print an announcement.)\r
+\r
+\r
+These requirements apply to the modified work as a whole. If\r
+identifiable sections of that work are not derived from the Program,\r
+and can be reasonably considered independent and separate works in\r
+themselves, then this License, and its terms, do not apply to those\r
+sections when you distribute them as separate works. But when you\r
+distribute the same sections as part of a whole which is a work based\r
+on the Program, the distribution of the whole must be on the terms of\r
+this License, whose permissions for other licensees extend to the\r
+entire whole, and thus to each and every part regardless of who wrote it.\r
+\r
+Thus, it is not the intent of this section to claim rights or contest\r
+your rights to work written entirely by you; rather, the intent is to\r
+exercise the right to control the distribution of derivative or\r
+collective works based on the Program.\r
+\r
+In addition, mere aggregation of another work not based on the Program\r
+with the Program (or with a work based on the Program) on a volume of\r
+a storage or distribution medium does not bring the other work under\r
+the scope of this License.\r
+\r
+ 3. You may copy and distribute the Program (or a work based on it,\r
+under Section 2) in object code or executable form under the terms of\r
+Sections 1 and 2 above provided that you also do one of the following:\r
+\r
+ a) Accompany it with the complete corresponding machine-readable\r
+ source code, which must be distributed under the terms of Sections\r
+ 1 and 2 above on a medium customarily used for software interchange; or,\r
+\r
+ b) Accompany it with a written offer, valid for at least three\r
+ years, to give any third party, for a charge no more than your\r
+ cost of physically performing source distribution, a complete\r
+ machine-readable copy of the corresponding source code, to be\r
+ distributed under the terms of Sections 1 and 2 above on a medium\r
+ customarily used for software interchange; or,\r
+\r
+ c) Accompany it with the information you received as to the offer\r
+ to distribute corresponding source code. (This alternative is\r
+ allowed only for noncommercial distribution and only if you\r
+ received the program in object code or executable form with such\r
+ an offer, in accord with Subsection b above.)\r
+\r
+The source code for a work means the preferred form of the work for\r
+making modifications to it. For an executable work, complete source\r
+code means all the source code for all modules it contains, plus any\r
+associated interface definition files, plus the scripts used to\r
+control compilation and installation of the executable. However, as a\r
+special exception, the source code distributed need not include\r
+anything that is normally distributed (in either source or binary\r
+form) with the major components (compiler, kernel, and so on) of the\r
+operating system on which the executable runs, unless that component\r
+itself accompanies the executable.\r
+\r
+If distribution of executable or object code is made by offering\r
+access to copy from a designated place, then offering equivalent\r
+access to copy the source code from the same place counts as\r
+distribution of the source code, even though third parties are not\r
+compelled to copy the source along with the object code.\r
+\r
+\r
+ 4. You may not copy, modify, sublicense, or distribute the Program\r
+except as expressly provided under this License. Any attempt\r
+otherwise to copy, modify, sublicense or distribute the Program is\r
+void, and will automatically terminate your rights under this License.\r
+However, parties who have received copies, or rights, from you under\r
+this License will not have their licenses terminated so long as such\r
+parties remain in full compliance.\r
+\r
+ 5. You are not required to accept this License, since you have not\r
+signed it. However, nothing else grants you permission to modify or\r
+distribute the Program or its derivative works. These actions are\r
+prohibited by law if you do not accept this License. Therefore, by\r
+modifying or distributing the Program (or any work based on the\r
+Program), you indicate your acceptance of this License to do so, and\r
+all its terms and conditions for copying, distributing or modifying\r
+the Program or works based on it.\r
+\r
+ 6. Each time you redistribute the Program (or any work based on the\r
+Program), the recipient automatically receives a license from the\r
+original licensor to copy, distribute or modify the Program subject to\r
+these terms and conditions. You may not impose any further\r
+restrictions on the recipients' exercise of the rights granted herein.\r
+You are not responsible for enforcing compliance by third parties to\r
+this License.\r
+\r
+ 7. If, as a consequence of a court judgment or allegation of patent\r
+infringement or for any other reason (not limited to patent issues),\r
+conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License. If you cannot\r
+distribute so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you\r
+may not distribute the Program at all. For example, if a patent\r
+license would not permit royalty-free redistribution of the Program by\r
+all those who receive copies directly or indirectly through you, then\r
+the only way you could satisfy both it and this License would be to\r
+refrain entirely from distribution of the Program.\r
+\r
+If any portion of this section is held invalid or unenforceable under\r
+any particular circumstance, the balance of the section is intended to\r
+apply and the section as a whole is intended to apply in other\r
+circumstances.\r
+\r
+It is not the purpose of this section to induce you to infringe any\r
+patents or other property right claims or to contest validity of any\r
+such claims; this section has the sole purpose of protecting the\r
+integrity of the free software distribution system, which is\r
+implemented by public license practices. Many people have made\r
+generous contributions to the wide range of software distributed\r
+through that system in reliance on consistent application of that\r
+system; it is up to the author/donor to decide if he or she is willing\r
+to distribute software through any other system and a licensee cannot\r
+impose that choice.\r
+\r
+This section is intended to make thoroughly clear what is believed to\r
+be a consequence of the rest of this License.\r
+\r
+\r
+ 8. If the distribution and/or use of the Program is restricted in\r
+certain countries either by patents or by copyrighted interfaces, the\r
+original copyright holder who places the Program under this License\r
+may add an explicit geographical distribution limitation excluding\r
+those countries, so that distribution is permitted only in or among\r
+countries not thus excluded. In such case, this License incorporates\r
+the limitation as if written in the body of this License.\r
+\r
+ 9. The Free Software Foundation may publish revised and/or new versions\r
+of the General Public License from time to time. Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+Each version is given a distinguishing version number. If the Program\r
+specifies a version number of this License which applies to it and "any\r
+later version", you have the option of following the terms and conditions\r
+either of that version or of any later version published by the Free\r
+Software Foundation. If the Program does not specify a version number of\r
+this License, you may choose any version ever published by the Free Software\r
+Foundation.\r
+\r
+ 10. If you wish to incorporate parts of the Program into other free\r
+programs whose distribution conditions are different, write to the author\r
+to ask for permission. For software which is copyrighted by the Free\r
+Software Foundation, write to the Free Software Foundation; we sometimes\r
+make exceptions for this. Our decision will be guided by the two goals\r
+of preserving the free status of all derivatives of our free software and\r
+of promoting the sharing and reuse of software generally.\r
+\r
+ NO WARRANTY\r
+\r
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\r
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\r
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\r
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
+REPAIR OR CORRECTION.\r
+\r
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGES.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+\r
+ How to Apply These Terms to Your New Programs\r
+\r
+ If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+ To do so, attach the following notices to the program. It is safest\r
+to attach them to the start of each source file to most effectively\r
+convey the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+ <one line to give the program's name and a brief idea of what it does.>\r
+ Copyright (C) <year> <name of author>\r
+\r
+ This program is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation; either version 2 of the License, or\r
+ (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+If the program is interactive, make it output a short notice like this\r
+when it starts in an interactive mode:\r
+\r
+ Gnomovision version 69, Copyright (C) year name of author\r
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+ This is free software, and you are welcome to redistribute it\r
+ under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License. Of course, the commands you use may\r
+be called something other than `show w' and `show c'; they could even be\r
+mouse-clicks or menu items--whatever suits your program.\r
+\r
+You should also get your employer (if you work as a programmer) or your\r
+school, if any, to sign a "copyright disclaimer" for the program, if\r
+necessary. Here is a sample; alter the names:\r
+\r
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
+\r
+ <signature of Ty Coon>, 1 April 1989\r
+ Ty Coon, President of Vice\r
+\r
+This General Public License does not permit incorporating your program into\r
+proprietary programs. If your program is a subroutine library, you may\r
+consider it more useful to permit linking proprietary applications with the\r
+library. If this is what you want to do, use the GNU Library General\r
+Public License instead of this License.\r
--- /dev/null
+File IO Lib Options\r
+-=-=-=-=-=-=-=-=-=-\r
+\r
+See defines in fat_opts.h:\r
+\r
+FATFS_IS_LITTLE_ENDIAN [1/0]\r
+ Which endian is your system? Set to 1 for little endian, 0 for big endian.\r
+\r
+FATFS_MAX_LONG_FILENAME [260]\r
+ By default, 260 characters (max LFN length). Increase this to support greater path depths.\r
+\r
+FATFS_MAX_OPEN_FILES \r
+ The more files you wish to have concurrently open, the greater this number should be.\r
+ This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).\r
+\r
+FAT_BUFFER_SECTORS\r
+ Minimum is 1, more increases performance.\r
+ This defines how many FAT sectors can be buffered per FAT_BUFFER entry.\r
+\r
+FAT_BUFFERS\r
+ Minimum is 1, more increases performance.\r
+ This defines how many FAT buffer entries are available.\r
+ Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE\r
+\r
+FATFS_INC_WRITE_SUPPORT\r
+ Support file write functionality.\r
+\r
+FAT_SECTOR_SIZE\r
+ Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).\r
+\r
+FAT_PRINTF\r
+ A define that allows the File IO library to print to console/stdout. \r
+ Provide your own printf function if printf not available.\r
+\r
+FAT_CLUSTER_CACHE_ENTRIES\r
+ Size of cluster chain cache (can be undefined if not required).\r
+ Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2\r
+ Improves access speed considerably.\r
+\r
+FATFS_INC_LFN_SUPPORT [1/0]\r
+ Enable/Disable support for long filenames.\r
+\r
+FATFS_DIR_LIST_SUPPORT [1/0]\r
+ Include support for directory listing.\r
+\r
+FATFS_INC_TIME_DATE_SUPPORT [1/0]\r
+ Use time/date functions provided by time.h to update creation & modification timestamps.\r
+\r
+FATFS_INC_FORMAT_SUPPORT\r
+ Include support for formatting disks (FAT16 only).\r
+\r
+FAT_PRINTF_NOINC_STDIO\r
+ Disable use of printf & inclusion of stdio.h\r
--- /dev/null
+Revision History\r
+-=-=-=-=-=-=-=-=-\r
+v2.6.11 - Fix compilation with GCC on 64-bit machines\r
+v2.6.10 - Added support for FAT32 format.\r
+V2.6.9 - Added support for time & date handling.\r
+V2.6.8 - Fixed error with FSINFO sector write.\r
+V2.6.7 - Added fgets().\r
+ Fixed C warnings, removed dependancy on some string.h functions.\r
+V2.6.6 \96 Massive read + write performance improvements.\r
+V2.6.5 \96 Bug fixes for big endian systems.\r
+V2.6.4 \96 Further bug fixes and performance improvements for write operations.\r
+V2.6.3 \96 Peformance improvements, FAT16 formatting support. Various bug fixes.\r
+V2.6 - Basic support for FAT16 added (18-04-10).\r
+V2.5 - Code cleaned up. Many bugs fixed. Thread safety functions added.\r
+V2.x - Write support added as well as better stdio like API.\r
+V1.0 - Rewrite of all code to enable multiple files to be opened and provides a \r
+ better file API.\r
+ Also better string matching, and generally better C code than origonal \r
+ version.\r
+V0.1c - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping \r
+ the drive reads from addressing a sector that is out of range.\r
+V0.1b - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 \r
+ access.\r
+V0.1a - First release (27/12/03); fopen(), fgetc() unbuffered reads.\r
--- /dev/null
+FAT File IO Library License\r
+-=-=-=-=-=-=-=-=-=-=-=-=-=-\r
+\r
+This versions license: GPL\r
+\r
+If you include GPL software in your project, you must release the source code of that project too.\r
+\r
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.\r
+\r
+Email: admin@ultra-embedded.com\r
--- /dev/null
+Media Access API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+ Sector: 32-bit sector number\r
+ Buffer: Target buffer to read n sectors of data into.\r
+ Sector_count: Number of sectors to read.\r
+\r
+Return: \r
+ int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+ Application/target specific disk/media read function.\r
+ Sector number (sectors are usually 512 byte pages) to read.\r
+\r
+Media Write API\r
+\r
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+ Sector: 32-bit sector number\r
+ Buffer: Target buffer to write n sectors of data from.\r
+ Sector_count: Number of sectors to write.\r
+\r
+Return: \r
+ int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+ Application/target specific disk/media write function.\r
+ Sector number (sectors are usually 512 byte pages) to write to.\r
+\r
+File IO Library Linkage\r
+ Use the following API to attach the media IO functions to the File IO library.\r
+\r
+ int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+\r
+\r
--- /dev/null
+#include <stdio.h>\r
+#include "fat_filelib.h"\r
+\r
+int media_init()\r
+{\r
+ // ...\r
+ return 1;\r
+}\r
+\r
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+ unsigned long i;\r
+\r
+ for (i=0;i<sector_count;i++)\r
+ {\r
+ // ...\r
+ // Add platform specific sector (512 bytes) read code here\r
+ //..\r
+\r
+ sector ++;\r
+ buffer += 512;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+ unsigned long i;\r
+\r
+ for (i=0;i<sector_count;i++)\r
+ {\r
+ // ...\r
+ // Add platform specific sector (512 bytes) write code here\r
+ //..\r
+\r
+ sector ++;\r
+ buffer += 512;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+void main()\r
+{\r
+ FL_FILE *file;\r
+\r
+ // Initialise media\r
+ media_init();\r
+\r
+ // Initialise File IO Library\r
+ fl_init();\r
+\r
+ // Attach media access functions to library\r
+ if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)\r
+ {\r
+ printf("ERROR: Media attach failed\n");\r
+ return; \r
+ }\r
+\r
+ // List root directory\r
+ fl_listdirectory("/");\r
+\r
+ // Create File\r
+ file = fl_fopen("/file.bin", "w");\r
+ if (file)\r
+ {\r
+ // Write some data\r
+ unsigned char data[] = { 1, 2, 3, 4 };\r
+ if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))\r
+ printf("ERROR: Write file failed\n");\r
+ }\r
+ else\r
+ printf("ERROR: Create file failed\n");\r
+\r
+ // Close file\r
+ fl_fclose(file);\r
+\r
+ // Delete File\r
+ if (fl_remove("/file.bin") < 0)\r
+ printf("ERROR: Delete file failed\n");\r
+\r
+ // List root directory\r
+ fl_listdirectory("/");\r
+\r
+ fl_shutdown();\r
+}\r
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+ uint8 num_of_fats;
+ uint16 reserved_sectors;
+ uint32 FATSz;
+ uint32 root_dir_sectors;
+ uint32 total_sectors;
+ uint32 data_sectors;
+ uint32 count_of_clusters;
+ uint8 valid_partition = 0;
+
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have a read function (write function is optional)
+ if (!fs->disk_io.read_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // MBR: Sector 0 on the disk
+ // NOTE: Some removeable media does not have this.
+
+ // Load MBR (LBA 0) into the 512 byte buffer
+ if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make Sure 0x55 and 0xAA are at end of sector
+ // (this should be the case regardless of the MBR or boot sector)
+ if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Now check again using the access function to prove endian conversion function
+ if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+ return FAT_INIT_ENDIAN_ERROR;
+
+ // Verify packed structures
+ if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+ return FAT_INIT_STRUCT_PACKING;
+
+ // Check the partition type code
+ switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+ {
+ case 0x0B:
+ case 0x06:
+ case 0x0C:
+ case 0x0E:
+ case 0x0F:
+ case 0x05:
+ valid_partition = 1;
+ break;
+ case 0x00:
+ valid_partition = 0;
+ break;
+ default:
+ if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+ valid_partition = 1;
+ break;
+ }
+
+ // Read LBA Begin for the file system
+ if (valid_partition)
+ fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+ // Else possibly MBR less disk
+ else
+ fs->lba_begin = 0;\r
+\r
+ // Load Volume 1 table into sector buffer
+ // (We may already have this in the buffer if MBR less drive!)
+ if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make sure there are 512 bytes per cluster
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+ return FAT_INIT_INVALID_SECTOR_SIZE;\r
+
+ // Load Parameters of FAT partition
+ fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+ reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+ num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+ fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ // For FAT32 (which this may be)
+ fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+ fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Calculate the root dir sectors
+ root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+ total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+ else
+ total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+ data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+ // Find out which version of FAT this is...
+ if (fs->sectors_per_cluster != 0)
+ {
+ count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+ if(count_of_clusters < 4085)
+ // Volume is FAT12
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+ else if(count_of_clusters < 65525)
+ {
+ // Clear this FAT32 specific param
+ fs->rootdir_first_cluster = 0;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+ return FAT_INIT_OK;
+ }
+ else
+ {
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+ return FAT_INIT_OK;
+ }
+ }
+ else
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+ else
+ return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+ uint32 sector_to_read = 0;
+ uint32 cluster_to_read = 0;
+ uint32 cluster_chain = 0;
+ uint32 i;
+ uint32 lba;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+ {
+ if (offset < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+ else
+ return 0;
+ }
+ // FAT16/32 Other
+ else
+ {
+ // Set start of cluster chain to initial value
+ cluster_chain = start_cluster;
+
+ // Find parameters
+ cluster_to_read = offset / fs->sectors_per_cluster;
+ sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+ // Follow chain to find cluster to read
+ for (i=0; i<cluster_to_read; i++)
+ cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+ // If end of cluster chain then return false
+ if (cluster_chain == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Calculate sector address
+ lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+ }
+
+ // User provided target array
+ if (target)
+ return fs->disk_io.read_media(lba, target, 1);
+ // Else read sector if not already loaded
+ else if (lba != fs->currentsector.address)
+ {
+ fs->currentsector.address = lba;
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16, there are a limited amount of sectors in root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Read from disk
+ return fs->disk_io.read_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate read address
+ fs->currentsector.address = lba;
+
+ // Read from disk
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate read address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16 we cannot extend the root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Write to disk
+ return fs->disk_io.write_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = lba;
+
+ // Write to disk
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate write address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+ FAT_PRINTF(("FAT details:\r\n"));
+ FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+ FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+ FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+ FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+ FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+ // NOTE: On FAT16 this will be 0 which has a special meaning...
+ return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ struct fat_dir_entry *directoryEntry;
+
+ fatfs_lfn_cache_init(&lfn, 1);
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ long_filename = fatfs_lfn_cache_get(&lfn);
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(long_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(short_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ return 1;
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+ time_t time_now;
+ struct tm * time_info;
+ uint16 fat_time;
+ uint16 fat_date;
+
+ // Get system time
+ time(&time_now);
+
+ // Convert to local time
+ time_info = localtime(&time_now);
+
+ // Convert time to FAT format
+ fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+ // Convert date to FAT format
+ fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+ // Update requested fields
+ if (create)
+ {
+ directoryEntry->CrtTime[1] = fat_time >> 8;
+ directoryEntry->CrtTime[0] = fat_time >> 0;
+ directoryEntry->CrtDate[1] = fat_date >> 8;
+ directoryEntry->CrtDate[0] = fat_date >> 0;
+ }
+
+ if (modify)
+ {
+ directoryEntry->WrtTime[1] = fat_time >> 8;
+ directoryEntry->WrtTime[0] = fat_time >> 0;
+ directoryEntry->WrtDate[1] = fat_date >> 8;
+ directoryEntry->WrtDate[0] = fat_date >> 0;
+ }
+
+ if (access)
+ {
+ directoryEntry->LstAccDate[1] = fat_time >> 8;
+ directoryEntry->LstAccDate[0] = fat_time >> 0;
+ directoryEntry->LstAccDate[1] = fat_date >> 8;
+ directoryEntry->LstAccDate[0] = fat_date >> 0;
+ }
+
+ return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ {
+ directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+ {
+ // Mark as deleted
+ directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+ dirls->cluster = StartCluster;
+ dirls->sector = 0;
+ dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+ uint8 i,item;
+ uint16 recordoffset;
+ struct fat_dir_entry *directoryEntry;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ int result = 0;
+
+ // Initialise LFN cache first
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ while (1)
+ {
+ // If data read OK
+ if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+ {
+ // Maximum of 16 directory entries
+ for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Increase directory offset
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if ( fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ // Get text
+ long_filename = fatfs_lfn_cache_get(&lfn);
+ strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME-1);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if ( fatfs_entry_sfn_only(directoryEntry) )
+ {
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ }// end of for
+
+ // If reached end of the dir move onto next sector
+ dirls->sector++;
+ dirls->offset = 0;
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+#endif
--- /dev/null
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK 0
+#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
+#define FAT_INIT_INVALID_SIGNATURE (-3)
+#define FAT_INIT_ENDIAN_ERROR (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
+#define FAT_INIT_STRUCT_PACKING (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+ // User supplied function pointers for disk IO
+ fn_diskio_read read_media;
+ fn_diskio_write write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+ uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+ uint32 address;
+ int dirty;
+ uint8 * ptr;
+
+ // Next in chain of sector buffers
+ struct fat_buffer *next;
+};
+
+typedef enum eFatType
+{
+ FAT_TYPE_16,
+ FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+ // Filesystem globals
+ uint8 sectors_per_cluster;
+ uint32 cluster_begin_lba;
+ uint32 rootdir_first_cluster;
+ uint32 rootdir_first_sector;
+ uint32 rootdir_sectors;
+ uint32 fat_begin_lba;
+ uint16 fs_info_sector;
+ uint32 lba_begin;
+ uint32 fat_sectors;
+ uint32 next_free_cluster;
+ uint16 root_entry_count;
+ uint16 reserved_sectors;
+ uint8 num_of_fats;
+ tFatType fat_type;
+
+ // Disk/Media API
+ struct disk_if disk_io;
+
+ // [Optional] Thread Safety
+ void (*fl_lock)(void);
+ void (*fl_unlock)(void);
+
+ // Working buffer
+ struct fat_buffer currentsector;
+
+ // FAT Buffer
+ struct fat_buffer *fat_buffer_head;
+ struct fat_buffer fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+ uint32 sector;
+ uint32 cluster;
+ uint8 offset;
+};
+
+struct fs_dir_ent
+{
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 is_dir;
+ uint32 cluster;
+ uint32 size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ uint16 access_date;
+ uint16 write_time;
+ uint16 write_date;
+ uint16 create_date;
+ uint16 create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs);
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void fatfs_show_details(struct fatfs *fs);
+uint32 fatfs_get_root_cluster(struct fatfs *fs);
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ int i;
+
+ for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+ {
+ file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+ file->cluster_cache_data[i] = 0;
+ }
+#endif
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ {
+ *pNextCluster = file->cluster_cache_data[slot];
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ file->cluster_cache_data[slot] = nextCluster;
+ else
+ {
+ file->cluster_cache_idx[slot] = clusterIdx;
+ file->cluster_cache_data[slot] = nextCluster;
+ }
+#endif
+
+ return 1;
+}
--- /dev/null
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
--- /dev/null
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+// FAT32 Offsets
+// Name Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT 0 // Length = 3
+#define BS_OEMNAME 3 // Length = 8
+#define BPB_BYTSPERSEC 11 // Length = 2
+#define BPB_SECPERCLUS 13 // Length = 1
+#define BPB_RSVDSECCNT 14 // Length = 2
+#define BPB_NUMFATS 16 // Length = 1
+#define BPB_ROOTENTCNT 17 // Length = 2
+#define BPB_TOTSEC16 19 // Length = 2
+#define BPB_MEDIA 21 // Length = 1
+#define BPB_FATSZ16 22 // Length = 2
+#define BPB_SECPERTRK 24 // Length = 2
+#define BPB_NUMHEADS 26 // Length = 2
+#define BPB_HIDDSEC 28 // Length = 4
+#define BPB_TOTSEC32 32 // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM 36 // Length = 1
+#define BS_FAT_BOOTSIG 38 // Length = 1
+#define BS_FAT_VOLID 39 // Length = 4
+#define BS_FAT_VOLLAB 43 // Length = 11
+#define BS_FAT_FILSYSTYPE 54 // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32 36 // Length = 4
+#define BPB_FAT32_EXTFLAGS 40 // Length = 2
+#define BPB_FAT32_FSVER 42 // Length = 2
+#define BPB_FAT32_ROOTCLUS 44 // Length = 4
+#define BPB_FAT32_FSINFO 48 // Length = 2
+#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
+#define BS_FAT32_DRVNUM 64 // Length = 1
+#define BS_FAT32_BOOTSIG 66 // Length = 1
+#define BS_FAT32_VOLID 67 // Length = 4
+#define BS_FAT32_VOLLAB 71 // Length = 11
+#define BS_FAT32_FILSYSTYPE 82 // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12 1
+#define FAT_TYPE_FAT16 2
+#define FAT_TYPE_FAT32 3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION 510
+#define SIGNATURE_VALUE 0xAA55
+#define PARTITION1_TYPECODE_LOCATION 450
+#define FAT32_TYPECODE1 0x0B
+#define FAT32_TYPECODE2 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION 454
+#define PARTITION1_SIZE_LOCATION 458
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define FAT_SFN_SIZE_FULL 11
+#define FAT_SFN_SIZE_PARTIAL 8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY 0x01
+#define FILE_ATTR_HIDDEN 0x02
+#define FILE_ATTR_SYSTEM 0x04
+#define FILE_ATTR_SYSHID 0x06
+#define FILE_ATTR_VOLUME_ID 0x08
+#define FILE_ATTR_DIRECTORY 0x10
+#define FILE_ATTR_ARCHIVE 0x20
+#define FILE_ATTR_LFN_TEXT 0x0F
+#define FILE_HEADER_BLANK 0x00
+#define FILE_HEADER_DELETED 0xE5
+#define FILE_TYPE_DIR 0x10
+#define FILE_TYPE_FILE 0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT 11
+#define FAT_TIME_HOURS_MASK 0x1F
+#define FAT_TIME_MINUTES_SHIFT 5
+#define FAT_TIME_MINUTES_MASK 0x3F
+#define FAT_TIME_SECONDS_SHIFT 0
+#define FAT_TIME_SECONDS_MASK 0x1F
+#define FAT_TIME_SECONDS_SCALE 2
+#define FAT_DATE_YEAR_SHIFT 9
+#define FAT_DATE_YEAR_MASK 0x7F
+#define FAT_DATE_MONTH_SHIFT 5
+#define FAT_DATE_MONTH_MASK 0xF
+#define FAT_DATE_DAY_SHIFT 0
+#define FAT_DATE_DAY_MASK 0x1F
+#define FAT_DATE_YEAR_OFFSET 1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER 0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+ uint8 Name[11];
+ uint8 Attr;
+ uint8 NTRes;
+ uint8 CrtTimeTenth;
+ uint8 CrtTime[2];
+ uint8 CrtDate[2];
+ uint8 LstAccDate[2];
+ uint16 FstClusHI;
+ uint8 WrtTime[2];
+ uint8 WrtDate[2];
+ uint16 FstClusLO;
+ uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE _files[FATFS_MAX_OPEN_FILES];
+static int _filelib_init = 0;
+static int _filelib_valid = 0;
+static struct fatfs _fs;
+static struct fat_list _open_file_list;
+static struct fat_list _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+ // Allocate free file
+ struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+ // Add to open list
+ if (node)
+ fat_list_insert_last(&_open_file_list, node);
+
+ return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+ struct fat_node *node;
+
+ // Compare open files
+ fat_list_for_each(&_open_file_list, node)
+ {
+ FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+ // If not the current file
+ if (openFile != file)
+ {
+ // Compare path and name
+ if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+ return 1;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+ // Remove from open list
+ fat_list_remove(&_open_file_list, &file->list_node);
+
+ // Add to free list
+ fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+// Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+ int levels;
+ int sublevel;
+ char currentfolder[FATFS_MAX_LONG_FILENAME];
+ struct fat_dir_entry sfEntry;
+ uint32 startcluster;
+
+ // Set starting cluster to root cluster
+ startcluster = fatfs_get_root_cluster(&_fs);
+
+ // Find number of levels
+ levels = fatfs_total_path_levels(path);
+
+ // Cycle through each level and get the start sector
+ for (sublevel=0;sublevel<(levels+1);sublevel++)
+ {
+ if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+ return 0;
+
+ // Find clusteraddress for folder (currentfolder)
+ if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+ {
+ // Check entry is folder
+ if (fatfs_entry_is_dir(&sfEntry))
+ startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ *pathCluster = startcluster;
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+ int i;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return 0;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return 0;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the folder (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // Erase new directory cluster
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;i<_fs.sectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+ {
+ _free_file(file);
+ return 0;
+ }
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_fat_purge(&_fs);
+
+ _free_file(file);
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0]==0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return NULL;
+ }
+ }
+
+ // Using dir cluster address search for filename
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+ // Make sure entry is file not dir!
+ if (fatfs_entry_is_file(&sfEntry))
+ {
+ // Initialise file details
+ memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+ file->filelength = FAT_HTONL(sfEntry.FileSize);
+ file->bytenum = 0;
+ file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&_fs, file);
+
+ fatfs_fat_purge(&_fs);
+
+ return file;
+ }
+
+ _free_file(file);
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+
+ // No write access?
+ if (!_fs.disk_io.write_media)
+ return NULL;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return NULL;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the file (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&_fs, file);
+
+ fatfs_fat_purge(&_fs);
+
+ return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+ uint32 Sector = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 i;
+ uint32 lba;
+
+ // Find cluster index within file & sector with cluster
+ ClusterIdx = offset / _fs.sectors_per_cluster;
+ Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+ // Limit number of sectors read to the number remaining in this cluster
+ if ((Sector + count) > _fs.sectors_per_cluster)
+ count = _fs.sectors_per_cluster - Sector;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i<ClusterIdx; i++)
+ {
+ uint32 nextCluster;
+
+ // Does the entry exist in the cache?
+ if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+ }
+
+ Cluster = nextCluster;
+ }
+
+ // Record current cluster lookup details (if valid)
+ if (Cluster != FAT32_LAST_CLUSTER)
+ {
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+ }
+
+ // If end of cluster chain then return false
+ if (Cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Calculate sector address
+ lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+ // Read sector of file
+ if (fatfs_sector_read(&_fs, lba, buffer, count))
+ return count;
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+ int i;
+
+ fat_list_init(&_free_file_list);
+ fat_list_init(&_open_file_list);
+
+ // Add all file objects to free list
+ for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+ fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+ _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+ _fs.fl_lock = lock;
+ _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ _fs.disk_io.read_media = rd;
+ _fs.disk_io.write_media = wr;
+
+ // Initialise FAT parameters
+ if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+ {
+ FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+ return res;
+ }
+
+ _filelib_valid = 1;
+ return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+ fatfs_fat_purge(&_fs);
+ FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+ int i;
+ FL_FILE* file;
+ uint8 flags = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!_filelib_valid)
+ return NULL;
+
+ if (!path || !mode)
+ return NULL;
+
+ // Supported Modes:
+ // "r" Open a file for reading.
+ // The file must exist.
+ // "w" Create an empty file for writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a" Append to a file.
+ // Writing operations append data at the end of the file.
+ // The file is created if it does not exist.
+ // "r+" Open a file for update both reading and writing.
+ // The file must exist.
+ // "w+" Create an empty file for both reading and writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a+" Open a file for reading and appending.
+ // All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+ // You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+ // will move it back to the end of file.
+ // The file is created if it does not exist.
+
+ for (i=0;i<(int)strlen(mode);i++)
+ {
+ switch (mode[i])
+ {
+ case 'r':
+ case 'R':
+ flags |= FILE_READ;
+ break;
+ case 'w':
+ case 'W':
+ flags |= FILE_WRITE;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ break;
+ case 'a':
+ case 'A':
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ break;
+ case '+':
+ if (flags & FILE_READ)
+ flags |= FILE_WRITE;
+ else if (flags & FILE_WRITE)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ }
+ else if (flags & FILE_APPEND)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ }
+ break;
+ case 'b':
+ case 'B':
+ flags |= FILE_BINARY;
+ break;
+ }
+ }
+
+ file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+ // No write support!
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+ // No write access - remove write/modify flags
+ if (!_fs.disk_io.write_media)
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+ FL_LOCK(&_fs);
+
+ // Read
+ if (flags & FILE_READ)
+ file = _open_file(path);
+
+ // Create New
+#if FATFS_INC_WRITE_SUPPORT
+ if (!file && (flags & FILE_CREATE))
+ file = _create_file(path);
+#endif
+
+ // Write Existing (and not open due to read or create)
+ if (!(flags & FILE_READ))
+ if ((flags & FILE_CREATE) && !file)
+ if (flags & (FILE_WRITE | FILE_APPEND))
+ file = _open_file(path);
+
+ if (file)
+ file->flags = flags;
+
+ FL_UNLOCK(&_fs);
+ return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+ uint32 SectorNumber = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 LastCluster = FAT32_LAST_CLUSTER;
+ uint32 i;
+ uint32 lba;
+ uint32 TotalWriteCount = count;
+
+ // Find values for Cluster index & sector within cluster
+ ClusterIdx = offset / _fs.sectors_per_cluster;
+ SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+ // Limit number of sectors written to the number remaining in this cluster
+ if ((SectorNumber + count) > _fs.sectors_per_cluster)
+ count = _fs.sectors_per_cluster - SectorNumber;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i<ClusterIdx; i++)
+ {
+ uint32 nextCluster;
+
+ // Does the entry exist in the cache?
+ if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+ }
+
+ LastCluster = Cluster;
+ Cluster = nextCluster;
+
+ // Dont keep following a dead end
+ if (Cluster == FAT32_LAST_CLUSTER)
+ break;
+ }
+
+ // If we have reached the end of the chain, allocate more!
+ if (Cluster == FAT32_LAST_CLUSTER)
+ {
+ // Add some more cluster(s) to the last good cluster chain
+ if (!fatfs_add_free_space(&_fs, &LastCluster, (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+ return 0;
+
+ Cluster = LastCluster;
+ }
+
+ // Record current cluster lookup details
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+
+ // Calculate write address
+ lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+ if (fatfs_sector_write(&_fs, lba, buf, count))
+ return count;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (file)
+ {
+ FL_LOCK(&_fs);
+
+ // If some write data still in buffer
+ if (file->file_data_dirty)
+ {
+ // Write back current sector before loading next
+ if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+ file->file_data_dirty = 0;
+ }
+
+ FL_UNLOCK(&_fs);
+ }
+#endif
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (file)
+ {
+ FL_LOCK(&_fs);
+
+ // Flush un-written data to file
+ fl_fflush(f);
+
+ // File size changed?
+ if (file->filelength_changed)
+ {
+#if FATFS_INC_WRITE_SUPPORT
+ // Update filesize in directory
+ fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+ file->filelength_changed = 0;
+ }
+
+ file->bytenum = 0;
+ file->filelength = 0;
+ file->startcluster = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Free file handle
+ _free_file(file);
+
+ fatfs_fat_purge(&_fs);
+
+ FL_UNLOCK(&_fs);
+ }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+ int res;
+ uint8 data = 0;
+
+ res = fl_fread(&data, 1, 1, f);
+ if (res == 1)
+ return (int)data;
+ else
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+ int idx = 0;
+
+ // Space for null terminator?
+ if (n > 0)
+ {
+ // While space (+space for null terminator)
+ while (idx < (n-1))
+ {
+ int ch = fl_fgetc(f);
+
+ // EOF / Error?
+ if (ch < 0)
+ break;
+
+ // Store character read from stream
+ s[idx++] = (char)ch;
+
+ // End of line?
+ if (ch == '\n')
+ break;
+ }
+
+ if (idx > 0)
+ s[idx] = '\0';
+ }
+
+ return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+ uint32 sector;
+ uint32 offset;
+ int copyCount;
+ int count = size * length;
+ int bytesRead = 0;
+
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (buffer==NULL || file==NULL)
+ return -1;
+
+ // No read permissions
+ if (!(file->flags & FILE_READ))
+ return -1;
+
+ // Nothing to be done
+ if (!count)
+ return 0;
+
+ // Check if read starts past end of file
+ if (file->bytenum >= file->filelength)
+ return -1;
+
+ // Limit to file size
+ if ( (file->bytenum + count) > file->filelength )
+ count = file->filelength - file->bytenum;
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesRead < count)
+ {
+ // Read whole sector, read from media directly into target buffer
+ if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+ {
+ // Read as many sectors as possible into target buffer
+ uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+ if (sectorsRead)
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsRead;
+ offset = 0;
+ }
+ else
+ break;
+ }
+ else
+ {
+ // Do we need to re-read the sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(file, sector, file->file_data_sector, 1))
+ // Read failed - out of range (probably)
+ break;
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (count - bytesRead))
+ copyCount = (count - bytesRead);
+
+ // Copy to application buffer
+ memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+
+ // Increase total read count
+ bytesRead += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+ }
+
+ return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res = -1;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!file)
+ return -1;
+
+ if (origin == SEEK_END && offset != 0)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // Invalidate file buffer
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+
+ if (origin == SEEK_SET)
+ {
+ file->bytenum = (uint32)offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+
+ res = 0;
+ }
+ else if (origin == SEEK_CUR)
+ {
+ // Positive shift
+ if (offset >= 0)
+ {
+ file->bytenum += offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+ }
+ // Negative shift
+ else
+ {
+ // Make shift positive
+ offset = -offset;
+
+ // Limit to negative shift to start of file
+ if ((uint32)offset > file->bytenum)
+ file->bytenum = 0;
+ else
+ file->bytenum-= offset;
+ }
+
+ res = 0;
+ }
+ else if (origin == SEEK_END)
+ {
+ file->bytenum = file->filelength;
+ res = 0;
+ }
+ else
+ res = -1;
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // Get position
+ *position = file->bytenum;
+
+ FL_UNLOCK(&_fs);
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+ uint32 pos = 0;
+
+ fl_fgetpos(f, &pos);
+
+ return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ if (file->bytenum == file->filelength)
+ res = EOF;
+ else
+ res = 0;
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+ uint8 data = (uint8)c;
+ int res;
+
+ res = fl_fwrite(&data, 1, 1, f);
+ if (res == 1)
+ return c;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ uint32 sector;
+ uint32 offset;
+ uint32 length = (size*count);
+ uint8 *buffer = (uint8 *)data;
+ uint32 bytesWritten = 0;
+ uint32 copyCount;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // No write permissions
+ if (!(file->flags & FILE_WRITE))
+ {
+ FL_UNLOCK(&_fs);
+ return -1;
+ }
+
+ // Append writes to end of file
+ if (file->flags & FILE_APPEND)
+ file->bytenum = file->filelength;
+ // Else write to current position
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesWritten < length)
+ {
+ // Whole sector or more to be written?
+ if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+ {
+ uint32 sectorsWrote;
+
+ // Buffered sector, flush back to disk
+ if (file->file_data_address != 0xFFFFFFFF)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ }
+
+ // Write as many sectors as possible
+ sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+ copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsWrote;
+ offset = 0;
+
+ if (!sectorsWrote)
+ break;
+ }
+ else
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (length - bytesWritten))
+ copyCount = (length - bytesWritten);
+
+ // Do we need to read a new sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ // If we plan to overwrite the whole sector, we don't need to read it first!
+ if (copyCount != FAT_SECTOR_SIZE)
+ {
+ // NOTE: This does not have succeed; if last sector of file
+ // reached, no valid data will be read in, but write will
+ // allocate some more space for new data.
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(file, sector, file->file_data_sector, 1))
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ }
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // Copy from application buffer into sector buffer
+ memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+ // Mark buffer as dirty
+ file->file_data_dirty = 1;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+ }
+
+ // Write increased extent of the file?
+ if (file->bytenum > file->filelength)
+ {
+ // Increase file size to new point
+ file->filelength = file->bytenum;
+
+ // We are changing the file length and this
+ // will need to be writen back at some point
+ file->filelength_changed = 1;
+ }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // If time & date support is enabled, always force directory entry to be
+ // written in-order to update file modify / access time & date.
+ file->filelength_changed = 1;
+#endif
+
+ FL_UNLOCK(&_fs);
+
+ return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+ int len = (int)strlen(str);
+ int res = fl_fwrite(str, 1, len, f);
+
+ if (res == len)
+ return len;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+ FL_FILE* file;
+ int res = -1;
+
+ FL_LOCK(&_fs);
+
+ // Use read_file as this will check if the file is already open!
+ file = fl_fopen((char*)filename, "r");
+ if (file)
+ {
+ // Delete allocated space
+ if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+ {
+ // Remove directory entries
+ if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Close the file handle (this should not write anything to the file
+ // as we have not changed the file since opening it!)
+ fl_fclose(file);
+
+ res = 0;
+ }
+ }
+ }
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+ res =_create_directory((char*)path);
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+ FL_DIR dirstat;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+ if (fl_opendir(path, &dirstat))
+ {
+ struct fs_dir_ent dirent;
+
+ while (fl_readdir(&dirstat, &dirent) == 0)
+ {
+#if FATFS_INC_TIME_DATE_SUPPORT
+ int d,m,y,h,mn,s;
+ fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+ fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+ FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
+#endif
+
+ if (dirent.is_dir)
+ {
+ FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+ }
+ else
+ {
+ FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+ }
+ }
+
+ fl_closedir(&dirstat);
+ }
+
+ FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+ int levels;
+ int res = 1;
+ uint32 cluster = FAT32_INVALID_CLUSTER;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ levels = fatfs_total_path_levels((char*)path) + 1;
+
+ // If path is in the root dir
+ if (levels == 0)
+ cluster = fatfs_get_root_cluster(&_fs);
+ // Find parent directory start cluster
+ else
+ res = _open_directory((char*)path, &cluster);
+
+ if (res)
+ fatfs_list_directory_start(&_fs, dir, cluster);
+
+ FL_UNLOCK(&_fs);
+
+ return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+ int res = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+ FL_UNLOCK(&_fs);
+
+ return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+ // Not used
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+ int res = 0;
+ FL_DIR dir;
+
+ if (fl_opendir(path, &dir))
+ {
+ res = 1;
+ fl_closedir(&dir);
+ }
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+ return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+ return &_fs;
+}
+#endif
--- /dev/null
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+ #define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+ #define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+ #define SEEK_SET 0
+#endif
+
+#ifndef EOF
+ #define EOF (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+ uint32 ClusterIdx;
+ uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+ uint32 parentcluster;
+ uint32 startcluster;
+ uint32 bytenum;
+ uint32 filelength;
+ int filelength_changed;
+ char path[FATFS_MAX_LONG_FILENAME];
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+ uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+ // Cluster Lookup
+ struct cluster_lookup last_fat_lookup;
+
+ // Read/Write sector buffer
+ uint8 file_data_sector[FAT_SECTOR_SIZE];
+ uint32 file_data_address;
+ int file_data_dirty;
+
+ // File fopen flags
+ uint8 flags;
+#define FILE_READ (1 << 0)
+#define FILE_WRITE (1 << 1)
+#define FILE_APPEND (1 << 2)
+#define FILE_BINARY (1 << 3)
+#define FILE_ERASE (1 << 4)
+#define FILE_CREATE (1 << 5)
+
+ struct fat_node list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void fl_init(void);
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void fl_shutdown(void);
+
+// Standard API
+void* fl_fopen(const char *path, const char *modifiers);
+void fl_fclose(void *file);
+int fl_fflush(void *file);
+int fl_fgetc(void *file);
+char * fl_fgets(char *s, int n, void *f);
+int fl_fputc(int c, void *file);
+int fl_fputs(const char * str, void *file);
+int fl_fwrite(const void * data, int size, int count, void *file );
+int fl_fread(void * data, int size, int count, void *file );
+int fl_fseek(void *file , long offset , int origin );
+int fl_fgetpos(void *file , uint32 * position);
+long fl_ftell(void *f);
+int fl_feof(void *f);
+int fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status FL_DIR;
+typedef struct fs_dir_ent fl_dirent;
+
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir);
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int fl_closedir(FL_DIR* dir);
+
+// Extensions
+void fl_listdirectory(const char *path);
+int fl_createdirectory(const char *path);
+int fl_is_dir(const char *path);
+
+int fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE FL_FILE
+
+#define fopen(a,b) fl_fopen(a, b)
+#define fclose(a) fl_fclose(a)
+#define fflush(a) fl_fflush(a)
+#define fgetc(a) fl_fgetc(a)
+#define fgets(a,b,c) fl_fgets(a, b, c)
+#define fputc(a,b) fl_fputc(a, b)
+#define fputs(a,b) fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d) fl_fread(a, b, c, d)
+#define fseek(a,b,c) fl_fseek(a, b, c)
+#define fgetpos(a,b) fl_fgetpos(a, b)
+#define ftell(a) fl_ftell(a)
+#define feof(a) fl_feof(a)
+#define remove(a) fl_remove(a)
+#define mkdir(a) fl_createdirectory(a)
+#define rmdir(a) 0
+
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+ uint32 sectors;
+ uint8 sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+ { 32680, 2}, // 16MB - 1K
+ { 262144, 4}, // 128MB - 2K
+ { 524288, 8}, // 256MB - 4K
+ { 1048576, 16}, // 512MB - 8K
+ { 2097152, 32}, // 1GB - 16K
+ { 4194304, 64}, // 2GB - 32K
+ { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+ { 0 , 0 } // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+ { 532480, 1}, // 260MB - 512b
+ { 16777216, 8}, // 8GB - 4K
+ { 33554432, 16}, // 16GB - 8K
+ { 67108864, 32}, // 32GB - 16K
+ { 0xFFFFFFFF, 64},// >32GB - 32K
+ { 0 , 0 } // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+ int i;
+
+ if (!is_fat32)
+ {
+ for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table16[i].sectors)
+ return _cluster_size_table16[i].sectors_per_cluster;
+ }
+ else
+ {
+ for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table32[i].sectors)
+ return _cluster_size_table32[i].sectors_per_cluster;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+ int i;
+
+ // Zero sector first
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ for (i=0;i<count;i++)
+ if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+ uint32 total_clusters;
+ int i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // OEM Name & Jump Code
+ fs->currentsector.sector[0] = 0xEB;
+ fs->currentsector.sector[1] = 0x3C;
+ fs->currentsector.sector[2] = 0x90;
+ fs->currentsector.sector[3] = 0x4D;
+ fs->currentsector.sector[4] = 0x53;
+ fs->currentsector.sector[5] = 0x44;
+ fs->currentsector.sector[6] = 0x4F;
+ fs->currentsector.sector[7] = 0x53;
+ fs->currentsector.sector[8] = 0x35;
+ fs->currentsector.sector[9] = 0x2E;
+ fs->currentsector.sector[10] = 0x30;
+
+ // Bytes per sector
+ fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+ fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+ // Get sectors per cluster size for the disk
+ fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+ if (!fs->sectors_per_cluster)
+ return 0; // Invalid disk size
+
+ // Sectors per cluster
+ fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+ // Reserved Sectors
+ if (!is_fat32)
+ fs->reserved_sectors = 8;
+ else
+ fs->reserved_sectors = 32;
+ fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+ fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+ // Number of FATS
+ fs->num_of_fats = 2;
+ fs->currentsector.sector[16] = fs->num_of_fats;
+
+ // Max entries in root dir (FAT16 only)
+ if (!is_fat32)
+ {
+ fs->root_entry_count = 512;
+ fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+ fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+ }
+ else
+ {
+ fs->root_entry_count = 0;
+ fs->currentsector.sector[17] = 0;
+ fs->currentsector.sector[18] = 0;
+ }
+
+ // [FAT16] Total sectors (use FAT32 count instead)
+ fs->currentsector.sector[19] = 0x00;
+ fs->currentsector.sector[20] = 0x00;
+
+ // Media type
+ fs->currentsector.sector[21] = 0xF8;
+
+
+ // FAT16 BS Details
+ if (!is_fat32)
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+ fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+ fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+ // Sectors per track
+ fs->currentsector.sector[24] = 0x00;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads
+ fs->currentsector.sector[26] = 0x00;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x20;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ // Drive number
+ fs->currentsector.sector[36] = 0x00;
+
+ // Reserved
+ fs->currentsector.sector[37] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[38] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[39] = 0x12;
+ fs->currentsector.sector[40] = 0x34;
+ fs->currentsector.sector[41] = 0x56;
+ fs->currentsector.sector[42] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+43] = name[i];
+ else
+ fs->currentsector.sector[i+43] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[54] = 'F';
+ fs->currentsector.sector[55] = 'A';
+ fs->currentsector.sector[56] = 'T';
+ fs->currentsector.sector[57] = '1';
+ fs->currentsector.sector[58] = '6';
+ fs->currentsector.sector[59] = ' ';
+ fs->currentsector.sector[60] = ' ';
+ fs->currentsector.sector[61] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+ // FAT32 BS Details
+ else
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ fs->currentsector.sector[22] = 0;
+ fs->currentsector.sector[23] = 0;
+
+ // Sectors per track (default)
+ fs->currentsector.sector[24] = 0x3F;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads (default)
+ fs->currentsector.sector[26] = 0xFF;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x00;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+ // BPB_FATSz32
+ fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+ fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+ fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+ fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+ // BPB_ExtFlags
+ fs->currentsector.sector[40] = 0;
+ fs->currentsector.sector[41] = 0;
+
+ // BPB_FSVer
+ fs->currentsector.sector[42] = 0;
+ fs->currentsector.sector[43] = 0;
+
+ // BPB_RootClus
+ fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+ fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+ fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+ fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+ // BPB_FSInfo
+ fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+ fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+ // BPB_BkBootSec
+ fs->currentsector.sector[50] = 6;
+ fs->currentsector.sector[51] = 0;
+
+ // Drive number
+ fs->currentsector.sector[64] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[66] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[67] = 0x12;
+ fs->currentsector.sector[68] = 0x34;
+ fs->currentsector.sector[69] = 0x56;
+ fs->currentsector.sector[70] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+71] = name[i];
+ else
+ fs->currentsector.sector[i+71] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[82] = 'F';
+ fs->currentsector.sector[83] = 'A';
+ fs->currentsector.sector[84] = 'T';
+ fs->currentsector.sector[85] = '3';
+ fs->currentsector.sector[86] = '2';
+ fs->currentsector.sector[87] = ' ';
+ fs->currentsector.sector[88] = ' ';
+ fs->currentsector.sector[89] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+
+ if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // FSI_LeadSig
+ fs->currentsector.sector[0] = 0x52;
+ fs->currentsector.sector[1] = 0x52;
+ fs->currentsector.sector[2] = 0x61;
+ fs->currentsector.sector[3] = 0x41;
+
+ // FSI_StrucSig
+ fs->currentsector.sector[484] = 0x72;
+ fs->currentsector.sector[485] = 0x72;
+ fs->currentsector.sector[486] = 0x41;
+ fs->currentsector.sector[487] = 0x61;
+
+ // FSI_Free_Count
+ fs->currentsector.sector[488] = 0xFF;
+ fs->currentsector.sector[489] = 0xFF;
+ fs->currentsector.sector[490] = 0xFF;
+ fs->currentsector.sector[491] = 0xFF;
+
+ // FSI_Nxt_Free
+ fs->currentsector.sector[492] = 0xFF;
+ fs->currentsector.sector[493] = 0xFF;
+ fs->currentsector.sector[494] = 0xFF;
+ fs->currentsector.sector[495] = 0xFF;
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+
+ if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+ uint32 i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // Initialise default allocate / reserved clusters
+ if (!is_fat32)
+ {
+ SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+ SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+ }
+ else
+ {
+ SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+ SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+ SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+ }
+
+ if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+ return 0;
+
+ // Zero remaining FAT sectors
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+ for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+ if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+
+ // Not valid for FAT16
+ fs->fs_info_sector = 0;
+ fs->rootdir_first_cluster = 0;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+ return 0;
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(fs, 0))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+
+ // Basic defaults for normal FAT32 partitions
+ fs->fs_info_sector = 1;
+ fs->rootdir_first_cluster = 2;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+ return 0;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FSInfo sector
+ if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+ return 0;
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(fs, 1))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ // 2GB - 32K limit for safe behaviour for FAT16
+ if (volume_sectors <= 4194304)
+ return fatfs_format_fat16(fs, volume_sectors, name);
+ else
+ return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
--- /dev/null
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
--- /dev/null
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+ #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+ #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+ struct fat_node *previous;
+ struct fat_node *next;
+};
+
+struct fat_list
+{
+ struct fat_node *head;
+ struct fat_node *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p) (p)->next
+#define fat_list_prev(l, p) (p)->previous
+#define fat_list_first(l) (l)->head
+#define fat_list_last(l) (l)->tail
+#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if(!node->previous)
+ list->head = node->next;
+ else
+ node->previous->next = node->next;
+
+ if(!node->next)
+ list->tail = node->previous;
+ else
+ node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node;
+ new_node->next = node->next;
+ if (!node->next)
+ list->tail = new_node;
+ else
+ node->next->previous = new_node;
+ node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node->previous;
+ new_node->next = node;
+ if (!node->previous)
+ list->head = new_node;
+ else
+ node->previous->next = new_node;
+ node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ node->previous = 0;
+ node->next = 0;
+ }
+ else
+ fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->tail)
+ fat_list_insert_first(list, node);
+ else
+ fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+ struct fat_node * node;
+
+ FAT_ASSERT(list);
+
+ node = fat_list_first(list);
+ if (node)
+ fat_list_remove(list, node);
+
+ return node;
+}
+
+#endif
+
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+ int i = 0;
+
+ lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Zero out buffer also
+ if (wipeTable)
+ for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+ memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+ uint8 LFNIndex, i;
+ LFNIndex = entryBuffer[0] & 0x1F;
+
+ // Limit file name to cache size!
+ if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+ return ;
+
+ // This is an error condition
+ if (LFNIndex == 0)
+ return ;
+
+ if (lfn->no_of_strings == 0)
+ lfn->no_of_strings = LFNIndex;
+
+ lfn->String[LFNIndex-1][0] = entryBuffer[1];
+ lfn->String[LFNIndex-1][1] = entryBuffer[3];
+ lfn->String[LFNIndex-1][2] = entryBuffer[5];
+ lfn->String[LFNIndex-1][3] = entryBuffer[7];
+ lfn->String[LFNIndex-1][4] = entryBuffer[9];
+ lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+ lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+ lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+ lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+ lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+ lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+ lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+ lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+ for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+ if (lfn->String[LFNIndex-1][i]==0xFF)
+ lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+ // Null terminate long filename
+ if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+ lfn->Null = '\0';
+ else if (lfn->no_of_strings)
+ lfn->String[lfn->no_of_strings][0] = '\0';
+ else
+ lfn->String[0][0] = '\0';
+
+ return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+ if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+ if ( (entry->Name[0]==FILE_HEADER_BLANK) ||
+ (entry->Name[0]==FILE_HEADER_DELETED)||
+ (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+ (entry->Attr & FILE_ATTR_SYSHID) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+ (lfn->no_of_strings) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) )
+ return 1;
+ else
+ return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_DIR)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_FILE)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+ int length = (int)strlen(filename);
+
+ if (length)
+ return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+ int i;
+ int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+ // 13 characters entries
+ int length = (int)strlen(filename);
+ int entriesRequired = fatfs_lfn_entries_required(filename);
+
+ // Filename offset
+ int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+ // Initialise to zeros
+ memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+ // LFN entry number
+ buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+ // LFN flag
+ buffer[11] = 0x0F;
+
+ // Checksum of short filename
+ buffer[13] = sfnChk;
+
+ // Copy to buffer
+ for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+ {
+ if ( (start+i) < length )
+ buffer[nameIndexes[i]] = filename[start+i];
+ else if ( (start+i) == length )
+ buffer[nameIndexes[i]] = 0x00;
+ else
+ {
+ buffer[nameIndexes[i]] = 0xFF;
+ buffer[nameIndexes[i]+1] = 0xFF;
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+ int i;
+
+ // Copy short filename
+ for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+ entry->Name[i] = shortfilename[i];
+
+ // Unless we have a RTC we might as well set these to 1980
+ entry->CrtTimeTenth = 0x00;
+ entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+ entry->CrtDate[1] = 0x00;
+ entry->CrtDate[0] = 0x20;
+ entry->LstAccDate[1] = 0x00;
+ entry->LstAccDate[0] = 0x20;
+ entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+ entry->WrtDate[1] = 0x00;
+ entry->WrtDate[0] = 0x20;
+
+ if (!dir)
+ entry->Attr = FILE_TYPE_FILE;
+ else
+ entry->Attr = FILE_TYPE_DIR;
+
+ entry->NTRes = 0x00;
+
+ entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+ entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+ entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+ int i;
+ int dotPos = -1;
+ char ext[3];
+ int pos;
+ int len = (int)strlen(filename);
+
+ // Invalid to start with .
+ if (filename[0]=='.')
+ return 0;
+
+ memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+ memset(ext, ' ', 3);
+
+ // Find dot seperator
+ for (i = 0; i< len; i++)
+ {
+ if (filename[i]=='.')
+ dotPos = i;
+ }
+
+ // Extract extensions
+ if (dotPos!=-1)
+ {
+ // Copy first three chars of extension
+ for (i = (dotPos+1); i < (dotPos+1+3); i++)
+ if (i<len)
+ ext[i-(dotPos+1)] = filename[i];
+
+ // Shorten the length to the dot position
+ len = dotPos;
+ }
+
+ // Add filename part
+ pos = 0;
+ for (i=0;i<len;i++)
+ {
+ if ( (filename[i]!=' ') && (filename[i]!='.') )
+ {
+ if (filename[i] >= 'a' && filename[i] <= 'z')
+ sfn_output[pos++] = filename[i] - 'a' + 'A';
+ else
+ sfn_output[pos++] = filename[i];
+ }
+
+ // Fill upto 8 characters
+ if (pos==FAT_SFN_SIZE_PARTIAL)
+ break;
+ }
+
+ // Add extension part
+ for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+ {
+ if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+ else
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+ char* cp;
+ char outbuf[12];
+ const char digits[] = "0123456789ABCDEF";
+
+ // Build string backwards
+ cp = outbuf;
+ do
+ {
+ *cp++ = digits[(int)(num % 10)];
+ }
+ while ((num /= 10) > 0);
+
+ *cp-- = 0;
+
+ // Copy in forwards
+ while (cp >= outbuf)
+ *s++ = *cp--;
+
+ *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+ int tail_chars;
+ char tail_str[12];
+
+ if (tailNum > 99999)
+ return 0;
+
+ // Convert to number
+ memset(tail_str, 0x00, sizeof(tail_str));
+ tail_str[0] = '~';
+ fatfs_itoa(tailNum, tail_str+1);
+
+ // Copy in base filename
+ memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+ // Overwrite with tail
+ tail_chars = (int)strlen(tail_str);
+ memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+ return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+ *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+ *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+ *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+ *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+ *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+ *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+ *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+ *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+ uint16 fat_time = 0;
+
+ // Most FAT times are to a resolution of 2 seconds
+ seconds /= FAT_TIME_SECONDS_SCALE;
+
+ fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+ fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+ fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+ return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+ uint16 fat_date = 0;
+
+ // FAT dates are relative to 1980
+ if (year >= FAT_DATE_YEAR_OFFSET)
+ year -= FAT_DATE_YEAR_OFFSET;
+
+ fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+ fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+ fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+ return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+ int i;
+ int j;
+
+ FAT_PRINTF(("Sector %d:\n", sector));
+
+ for (i=0;i<FAT_SECTOR_SIZE;i++)
+ {
+ if (!((i) % 16))
+ {
+ FAT_PRINTF((" %04d: ", i));
+ }
+
+ FAT_PRINTF(("%02x", data[i]));
+ if (!((i+1) % 4))
+ {
+ FAT_PRINTF((" "));
+ }
+
+ if (!((i+1) % 16))
+ {
+ FAT_PRINTF((" "));
+ for (j=0;j<16;j++)
+ {
+ char ch = data[i-15+j];
+
+ // Is printable?
+ if (ch > 31 && ch < 127)
+ {
+ FAT_PRINTF(("%c", ch));
+ }
+ else
+ {
+ FAT_PRINTF(("."));
+ }
+ }
+
+ FAT_PRINTF(("\n"));
+ }
+ }
+}
+#endif
--- /dev/null
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES 20
+#define MAX_LFN_ENTRY_LENGTH 13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); \
+ buffer[location+2] = (uint8)((value>>16)&0xFF); \
+ buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Structure (max 260 LFN length)
+ uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+ uint8 Null;
+#endif
+ uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int fatfs_entry_is_file(struct fat_dir_entry *entry);
+int fatfs_lfn_entries_required(char *filename);
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16 fatfs_convert_to_fat_date(int day, int month, int year);
+void fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
--- /dev/null
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+ #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+ #define FATFS_IS_LITTLE_ENDIAN 1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+ #define FATFS_MAX_LONG_FILENAME 260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+ #define FATFS_MAX_OPEN_FILES 2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES 128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+ #define FATFS_INC_WRITE_SUPPORT 1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+ #define FATFS_INC_LFN_SUPPORT 1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+ #define FATFS_DIR_LIST_SUPPORT 1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+ #define FATFS_INC_TIME_DATE_SUPPORT 0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+ #define FATFS_INC_FORMAT_SUPPORT 1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE 512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+ // Don't include stdio, but there is a printf function available
+ #ifdef FAT_PRINTF_NOINC_STDIO
+ extern int printf(const char* ctrl1, ... );
+ #define FAT_PRINTF(a) printf a
+ // Include stdio to use printf
+ #else
+ #include <stdio.h>
+ #define FAT_PRINTF(a) printf a
+ #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+ #include <time.h>
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+// c:\folder\file.zip
+// /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+ int levels = 0;
+ char expectedchar;
+
+ if (!path)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Count levels in path string
+ while (*path)
+ {
+ // Fast forward through actual subdir text to next slash
+ for (; *path; )
+ {
+ // If slash detected escape from for loop
+ if (*path == expectedchar) { path++; break; }
+ path++;
+ }
+
+ // Increase number of subdirs founds
+ levels++;
+ }
+
+ // Subtract the file itself
+ return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+ int i;
+ int pathlen=0;
+ int levels=0;
+ int copypnt=0;
+ char expectedchar;
+
+ if (!path || max_len <= 0)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Get string length of path
+ pathlen = (int)strlen (path);
+
+ // Loop through the number of times as characters in 'path'
+ for (i = 0; i<pathlen; i++)
+ {
+ // If a '\' is found then increase level
+ if (*path == expectedchar) levels++;
+
+ // If correct level and the character is not a '\' or '/' then copy text to 'output'
+ if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+ output[copypnt++] = *path;
+
+ // Increment through path string
+ path++;
+ }
+
+ // Null Terminate
+ output[copypnt] = '\0';
+
+ // If a string was copied return 0 else return 1
+ if (output[0] != '\0')
+ return 0; // OK
+ else
+ return -1; // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder filename = file.zip
+// E.g. C:\file.zip -> path = [blank] filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+ int strindex;
+
+ // Count the levels to the filepath
+ int levels = fatfs_total_path_levels(full_path);
+ if (levels == -1)
+ return -1;
+
+ // Get filename part of string
+ if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+ return -1;
+
+ // If root file
+ if (levels == 0)
+ path[0] = '\0';
+ else
+ {
+ strindex = (int)strlen(full_path) - (int)strlen(filename);
+ if (strindex > max_path)
+ strindex = max_path;
+
+ memcpy(path, full_path, strindex);
+ path[strindex-1] = '\0';
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+ int diff;
+ char a,b;
+
+ while (n--)
+ {
+ a = *s1;
+ b = *s2;
+
+ // Make lower case if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+ if ((b>='A') && (b<='Z'))
+ b+= 32;
+
+ diff = a - b;
+
+ // If different
+ if (diff)
+ return diff;
+
+ // If run out of strings
+ if ( (*s1 == 0) || (*s2 == 0) )
+ break;
+
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+ int dotPos = -1;
+ char *strSrc = str;
+
+ // Find last '.' in string (if at all)
+ while (*strSrc)
+ {
+ if (*strSrc=='.')
+ dotPos = (int)(strSrc-str);
+
+ strSrc++;
+ }
+
+ return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+ int length = strLen;
+ char *strSrc = str+strLen-1;
+
+ // Find last non white space
+ while (strLen != 0)
+ {
+ if (*strSrc == ' ')
+ length = (int)(strSrc - str);
+ else
+ break;
+
+ strSrc--;
+ strLen--;
+ }
+
+ return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+ char *ext1 = NULL;
+ char *ext2 = NULL;
+ int ext1Pos, ext2Pos;
+ int file1Len, file2Len;
+
+ // Get both files extension
+ ext1Pos = FileString_GetExtension(strA);
+ ext2Pos = FileString_GetExtension(strB);
+
+ // NOTE: Extension position can be different for matching
+ // filename if trailing space are present before it!
+ // Check that if one has an extension, so does the other
+ if ((ext1Pos==-1) && (ext2Pos!=-1))
+ return 0;
+ if ((ext2Pos==-1) && (ext1Pos!=-1))
+ return 0;
+
+ // If they both have extensions, compare them
+ if (ext1Pos!=-1)
+ {
+ // Set pointer to start of extension
+ ext1 = strA+ext1Pos+1;
+ ext2 = strB+ext2Pos+1;
+
+ // Verify that the file extension lengths match!
+ if (strlen(ext1) != strlen(ext2))
+ return 0;
+
+ // If they dont match
+ if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+ return 0;
+
+ // Filelength is upto extensions
+ file1Len = ext1Pos;
+ file2Len = ext2Pos;
+ }
+ // No extensions
+ else
+ {
+ // Filelength is actual filelength
+ file1Len = (int)strlen(strA);
+ file2Len = (int)strlen(strB);
+ }
+
+ // Find length without trailing spaces (before ext)
+ file1Len = FileString_TrimLength(strA, file1Len);
+ file2Len = FileString_TrimLength(strB, file2Len);
+
+ // Check the file lengths match
+ if (file1Len!=file2Len)
+ return 0;
+
+ // Compare main part of filenames
+ if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+ return 0;
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+ if (path)
+ {
+ while (*path)
+ {
+ // Last character?
+ if (!(*(path+1)))
+ {
+ if (*path == '\\' || *path == '/')
+ return 1;
+ }
+
+ path++;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+ int len = 0;
+ while (*in && len <= 11)
+ {
+ char a = *in++;
+
+ if (a == ' ')
+ continue;
+ // Make lower case if uppercase
+ else if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+ int len = 0;
+
+ // Get files extension offset
+ int ext_pos = FileString_GetExtension(filename);
+
+ if (ext_pos > 0 && out && maxlen)
+ {
+ filename += ext_pos + 1;
+
+ while (*filename && len < (maxlen-1))
+ {
+ char a = *filename++;
+
+ // Make lowercase if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+ int len = 0;
+ char last = 0;
+ char seperator = '/';
+
+ if (path && filename && out && maxlen > 0)
+ {
+ while (*path && len < (maxlen-2))
+ {
+ last = *path++;
+ if (last == '\\')
+ seperator = '\\';
+ *out++ = last;
+ len++;
+ }
+
+ // Add a seperator if trailing one not found
+ if (last != '\\' && last != '/')
+ *out++ = seperator;
+
+ while (*filename && len < (maxlen-1))
+ {
+ *out++ = *filename++;
+ len++;
+ }
+
+ *out = '\0';
+
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+ char output[255];
+ char output2[255];
+
+ assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+ assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+ assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+ assert(fatfs_total_path_levels("C:\\") == -1);
+ assert(fatfs_total_path_levels("") == -1);
+ assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+ assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "folder") == 0);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "dev") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "etc") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "C:\\folder") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(output[0] == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "/dev/etc") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+ assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+ assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+ assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength("C:\\file.zip ", strlen("C:\\file.zip ")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength(" ", strlen(" ")) == 0);
+
+ assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file2.ext") == 0);
+
+ assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+ assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+ assert(fatfs_string_ends_with_slash("/path") == 0);
+ assert(fatfs_string_ends_with_slash("/path/a") == 0);
+ assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+ assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+ assert(strcmp(output, "ext") != 0);
+
+ assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+ assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+ assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
--- /dev/null
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+ #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+// FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location) ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location) ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+ int i;
+
+ // FAT buffer chain head
+ fs->fat_buffer_head = NULL;
+
+ for (i=0;i<FAT_BUFFERS;i++)
+ {
+ // Initialise buffers to invalid
+ fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+ fs->fat_buffers[i].dirty = 0;
+ memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+ fs->fat_buffers[i].ptr = NULL;
+
+ // Add to head of queue
+ fs->fat_buffers[i].next = fs->fat_buffer_head;
+ fs->fat_buffer_head = &fs->fat_buffers[i];
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+ if (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ {
+ if (fs->disk_io.write_media)
+ {
+ uint32 sectors = FAT_BUFFER_SECTORS;
+ uint32 offset = pcur->address - fs->fat_begin_lba;
+
+ // Limit to sectors used for the FAT
+ if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+ sectors = FAT_BUFFER_SECTORS;
+ else
+ sectors = fs->fat_sectors - offset;
+
+ if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+ return 0;
+ }
+
+ pcur->dirty = 0;
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+ struct fat_buffer *last = NULL;
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Sector within this buffer?
+ if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+ break;
+
+ // End of list?
+ if (pcur->next == NULL)
+ {
+ // Remove buffer from list
+ if (last)
+ last->next = NULL;
+ // We the first and last buffer in the chain?
+ else
+ fs->fat_buffer_head = NULL;
+ }
+
+ last = pcur;
+ pcur = pcur->next;
+ }
+
+ // We found the sector already in FAT buffer chain
+ if (pcur)
+ {
+ pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+ return pcur;
+ }
+
+ // Else, we removed the last item from the list
+ pcur = last;
+
+ // Add to start of sector buffer list (now newest sector)
+ pcur->next = fs->fat_buffer_head;
+ fs->fat_buffer_head = pcur;
+
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(fs, pcur))
+ return 0;
+
+ // Address is now new sector
+ pcur->address = sector;
+
+ // Read next sector
+ if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+ {
+ // Read failed, invalidate buffer address
+ pcur->address = FAT32_INVALID_CLUSTER;
+ return NULL;
+ }
+
+ pcur->ptr = pcur->sector;
+ return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(fs, pcur))
+ return 0;
+
+ pcur = pcur->next;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ struct fat_buffer *pbuf;
+
+ // Why is '..' labelled with cluster 0 when it should be 2 ??
+ if (current_cluster == 0)
+ current_cluster = 2;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return (FAT32_LAST_CLUSTER);
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+ // If end of chain found
+ if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+
+ // If end of chain found
+ if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+
+ // Else return next cluster
+ return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ ;
+ else
+ {
+ // Load sector to change it
+ struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+ if (!pbuf)
+ return ;
+
+ // Change
+ FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+ fs->next_free_cluster = newValue;
+
+ // Write back FSINFO sector to disk
+ if (fs->disk_io.write_media)
+ fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+ // Invalidate cache entry
+ pbuf->address = FAT32_INVALID_CLUSTER;
+ pbuf->dirty = 0;
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ uint32 current_cluster = start_cluster;
+ struct fat_buffer *pbuf;
+
+ do
+ {
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ if ( fat_sector_offset < fs->fat_sectors)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+ }
+
+ if (nextcluster !=0 )
+ current_cluster++;
+ }
+ else
+ // Otherwise, run out of FAT sectors to check...
+ return 0;
+ }
+ while (nextcluster != 0x0);
+
+ // Found blank entry
+ *free_cluster = current_cluster;
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+ struct fat_buffer *pbuf;
+ uint32 fat_sector_offset, position;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = cluster / 256;
+ else
+ fat_sector_offset = cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 16 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 256)) * 2;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 128)) * 4;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+ uint32 last_cluster;
+ uint32 next_cluster = start_cluster;
+
+ // Loop until end of chain
+ while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+ // Clear last link
+ fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+ uint32 last_cluster = FAT32_LAST_CLUSTER;
+ uint32 next_cluster = start_cluster;
+
+ if (start_cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Loop until end of chain
+ while ( next_cluster != FAT32_LAST_CLUSTER )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+ if (!next_cluster)
+ return 0;
+ }
+
+ // Add link in for new cluster
+ fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+ // Mark new cluster as end of chain
+ fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+ uint32 i,j;
+ uint32 count = 0;
+ struct fat_buffer *pbuf;
+
+ for (i = 0; i < fs->fat_sectors; i++)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+ if (!pbuf)
+ break;
+
+ for (j = 0; j < FAT_SECTOR_SIZE; )
+ {
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 2;
+ }
+ else
+ {
+ if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 4;
+ }
+ }
+ }
+
+ return count;
+}
--- /dev/null
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs);
+int fatfs_fat_purge(struct fatfs *fs);
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32 fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
--- /dev/null
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+ #if __SIZEOF_LONG__ == 8
+ #define FATFS_DEF_UINT32_AS_INT
+ #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+
+ // If compiling on a 64-bit machine, use int as 32-bits
+ #ifdef FATFS_DEF_UINT32_AS_INT
+ typedef unsigned int uint32;
+ // Else for 32-bit machines & embedded systems, use long...
+ #else
+ typedef unsigned long uint32;
+ #endif
+#endif
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+ #define FAT_HTONS(n) (n)
+ #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+ #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+ #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+ ((((uint32)(n) & 0xFF00)) << 8) | \
+ ((((uint32)(n) & 0xFF0000)) >> 8) | \
+ ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED __attribute__ ((packed))
+#else
+ // Other compilers may require other methods of packing structures
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+ uint32 i;
+ uint32 nextcluster;
+ uint32 start = *startCluster;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+ for (i=0;i<clusters;i++)
+ {
+ // Start looking for free clusters from the beginning
+ if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+ {
+ // Point last to this
+ fatfs_fat_set_cluster(fs, start, nextcluster);
+
+ // Point this to end of file
+ fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+ // Adjust argument reference
+ start = nextcluster;
+ if (i == 0)
+ *startCluster = nextcluster;
+ }
+ else
+ return 0;
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+ uint32 clusterSize;
+ uint32 clusterCount;
+ uint32 nextcluster;
+
+ if (size==0)
+ return 0;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+ // Work out size and clusters
+ clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+ clusterCount = (size / clusterSize);
+
+ // If any left over
+ if (size-(clusterSize*clusterCount))
+ clusterCount++;
+
+ // Allocated first link in the chain if a new file
+ if (newFile)
+ {
+ if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+ return 0;
+
+ // If this is all that is needed then all done
+ if (clusterCount==1)
+ {
+ fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+ *startCluster = nextcluster;
+ return 1;
+ }
+ }
+ // Allocate from end of current chain (startCluster is end of chain)
+ else
+ nextcluster = *startCluster;
+
+ if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+ struct fat_dir_entry *directoryEntry;
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ int possible_spaces = 0;
+ int start_recorded = 0;
+
+ // No entries required?
+ if (entryCount == 0)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+ // LFN Entry
+ if (fatfs_entry_lfn_text(directoryEntry))
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the count in-case the file turns
+ // out to be deleted...
+ possible_spaces++;
+ }
+ // SFN Entry
+ else
+ {
+ // Has file been deleted?
+ if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+
+ // Else continue counting until we find a valid entry!
+ }
+ // Is the file entry empty?
+ else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the blank entries count
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+ }
+ // File entry is valid
+ else
+ {
+ // Reset all flags
+ possible_spaces = 0;
+ start_recorded = 0;
+ }
+ }
+ } // End of for
+ } // End of if
+ // Run out of free space in the directory, allocate some more
+ else
+ {
+ uint32 newCluster;
+
+ // Get a new cluster for directory
+ if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+ return 0;
+
+ // Add cluster to end of directory tree
+ if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+ return 0;
+
+ // Erase new directory cluster
+ memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;i<fs->sectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(fs, newCluster, i, 0))
+ return 0;
+ }
+
+ // If non of the name fitted on previous sectors
+ if (!start_recorded)
+ {
+ // Store start
+ *pSector = (x-1);
+ *pOffset = 0;
+ start_recorded = 1;
+ }
+
+ return 1;
+ }
+ } // End of while loop
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ uint32 x=0;
+ int entryCount;
+ struct fat_dir_entry shortEntry;
+ int dirtySector = 0;
+
+ uint32 dirSector = 0;
+ uint8 dirOffset = 0;
+ int foundEnd = 0;
+
+ uint8 checksum;
+ uint8 *pSname;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+ // How many LFN entries are required?
+ // NOTE: We always request one LFN even if it would fit in a SFN!
+ entryCount = fatfs_lfn_entries_required(filename);
+ if (!entryCount)
+ return 0;
+#else
+ entryCount = 0;
+#endif
+
+ // Find space in the directory for this filename (or allocate some more)
+ // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+ if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+ return 0;
+
+ // Generate checksum of short filename
+ pSname = (uint8*)shortfilename;
+ checksum = 0;
+ for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+ // Start from current sector where space was found!
+ x = dirSector;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // If the start position for the entry has been found
+ if (foundEnd==0)
+ if ( (dirSector==(x-1)) && (dirOffset==item) )
+ foundEnd = 1;
+
+ // Start adding filename
+ if (foundEnd)
+ {
+ if (entryCount==0)
+ {
+ // Short filename
+ fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update create, access & modify time & date
+ fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+ memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+ // Writeback
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+#if FATFS_INC_LFN_SUPPORT
+ else
+ {
+ entryCount--;
+
+ // Copy entry to directory buffer
+ fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+ dirtySector = 1;
+ }
+#endif
+ }
+ } // End of if
+
+ // Write back to disk before loading another sector
+ if (dirtySector)
+ {
+ if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+ return 0;
+
+ dirtySector = 0;
+ }
+ }
+ else
+ return 0;
+ } // End of while loop
+
+ return 0;
+}
+#endif
--- /dev/null
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
--- /dev/null
+#!/bin/sh
+
+# https://github.com/lammertb/libhttp/archive/v1.8.tar.gz
+
+
+# rm -rf include
+# rm -rf lib
+# mkdir include
+# mkdir lib
+
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# cp -a include/civetweb.h ../include/
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_64.a
+
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib COPT="-m32 -DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_32.a
+
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib CC=aarch64-linux-gnu-gcc COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_aa64.a
+
+
+# cd ..
+# rm -rf libhttp-1.8
+
+
--- /dev/null
+/* Copyright (c) 2013-2016 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#if defined(_WIN32)
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
+#endif
+#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */
+#define _WIN32_WINNT 0x0501
+#endif
+#else
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE /* for setgroups() */
+#endif
+#if defined(__linux__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
+#endif
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
+#endif
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
+#endif
+#ifdef __sun
+#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
+#define __inline inline /* not recognized on older compiler versions */
+#endif
+#endif
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+#define USE_TIMERS
+#endif
+
+#if defined(_MSC_VER)
+/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
+#pragma warning(disable : 4306)
+/* conditional expression is constant: introduced by FD_SET(..) */
+#pragma warning(disable : 4127)
+/* non-constant aggregate initializer: issued due to missing C99 support */
+#pragma warning(disable : 4204)
+/* padding added after data member */
+#pragma warning(disable : 4820)
+/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
+#pragma warning(disable : 4668)
+/* no function prototype given: converting '()' to '(void)' */
+#pragma warning(disable : 4255)
+/* function has been selected for automatic inline expansion */
+#pragma warning(disable : 4711)
+#endif
+
+
+/* This code uses static_assert to check some conditions.
+ * Unfortunately some compilers still do not support it, so we have a
+ * replacement function here. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define mg_static_assert static_assert
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define mg_static_assert static_assert
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define mg_static_assert _Static_assert
+#else
+char static_assert_replacement[1];
+#define mg_static_assert(cond, txt) \
+ extern char static_assert_replacement[(cond) ? 1 : -1]
+#endif
+
+mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
+ "int data type size check");
+mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
+ "pointer data type size check");
+mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
+/* mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t data
+ * type size check"); */
+
+/* DTL -- including winsock2.h works better if lean and mean */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(__SYMBIAN32__)
+#define NO_SSL /* SSL is not supported */
+#define NO_CGI /* CGI is not supported */
+#define PATH_MAX FILENAME_MAX
+#endif /* __SYMBIAN32__ */
+
+
+/* Include the header file here, so the CivetWeb interface is defined for the
+ * entire implementation, including the following forward definitions. */
+#include "civetweb.h"
+
+
+#ifndef IGNORE_UNUSED_RESULT
+#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
+#endif
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+#ifdef __MACH__
+
+#define CLOCK_MONOTONIC (1)
+#define CLOCK_REALTIME (2)
+
+#include <sys/time.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <assert.h>
+
+
+/* clock_gettime is not implemented on OSX */
+int clock_gettime(int clk_id, struct timespec *t);
+
+int
+clock_gettime(int clk_id, struct timespec *t)
+{
+ memset(t, 0, sizeof(*t));
+ if (clk_id == CLOCK_REALTIME) {
+ struct timeval now;
+ int rv = gettimeofday(&now, NULL);
+ if (rv) {
+ return rv;
+ }
+ t->tv_sec = now.tv_sec;
+ t->tv_nsec = now.tv_usec * 1000;
+ return 0;
+
+ } else if (clk_id == CLOCK_MONOTONIC) {
+ static uint64_t clock_start_time = 0;
+ static mach_timebase_info_data_t timebase_ifo = {0, 0};
+
+ uint64_t now = mach_absolute_time();
+
+ if (clock_start_time == 0) {
+ kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
+#if defined(DEBUG)
+ assert(mach_status == KERN_SUCCESS);
+#else
+ /* appease "unused variable" warning for release builds */
+ (void)mach_status;
+#endif
+ clock_start_time = now;
+ }
+
+ now = (uint64_t)((double)(now - clock_start_time)
+ * (double)timebase_ifo.numer
+ / (double)timebase_ifo.denom);
+
+ t->tv_sec = now / 1000000000;
+ t->tv_nsec = now % 1000000000;
+ return 0;
+ }
+ return -1; /* EINVAL - Clock ID is unknown */
+}
+#endif
+
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+
+
+#ifndef MAX_WORKER_THREADS
+#define MAX_WORKER_THREADS (1024 * 64)
+#endif
+#ifndef SOCKET_TIMEOUT_QUANTUM
+#define SOCKET_TIMEOUT_QUANTUM (10000)
+#endif
+
+mg_static_assert(MAX_WORKER_THREADS >= 1,
+ "worker threads must be a positive number");
+
+#if defined(_WIN32) \
+ && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */
+#include <windows.h>
+#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
+#include <ws2tcpip.h>
+
+typedef const char *SOCK_OPT_TYPE;
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (MAX_PATH)
+#endif
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (4096)
+#endif
+
+mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
+
+#ifndef _IN_PORT_T
+#ifndef in_port_t
+#define in_port_t u_short
+#endif
+#endif
+
+#ifndef _WIN32_WCE
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else /* _WIN32_WCE */
+#define NO_CGI /* WinCE has no pipes */
+
+typedef long off_t;
+
+#define errno ((int)(GetLastError()))
+#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
+#endif /* _WIN32_WCE */
+
+#define MAKEUQUAD(lo, hi) \
+ ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
+#define RATE_DIFF (10000000) /* 100 nsecs */
+#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
+#define SYS2UNIX_TIME(lo, hi) \
+ ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
+
+/* Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers */
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
+#define strtoll(x, y, z) (_atoi64(x))
+#else
+#define __func__ __FUNCTION__
+#define strtoull(x, y, z) (_strtoui64(x, y, z))
+#define strtoll(x, y, z) (_strtoi64(x, y, z))
+#endif
+#endif /* _MSC_VER */
+
+#define ERRNO ((int)(GetLastError()))
+#define NO_SOCKLEN_T
+
+#if defined(_WIN64) || defined(__MINGW64__)
+#define SSL_LIB "ssleay64.dll"
+#define CRYPTO_LIB "libeay64.dll"
+#else
+#define SSL_LIB "ssleay32.dll"
+#define CRYPTO_LIB "libeay32.dll"
+#endif
+
+#define O_NONBLOCK (0)
+#ifndef W_OK
+#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
+#endif
+#if !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif /* !EWOULDBLOCK */
+#define _POSIX_
+#define INT64_FMT "I64d"
+#define UINT64_FMT "I64u"
+
+#define WINCDECL __cdecl
+#define SHUT_RD (0)
+#define SHUT_WR (1)
+#define SHUT_BOTH (2)
+#define vsnprintf_impl _vsnprintf
+#define access _access
+#define mg_sleep(x) (Sleep(x))
+
+#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
+#ifndef popen
+#define popen(x, y) (_popen(x, y))
+#endif
+#ifndef pclose
+#define pclose(x) (_pclose(x))
+#endif
+#define close(x) (_close(x))
+#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
+#define RTLD_LAZY (0)
+#define fseeko(x, y, z) (_lseeki64(_fileno(x), (y), (z)) == -1 ? -1 : 0)
+#define fdopen(x, y) (_fdopen((x), (y)))
+#define write(x, y, z) (_write((x), (y), (unsigned)z))
+#define read(x, y, z) (_read((x), (y), (unsigned)z))
+#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
+#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
+#define sleep(x) (Sleep((x)*1000))
+#define rmdir(x) (_rmdir(x))
+#define timegm(x) (_mkgmtime(x))
+
+#if !defined(fileno)
+#define fileno(x) (_fileno(x))
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef DWORD pthread_key_t;
+typedef HANDLE pthread_t;
+typedef struct {
+ CRITICAL_SECTION threadIdSec;
+ int waitingthreadcount; /* The number of threads queued. */
+ pthread_t *waitingthreadhdls; /* The thread handles. */
+} pthread_cond_t;
+
+#ifndef __clockid_t_defined
+typedef DWORD clockid_t;
+#endif
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC (1)
+#endif
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME (2)
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define _TIMESPEC_DEFINED
+#endif
+#ifndef _TIMESPEC_DEFINED
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif
+
+#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+static void path_to_unicode(const struct mg_connection *conn,
+ const char *path,
+ wchar_t *wbuf,
+ size_t wbuf_len);
+struct file;
+static const char *
+mg_fgets(char *buf, size_t size, struct file *filep, char **p);
+
+
+#if defined(HAVE_STDINT)
+#include <stdint.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 int64_t;
+#define INT64_MAX (9223372036854775807)
+#endif /* HAVE_STDINT */
+
+/* POSIX dirent interface */
+struct dirent {
+ char d_name[PATH_MAX];
+};
+
+typedef struct DIR {
+ HANDLE handle;
+ WIN32_FIND_DATAW info;
+ struct dirent result;
+} DIR;
+
+#if defined(_WIN32) && !defined(POLLIN)
+#ifndef HAVE_POLL
+struct pollfd {
+ SOCKET fd;
+ short events;
+ short revents;
+};
+#define POLLIN (0x0300)
+#endif
+#endif
+
+/* Mark required libraries */
+#if defined(_MSC_VER)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#else /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \
+ block */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+typedef const void *SOCK_OPT_TYPE;
+
+#if defined(ANDROID)
+typedef unsigned short int in_port_t;
+#endif
+
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#define vsnprintf_impl vsnprintf
+
+#if !defined(NO_SSL_DL) && !defined(NO_SSL)
+#include <dlfcn.h>
+#endif
+#include <pthread.h>
+#if defined(__MACH__)
+#define SSL_LIB "libssl.dylib"
+#define CRYPTO_LIB "libcrypto.dylib"
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "libssl.so"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libcrypto.so"
+#endif
+#endif
+#ifndef O_BINARY
+#define O_BINARY (0)
+#endif /* O_BINARY */
+#define closesocket(a) (close(a))
+#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
+#define mg_remove(conn, x) (remove(x))
+#define mg_sleep(x) (usleep((x)*1000))
+#define mg_opendir(conn, x) (opendir(x))
+#define mg_closedir(x) (closedir(x))
+#define mg_readdir(x) (readdir(x))
+#define ERRNO (errno)
+#define INVALID_SOCKET (-1)
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+typedef int SOCKET;
+#define WINCDECL
+
+#if defined(__hpux)
+/* HPUX 11 does not have monotonic, fall back to realtime */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
+ * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
+ * the prototypes use int* rather than socklen_t* which matches the
+ * actual library expectation. When called with the wrong size arg
+ * accept() returns a zero client inet addr and check_acl() always
+ * fails. Since socklen_t is widely used below, just force replace
+ * their typedef with int. - DTL
+ */
+#define socklen_t int
+#endif /* hpux */
+
+#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \
+ block */
+
+/* va_copy should always be a macro, C99 and C++11 - DTL */
+#ifndef va_copy
+#define va_copy(x, y) ((x) = (y))
+#endif
+
+#ifdef _WIN32
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static CRITICAL_SECTION global_log_file_lock;
+static DWORD
+pthread_self(void)
+{
+ return GetCurrentThreadId();
+}
+
+
+static int
+pthread_key_create(
+ pthread_key_t *key,
+ void (*_ignored)(void *) /* destructor not supported for Windows */
+ )
+{
+ (void)_ignored;
+
+ if ((key != 0)) {
+ *key = TlsAlloc();
+ return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
+ }
+ return -2;
+}
+
+
+static int
+pthread_key_delete(pthread_key_t key)
+{
+ return TlsFree(key) ? 0 : 1;
+}
+
+
+static int
+pthread_setspecific(pthread_key_t key, void *value)
+{
+ return TlsSetValue(key, value) ? 0 : 1;
+}
+
+
+static void *
+pthread_getspecific(pthread_key_t key)
+{
+ return TlsGetValue(key);
+}
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
+#else
+static pthread_mutexattr_t pthread_mutex_attr;
+#endif /* _WIN32 */
+
+
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#define CGI_ENVIRONMENT_SIZE (4096)
+#define MAX_CGI_ENVIR_VARS (256)
+#define MG_BUF_LEN (8192)
+
+#ifndef MAX_REQUEST_SIZE
+#define MAX_REQUEST_SIZE (16384)
+#endif
+
+mg_static_assert(MAX_REQUEST_SIZE >= 256,
+ "request size length must be a positive number");
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+#if !defined(DEBUG_TRACE)
+#if defined(DEBUG)
+
+
+static void DEBUG_TRACE_FUNC(const char *func,
+ unsigned line,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(3, 4);
+
+static void
+DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
+{
+ va_list args;
+ flockfile(stdout);
+ printf("*** %lu.%p.%s.%u: ",
+ (unsigned long)time(NULL),
+ (void *)pthread_self(),
+ func,
+ line);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ putchar('\n');
+ fflush(stdout);
+ funlockfile(stdout);
+}
+
+#define DEBUG_TRACE(fmt, ...) \
+ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
+
+#else
+#define DEBUG_TRACE(fmt, ...) \
+ do { \
+ } while (0)
+#endif /* DEBUG */
+#endif /* DEBUG_TRACE */
+
+#if defined(MEMORY_DEBUGGING)
+unsigned long mg_memory_debug_blockCount = 0;
+unsigned long mg_memory_debug_totalMemUsed = 0;
+
+
+static void *
+mg_malloc_ex(size_t size, const char *file, unsigned line)
+{
+ void *data = malloc(size + sizeof(size_t));
+ void *memory = 0;
+ char mallocStr[256];
+
+ if (data) {
+ *(size_t *)data = size;
+ mg_memory_debug_totalMemUsed += size;
+ mg_memory_debug_blockCount++;
+ memory = (void *)(((char *)data) + sizeof(size_t));
+ }
+
+ sprintf(mallocStr,
+ "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
+ memory,
+ (unsigned long)size,
+ mg_memory_debug_totalMemUsed,
+ mg_memory_debug_blockCount,
+ file,
+ line);
+#if defined(_WIN32)
+ OutputDebugStringA(mallocStr);
+#else
+ DEBUG_TRACE("%s", mallocStr);
+#endif
+
+ return memory;
+}
+
+
+static void *
+mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
+{
+ void *data = mg_malloc_ex(size * count, file, line);
+ if (data) {
+ memset(data, 0, size);
+ }
+ return data;
+}
+
+
+static void
+mg_free_ex(void *memory, const char *file, unsigned line)
+{
+ char mallocStr[256];
+ void *data = (void *)(((char *)memory) - sizeof(size_t));
+ size_t size;
+
+ if (memory) {
+ size = *(size_t *)data;
+ mg_memory_debug_totalMemUsed -= size;
+ mg_memory_debug_blockCount--;
+ sprintf(mallocStr,
+ "MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
+ memory,
+ (unsigned long)size,
+ mg_memory_debug_totalMemUsed,
+ mg_memory_debug_blockCount,
+ file,
+ line);
+#if defined(_WIN32)
+ OutputDebugStringA(mallocStr);
+#else
+ DEBUG_TRACE("%s", mallocStr);
+#endif
+
+ free(data);
+ }
+}
+
+
+static void *
+mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
+{
+ char mallocStr[256];
+ void *data;
+ void *_realloc;
+ size_t oldsize;
+
+ if (newsize) {
+ if (memory) {
+ data = (void *)(((char *)memory) - sizeof(size_t));
+ oldsize = *(size_t *)data;
+ _realloc = realloc(data, newsize + sizeof(size_t));
+ if (_realloc) {
+ data = _realloc;
+ mg_memory_debug_totalMemUsed -= oldsize;
+ sprintf(mallocStr,
+ "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
+ memory,
+ (unsigned long)oldsize,
+ mg_memory_debug_totalMemUsed,
+ mg_memory_debug_blockCount,
+ file,
+ line);
+#if defined(_WIN32)
+ OutputDebugStringA(mallocStr);
+#else
+ DEBUG_TRACE("%s", mallocStr);
+#endif
+ mg_memory_debug_totalMemUsed += newsize;
+ sprintf(mallocStr,
+ "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
+ memory,
+ (unsigned long)newsize,
+ mg_memory_debug_totalMemUsed,
+ mg_memory_debug_blockCount,
+ file,
+ line);
+#if defined(_WIN32)
+ OutputDebugStringA(mallocStr);
+#else
+ DEBUG_TRACE("%s", mallocStr);
+#endif
+ *(size_t *)data = newsize;
+ data = (void *)(((char *)data) + sizeof(size_t));
+ } else {
+#if defined(_WIN32)
+ OutputDebugStringA("MEM: realloc failed\n");
+#else
+ DEBUG_TRACE("%s", "MEM: realloc failed\n");
+#endif
+ return _realloc;
+ }
+ } else {
+ data = mg_malloc_ex(newsize, file, line);
+ }
+ } else {
+ data = 0;
+ mg_free_ex(memory, file, line);
+ }
+
+ return data;
+}
+
+#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__)
+#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__)
+#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__)
+#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
+
+#else
+
+static __inline void *
+mg_malloc(size_t a)
+{
+ return malloc(a);
+}
+
+static __inline void *
+mg_calloc(size_t a, size_t b)
+{
+ return calloc(a, b);
+}
+
+static __inline void *
+mg_realloc(void *a, size_t b)
+{
+ return realloc(a, b);
+}
+
+static __inline void
+mg_free(void *a)
+{
+ free(a);
+}
+
+#endif
+
+
+static void mg_vsnprintf(const struct mg_connection *conn,
+ int *truncated,
+ char *buf,
+ size_t buflen,
+ const char *fmt,
+ va_list ap);
+
+static void mg_snprintf(const struct mg_connection *conn,
+ int *truncated,
+ char *buf,
+ size_t buflen,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(5, 6);
+
+/* This following lines are just meant as a reminder to use the mg-functions
+ * for memory management */
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef calloc
+#undef calloc
+#endif
+#ifdef realloc
+#undef realloc
+#endif
+#ifdef free
+#undef free
+#endif
+#ifdef snprintf
+#undef snprintf
+#endif
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
+#ifdef _WIN32 /* vsnprintf must not be used in any system, * \
+ * but this define only works well for Windows. */
+#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
+#endif
+
+#define MD5_STATIC static
+#include "md5.inl"
+
+/* Darwin prior to 7.0 and Win32 do not have socklen_t */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+#define _DARWIN_UNLIMITED_SELECT
+
+#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
+
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL (0)
+#endif
+
+#if !defined(SOMAXCONN)
+#define SOMAXCONN (100)
+#endif
+
+/* Size of the accepted socket queue */
+#if !defined(MGSQLEN)
+#define MGSQLEN (20)
+#endif
+
+#if defined(NO_SSL_DL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#else
+/* SSL loaded dynamically from DLL.
+ * I put the prototypes here to be independent from OpenSSL source
+ * installation. */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+#define SSL_CTRL_OPTIONS (32)
+#define SSL_CTRL_CLEAR_OPTIONS (77)
+#define SSL_CTRL_SET_ECDH_AUTO (94)
+
+#define SSL_VERIFY_NONE (0)
+#define SSL_VERIFY_PEER (1)
+#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
+#define SSL_VERIFY_CLIENT_ONCE (4)
+#define SSL_OP_ALL ((long)(0x80000BFFUL))
+#define SSL_OP_NO_SSLv2 (0x01000000L)
+#define SSL_OP_NO_SSLv3 (0x02000000L)
+#define SSL_OP_NO_TLSv1 (0x04000000L)
+#define SSL_OP_NO_TLSv1_2 (0x08000000L)
+#define SSL_OP_NO_TLSv1_1 (0x10000000L)
+#define SSL_OP_SINGLE_DH_USE (0x00100000L)
+
+struct ssl_func {
+ const char *name; /* SSL function name */
+ void (*ptr)(void); /* Function pointer */
+};
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb \
+ (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
+#define SSL_CTX_use_certificate_chain_file \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
+#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
+#define SSL_CTX_set_verify \
+ (*(void (*)(SSL_CTX *, \
+ int, \
+ int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations \
+ (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
+#define SSL_CIPHER_get_name \
+ (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
+#define SSL_CTX_set_session_id_context \
+ (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
+#define SSL_CTX_set_cipher_list \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
+#define SSL_CTX_set_options(ctx, op) \
+ SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
+#define SSL_CTX_clear_options(ctx, op) \
+ SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
+#define CRYPTO_set_locking_callback \
+ (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
+#define CRYPTO_set_id_callback \
+ (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
+#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
+#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
+#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
+#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
+
+
+/* set_ssl_option() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
+ {"SSL_accept", NULL},
+ {"SSL_connect", NULL},
+ {"SSL_read", NULL},
+ {"SSL_write", NULL},
+ {"SSL_get_error", NULL},
+ {"SSL_set_fd", NULL},
+ {"SSL_new", NULL},
+ {"SSL_CTX_new", NULL},
+ {"SSLv23_server_method", NULL},
+ {"SSL_library_init", NULL},
+ {"SSL_CTX_use_PrivateKey_file", NULL},
+ {"SSL_CTX_use_certificate_file", NULL},
+ {"SSL_CTX_set_default_passwd_cb", NULL},
+ {"SSL_CTX_free", NULL},
+ {"SSL_load_error_strings", NULL},
+ {"SSL_CTX_use_certificate_chain_file", NULL},
+ {"SSLv23_client_method", NULL},
+ {"SSL_pending", NULL},
+ {"SSL_CTX_set_verify", NULL},
+ {"SSL_shutdown", NULL},
+ {"SSL_CTX_load_verify_locations", NULL},
+ {"SSL_CTX_set_default_verify_paths", NULL},
+ {"SSL_CTX_set_verify_depth", NULL},
+ {"SSL_get_peer_certificate", NULL},
+ {"SSL_get_version", NULL},
+ {"SSL_get_current_cipher", NULL},
+ {"SSL_CIPHER_get_name", NULL},
+ {"SSL_CTX_check_private_key", NULL},
+ {"SSL_CTX_set_session_id_context", NULL},
+ {"SSL_CTX_ctrl", NULL},
+ {"SSL_CTX_set_cipher_list", NULL},
+ {NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+#if !defined(NO_SSL)
+static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
+ {"CRYPTO_set_locking_callback", NULL},
+ {"CRYPTO_set_id_callback", NULL},
+ {"ERR_get_error", NULL},
+ {"ERR_error_string", NULL},
+ {"ERR_remove_state", NULL},
+ {"ERR_free_strings", NULL},
+ {"ENGINE_cleanup", NULL},
+ {"CONF_modules_unload", NULL},
+ {"CRYPTO_cleanup_all_ex_data", NULL},
+ {"EVP_cleanup", NULL},
+ {NULL, NULL}};
+#endif /* NO_SSL */
+#endif /* NO_SSL_DL */
+
+
+#if !defined(NO_CACHING)
+static const char *month_names[] = {"Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"};
+#endif /* !NO_CACHING */
+
+/* Unified socket address. For IPv6 support, add IPv6 address structure in the
+ * union u. */
+union usa {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if defined(USE_IPV6)
+ struct sockaddr_in6 sin6;
+#endif
+};
+
+/* Describes a string (chunk of memory). */
+struct vec {
+ const char *ptr;
+ size_t len;
+};
+
+struct file {
+ uint64_t size;
+ time_t last_modified;
+ FILE *fp;
+ const char *membuf; /* Non-NULL if file data is in memory */
+ int is_directory;
+ int gzipped; /* set to 1 if the content is gzipped
+ * in which case we need a content-encoding: gzip header */
+};
+
+#define STRUCT_FILE_INITIALIZER \
+ { \
+ (uint64_t)0, (time_t)0, (FILE *)NULL, (const char *)NULL, 0, 0 \
+ }
+
+/* Describes listening socket, or socket which was accept()-ed by the master
+ * thread and queued for future handling by the worker thread. */
+struct socket {
+ SOCKET sock; /* Listening socket */
+ union usa lsa; /* Local socket address */
+ union usa rsa; /* Remote socket address */
+ unsigned char is_ssl; /* Is port SSL-ed */
+ unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
+ * port */
+};
+
+/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */
+enum {
+ CGI_EXTENSIONS,
+ CGI_ENVIRONMENT,
+ PUT_DELETE_PASSWORDS_FILE,
+ CGI_INTERPRETER,
+ PROTECT_URI,
+ AUTHENTICATION_DOMAIN,
+ SSI_EXTENSIONS,
+ THROTTLE,
+ ACCESS_LOG_FILE,
+ ENABLE_DIRECTORY_LISTING,
+ ERROR_LOG_FILE,
+ GLOBAL_PASSWORDS_FILE,
+ INDEX_FILES,
+ ENABLE_KEEP_ALIVE,
+ ACCESS_CONTROL_LIST,
+ EXTRA_MIME_TYPES,
+ LISTENING_PORTS,
+ DOCUMENT_ROOT,
+ SSL_CERTIFICATE,
+ NUM_THREADS,
+ RUN_AS_USER,
+ REWRITE,
+ HIDE_FILES,
+ REQUEST_TIMEOUT,
+ SSL_DO_VERIFY_PEER,
+ SSL_CA_PATH,
+ SSL_CA_FILE,
+ SSL_VERIFY_DEPTH,
+ SSL_DEFAULT_VERIFY_PATHS,
+ SSL_CIPHER_LIST,
+ SSL_PROTOCOL_VERSION,
+ SSL_SHORT_TRUST,
+#if defined(USE_WEBSOCKET)
+ WEBSOCKET_TIMEOUT,
+#endif
+ DECODE_URL,
+
+#if defined(USE_LUA)
+ LUA_PRELOAD_FILE,
+ LUA_SCRIPT_EXTENSIONS,
+ LUA_SERVER_PAGE_EXTENSIONS,
+#endif
+#if defined(USE_DUKTAPE)
+ DUKTAPE_SCRIPT_EXTENSIONS,
+#endif
+
+#if defined(USE_WEBSOCKET)
+ WEBSOCKET_ROOT,
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ LUA_WEBSOCKET_EXTENSIONS,
+#endif
+ ACCESS_CONTROL_ALLOW_ORIGIN,
+ ERROR_PAGES,
+ CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
+ * socket option typedef TCP_NODELAY. */
+#if !defined(NO_CACHING)
+ STATIC_FILE_MAX_AGE,
+#endif
+
+ NUM_OPTIONS
+};
+
+
+/* Config option name, config types, default value */
+static struct mg_option config_options[] = {
+ {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+ {"cgi_environment", CONFIG_TYPE_STRING, NULL},
+ {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL},
+ {"cgi_interpreter", CONFIG_TYPE_FILE, NULL},
+ {"protect_uri", CONFIG_TYPE_STRING, NULL},
+ {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"},
+ {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
+ {"throttle", CONFIG_TYPE_STRING, NULL},
+ {"access_log_file", CONFIG_TYPE_FILE, NULL},
+ {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"},
+ {"error_log_file", CONFIG_TYPE_FILE, NULL},
+ {"global_auth_file", CONFIG_TYPE_FILE, NULL},
+ {"index_files",
+ CONFIG_TYPE_STRING,
+#ifdef USE_LUA
+ "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,"
+ "index.shtml,index.php"},
+#else
+ "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
+#endif
+ {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"},
+ {"access_control_list", CONFIG_TYPE_STRING, NULL},
+ {"extra_mime_types", CONFIG_TYPE_STRING, NULL},
+ {"listening_ports", CONFIG_TYPE_STRING, "8080"},
+ {"document_root", CONFIG_TYPE_DIRECTORY, NULL},
+ {"ssl_certificate", CONFIG_TYPE_FILE, NULL},
+ {"num_threads", CONFIG_TYPE_NUMBER, "50"},
+ {"run_as_user", CONFIG_TYPE_STRING, NULL},
+ {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL},
+ {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
+ {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+ {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
+ {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
+ {"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
+ {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
+ {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
+ {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL},
+ {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"},
+ {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"},
+#if defined(USE_WEBSOCKET)
+ {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+#endif
+ {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"},
+
+#if defined(USE_LUA)
+ {"lua_preload_file", CONFIG_TYPE_FILE, NULL},
+ {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+ {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
+#endif
+#if defined(USE_DUKTAPE)
+ /* The support for duktape is still in alpha version state.
+ * The name of this config option might change. */
+ {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
+#endif
+
+#if defined(USE_WEBSOCKET)
+ {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+#endif
+ {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"},
+ {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
+ {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
+#if !defined(NO_CACHING)
+ {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"},
+#endif
+
+ {NULL, CONFIG_TYPE_UNKNOWN, NULL}};
+
+/* Check if the config_options and the corresponding enum have compatible
+ * sizes. */
+mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
+ == (NUM_OPTIONS + 1),
+ "config_options and enum not sync");
+
+enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
+
+struct mg_handler_info {
+ /* Name/Pattern of the URI. */
+ char *uri;
+ size_t uri_len;
+
+ /* handler type */
+ int handler_type;
+
+ /* Handler for http/https or authorization requests. */
+ mg_request_handler handler;
+
+ /* Handler for ws/wss (websocket) requests. */
+ mg_websocket_connect_handler connect_handler;
+ mg_websocket_ready_handler ready_handler;
+ mg_websocket_data_handler data_handler;
+ mg_websocket_close_handler close_handler;
+
+ /* Handler for authorization requests */
+ mg_authorization_handler auth_handler;
+
+ /* User supplied argument for the handler function. */
+ void *cbdata;
+
+ /* next handler in a linked list */
+ struct mg_handler_info *next;
+};
+
+struct mg_context {
+ volatile int stop_flag; /* Should we stop event loop */
+ SSL_CTX *ssl_ctx; /* SSL context */
+ char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
+ struct mg_callbacks callbacks; /* User-defined callback function */
+ void *user_data; /* User-defined data */
+ int context_type; /* 1 = server context, 2 = client context */
+
+ struct socket *listening_sockets;
+ in_port_t *listening_ports;
+ unsigned int num_listening_sockets;
+
+ volatile int
+ running_worker_threads; /* Number of currently running worker threads */
+ pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+ pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */
+
+ struct socket queue[MGSQLEN]; /* Accepted sockets */
+ volatile int sq_head; /* Head of the socket queue */
+ volatile int sq_tail; /* Tail of the socket queue */
+ pthread_cond_t sq_full; /* Signaled when socket is produced */
+ pthread_cond_t sq_empty; /* Signaled when socket is consumed */
+ pthread_t masterthreadid; /* The master thread ID */
+ unsigned int
+ cfg_worker_threads; /* The number of configured worker threads. */
+ pthread_t *workerthreadids; /* The worker thread IDs */
+
+ time_t start_time; /* Server start time, used for authentication */
+ uint64_t auth_nonce_mask; /* Mask for all nonce values */
+ pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+ unsigned long nonce_count; /* Used nonces, used for authentication */
+
+ char *systemName; /* What operating system is running */
+
+ /* linked list of uri handlers */
+ struct mg_handler_info *handlers;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ /* linked list of shared lua websockets */
+ struct mg_shared_lua_websocket_list *shared_lua_websockets;
+#endif
+
+#ifdef USE_TIMERS
+ struct ttimers *timers;
+#endif
+};
+
+
+struct mg_connection {
+ struct mg_request_info request_info;
+ struct mg_context *ctx;
+ SSL *ssl; /* SSL descriptor */
+ SSL_CTX *client_ssl_ctx; /* SSL context for client connections */
+ struct socket client; /* Connected client */
+ time_t conn_birth_time; /* Time (wall clock) when connection was
+ * established */
+ struct timespec req_time; /* Time (since system start) when the request
+ * was received */
+ int64_t num_bytes_sent; /* Total bytes sent to client */
+ int64_t content_len; /* Content-Length header value */
+ int64_t consumed_content; /* How many bytes of content have been read */
+ int is_chunked; /* Transfer-Encoding is chunked: 0=no, 1=yes:
+ * data available, 2: all data read */
+ size_t chunk_remainder; /* Unread data from the last chunk */
+ char *buf; /* Buffer for received data */
+ char *path_info; /* PATH_INFO part of the URL */
+
+ int must_close; /* 1 if connection must be closed */
+ int in_error_handler; /* 1 if in handler for user defined error
+ * pages */
+ int internal_error; /* 1 if an error occured while processing the
+ * request */
+
+ int buf_size; /* Buffer size */
+ int request_len; /* Size of the request + headers in a buffer */
+ int data_len; /* Total size of data in a buffer */
+ int status_code; /* HTTP reply status code, e.g. 200 */
+ int throttle; /* Throttling, bytes/sec. <= 0 means no
+ * throttle */
+ time_t last_throttle_time; /* Last time throttled data was sent */
+ int64_t last_throttle_bytes; /* Bytes sent this second */
+ pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure
+ * atomic transmissions for websockets */
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ void *lua_websocket_state; /* Lua_State for a websocket connection */
+#endif
+};
+
+
+static pthread_key_t sTlsKey; /* Thread local storage index */
+static int sTlsInit = 0;
+static int thread_idx_max = 0;
+
+
+struct mg_workerTLS {
+ int is_master;
+ unsigned long thread_idx;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ HANDLE pthread_cond_helper_mutex;
+#endif
+};
+
+/* Directory entry */
+struct de {
+ struct mg_connection *conn;
+ char *file_name;
+ struct file file;
+};
+
+
+#if defined(USE_WEBSOCKET)
+static int is_websocket_protocol(const struct mg_connection *conn);
+#else
+#define is_websocket_protocol(conn) (0)
+#endif
+
+
+static int
+mg_atomic_inc(volatile int *addr)
+{
+ int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ /* Depending on the SDK, this function uses either
+ * (volatile unsigned int *) or (volatile LONG *),
+ * so whatever you use, the other SDK is likely to raise a warning. */
+ ret = InterlockedIncrement((volatile long *)addr);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+ ret = __sync_add_and_fetch(addr, 1);
+#else
+ ret = (++(*addr));
+#endif
+ return ret;
+}
+
+
+static int
+mg_atomic_dec(volatile int *addr)
+{
+ int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ /* Depending on the SDK, this function uses either
+ * (volatile unsigned int *) or (volatile LONG *),
+ * so whatever you use, the other SDK is likely to raise a warning. */
+ ret = InterlockedDecrement((volatile long *)addr);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+ ret = __sync_sub_and_fetch(addr, 1);
+#else
+ ret = (--(*addr));
+#endif
+ return ret;
+}
+
+#if !defined(NO_THREAD_NAME)
+#if defined(_WIN32) && defined(_MSC_VER)
+/* Set the thread name for debugging purposes in Visual Studio
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; /* Must be 0x1000. */
+ LPCSTR szName; /* Pointer to name (in user addr space). */
+ DWORD dwThreadID; /* Thread ID (-1=caller thread). */
+ DWORD dwFlags; /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+#elif defined(__linux__)
+#include <sys/prctl.h>
+#include <sys/sendfile.h>
+#endif
+
+
+static void
+mg_set_thread_name(const char *name)
+{
+ char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
+
+ mg_snprintf(
+ NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
+
+#if defined(_WIN32)
+#if defined(_MSC_VER)
+ /* Windows and Visual Studio Compiler */
+ __try
+ {
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = ~0U;
+ info.dwFlags = 0;
+
+ RaiseException(0x406D1388,
+ 0,
+ sizeof(info) / sizeof(ULONG_PTR),
+ (ULONG_PTR *)&info);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+#elif defined(__MINGW32__)
+/* No option known to set thread name for MinGW */
+#endif
+#elif defined(__GLIBC__) \
+ && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
+ /* pthread_setname_np first appeared in glibc in version 2.12*/
+ (void)pthread_setname_np(pthread_self(), threadName);
+#elif defined(__linux__)
+ /* on linux we can use the old prctl function */
+ (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
+#endif
+}
+#else /* !defined(NO_THREAD_NAME) */
+void
+mg_set_thread_name(const char *threadName)
+{
+}
+#endif
+
+
+#if defined(MG_LEGACY_INTERFACE)
+const char **
+mg_get_valid_option_names(void)
+{
+ /* This function is deprecated. Use mg_get_valid_options instead. */
+ static const char *
+ data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0};
+ int i;
+
+ for (i = 0; config_options[i].name != NULL; i++) {
+ data[i * 2] = config_options[i].name;
+ data[i * 2 + 1] = config_options[i].default_value;
+ }
+
+ return data;
+}
+#endif
+
+
+const struct mg_option *
+mg_get_valid_options(void)
+{
+ return config_options;
+}
+
+
+static int
+is_file_in_memory(const struct mg_connection *conn,
+ const char *path,
+ struct file *filep)
+{
+ size_t size = 0;
+ if (!conn || !filep) {
+ return 0;
+ }
+
+ if (conn->ctx->callbacks.open_file) {
+ filep->membuf = conn->ctx->callbacks.open_file(conn, path, &size);
+ if (filep->membuf != NULL) {
+ /* NOTE: override filep->size only on success. Otherwise, it might
+ * break constructs like if (!mg_stat() || !mg_fopen()) ... */
+ filep->size = size;
+ }
+ }
+
+ return filep->membuf != NULL;
+}
+
+
+static int
+is_file_opened(const struct file *filep)
+{
+ if (!filep) {
+ return 0;
+ }
+
+ return filep->membuf != NULL || filep->fp != NULL;
+}
+
+
+/* mg_fopen will open a file either in memory or on the disk.
+ * The input parameter path is a string in UTF-8 encoding.
+ * The input parameter mode is the same as for fopen.
+ * Either fp or membuf will be set in the output struct filep.
+ * The function returns 1 on success, 0 on error. */
+static int
+mg_fopen(const struct mg_connection *conn,
+ const char *path,
+ const char *mode,
+ struct file *filep)
+{
+ struct stat st;
+
+ if (!filep) {
+ return 0;
+ }
+
+ /* TODO (high): mg_fopen should only open a file, while mg_stat should
+ * only get the file status. They should not work on different members of
+ * the same structure (bad cohesion). */
+ memset(filep, 0, sizeof(*filep));
+
+ if (stat(path, &st) == 0) {
+ filep->size = (uint64_t)(st.st_size);
+ }
+
+ if (!is_file_in_memory(conn, path, filep)) {
+#ifdef _WIN32
+ wchar_t wbuf[PATH_MAX], wmode[20];
+ path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+ MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
+ filep->fp = _wfopen(wbuf, wmode);
+#else
+ /* Linux et al already use unicode. No need to convert. */
+ filep->fp = fopen(path, mode);
+#endif
+ }
+
+ return is_file_opened(filep);
+}
+
+
+static void
+mg_fclose(struct file *filep)
+{
+ if (filep != NULL && filep->fp != NULL) {
+ fclose(filep->fp);
+ }
+}
+
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+ for (; *src != '\0' && n > 1; n--) {
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+}
+
+
+static int
+lowercase(const char *s)
+{
+ return tolower(*(const unsigned char *)s);
+}
+
+
+int
+mg_strncasecmp(const char *s1, const char *s2, size_t len)
+{
+ int diff = 0;
+
+ if (len > 0) {
+ do {
+ diff = lowercase(s1++) - lowercase(s2++);
+ } while (diff == 0 && s1[-1] != '\0' && --len > 0);
+ }
+
+ return diff;
+}
+
+
+int
+mg_strcasecmp(const char *s1, const char *s2)
+{
+ int diff;
+
+ do {
+ diff = lowercase(s1++) - lowercase(s2++);
+ } while (diff == 0 && s1[-1] != '\0');
+
+ return diff;
+}
+
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+ char *p;
+
+ if ((p = (char *)mg_malloc(len + 1)) != NULL) {
+ mg_strlcpy(p, ptr, len + 1);
+ }
+
+ return p;
+}
+
+
+static char *
+mg_strdup(const char *str)
+{
+ return mg_strndup(str, strlen(str));
+}
+
+
+static const char *
+mg_strcasestr(const char *big_str, const char *small_str)
+{
+ size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
+
+ if (big_len >= small_len) {
+ for (i = 0; i <= (big_len - small_len); i++) {
+ if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
+ return big_str + i;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Return null terminated string of given maximum length.
+ * Report errors if length is exceeded. */
+static void
+mg_vsnprintf(const struct mg_connection *conn,
+ int *truncated,
+ char *buf,
+ size_t buflen,
+ const char *fmt,
+ va_list ap)
+{
+ int n, ok;
+
+ if (buflen == 0) {
+ return;
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+/* Using fmt as a non-literal is intended here, since it is mostly called
+ * indirectly by mg_snprintf */
+#endif
+
+ n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
+ ok = (n >= 0) && ((size_t)n < buflen);
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+ if (ok) {
+ if (truncated) {
+ *truncated = 0;
+ }
+ } else {
+ if (truncated) {
+ *truncated = 1;
+ }
+ mg_cry(conn,
+ "truncating vsnprintf buffer: [%.*s]",
+ (int)((buflen > 200) ? 200 : (buflen - 1)),
+ buf);
+ n = (int)buflen - 1;
+ }
+ buf[n] = '\0';
+}
+
+
+static void
+mg_snprintf(const struct mg_connection *conn,
+ int *truncated,
+ char *buf,
+ size_t buflen,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
+ va_end(ap);
+}
+
+
+static int
+get_option_index(const char *name)
+{
+ int i;
+
+ for (i = 0; config_options[i].name != NULL; i++) {
+ if (strcmp(config_options[i].name, name) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+const char *
+mg_get_option(const struct mg_context *ctx, const char *name)
+{
+ int i;
+ if ((i = get_option_index(name)) == -1) {
+ return NULL;
+ } else if (!ctx || ctx->config[i] == NULL) {
+ return "";
+ } else {
+ return ctx->config[i];
+ }
+}
+
+
+struct mg_context *
+mg_get_context(const struct mg_connection *conn)
+{
+ return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx);
+}
+
+
+void *
+mg_get_user_data(const struct mg_context *ctx)
+{
+ return (ctx == NULL) ? NULL : ctx->user_data;
+}
+
+
+void
+mg_set_user_connection_data(struct mg_connection *conn, void *data)
+{
+ if (conn != NULL) {
+ conn->request_info.conn_data = data;
+ }
+}
+
+
+void *
+mg_get_user_connection_data(const struct mg_connection *conn)
+{
+ if (conn != NULL) {
+ return conn->request_info.conn_data;
+ }
+ return NULL;
+}
+
+
+size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
+{
+ size_t i;
+ if (!ctx) {
+ return 0;
+ }
+ for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
+ ssl[i] = ctx->listening_sockets[i].is_ssl;
+ ports[i] = ctx->listening_ports[i];
+ }
+ return i;
+}
+
+
+int
+mg_get_server_ports(const struct mg_context *ctx,
+ int size,
+ struct mg_server_ports *ports)
+{
+ int i, cnt = 0;
+
+ if (size <= 0) {
+ return -1;
+ }
+ memset(ports, 0, sizeof(*ports) * (size_t)size);
+ if (!ctx) {
+ return -1;
+ }
+ if (!ctx->listening_sockets || !ctx->listening_ports) {
+ return -1;
+ }
+
+ for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
+
+ ports[cnt].port = ctx->listening_ports[i];
+ ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
+ ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
+
+ if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
+ /* IPv4 */
+ ports[cnt].protocol = 1;
+ cnt++;
+ } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
+ /* IPv6 */
+ ports[cnt].protocol = 3;
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+
+static void
+sockaddr_to_string(char *buf, size_t len, const union usa *usa)
+{
+ buf[0] = '\0';
+
+ if (!usa) {
+ return;
+ }
+
+ if (usa->sa.sa_family == AF_INET) {
+ getnameinfo(&usa->sa,
+ sizeof(usa->sin),
+ buf,
+ (unsigned)len,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ }
+#if defined(USE_IPV6)
+ else if (usa->sa.sa_family == AF_INET6) {
+ getnameinfo(&usa->sa,
+ sizeof(usa->sin6),
+ buf,
+ (unsigned)len,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ }
+#endif
+}
+
+
+/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
+ * included in all responses other than 100, 101, 5xx. */
+static void
+gmt_time_string(char *buf, size_t buf_len, time_t *t)
+{
+ struct tm *tm;
+
+ tm = ((t != NULL) ? gmtime(t) : NULL);
+ if (tm != NULL) {
+ strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
+ } else {
+ mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
+ buf[buf_len - 1] = '\0';
+ }
+}
+
+
+/* difftime for struct timespec. Return value is in seconds. */
+static double
+mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
+{
+ return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
+ + (double)(ts_now->tv_sec - ts_before->tv_sec);
+}
+
+
+/* Print error message to the opened error log stream. */
+void
+mg_cry(const struct mg_connection *conn, const char *fmt, ...)
+{
+ char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
+ va_list ap;
+ struct file fi;
+ time_t timestamp;
+
+ va_start(ap, fmt);
+ IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+
+ if (!conn) {
+ puts(buf);
+ return;
+ }
+
+ /* Do not lock when getting the callback value, here and below.
+ * I suppose this is fine, since function cannot disappear in the
+ * same way string option can. */
+ if ((conn->ctx->callbacks.log_message == NULL)
+ || (conn->ctx->callbacks.log_message(conn, buf) == 0)) {
+
+ if (conn->ctx->config[ERROR_LOG_FILE] != NULL) {
+ if (mg_fopen(conn, conn->ctx->config[ERROR_LOG_FILE], "a+", &fi)
+ == 0) {
+ fi.fp = NULL;
+ }
+ } else {
+ fi.fp = NULL;
+ }
+
+ if (fi.fp != NULL) {
+ flockfile(fi.fp);
+ timestamp = time(NULL);
+
+ sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+ fprintf(fi.fp,
+ "[%010lu] [error] [client %s] ",
+ (unsigned long)timestamp,
+ src_addr);
+
+ if (conn->request_info.request_method != NULL) {
+ fprintf(fi.fp,
+ "%s %s: ",
+ conn->request_info.request_method,
+ conn->request_info.request_uri);
+ }
+
+ fprintf(fi.fp, "%s", buf);
+ fputc('\n', fi.fp);
+ fflush(fi.fp);
+ funlockfile(fi.fp);
+ mg_fclose(&fi);
+ }
+ }
+}
+
+
+/* Return fake connection structure. Used for logging, if connection
+ * is not applicable at the moment of logging. */
+static struct mg_connection *
+fc(struct mg_context *ctx)
+{
+ static struct mg_connection fake_connection;
+ fake_connection.ctx = ctx;
+ return &fake_connection;
+}
+
+
+const char *
+mg_version(void)
+{
+ return CIVETWEB_VERSION;
+}
+
+
+const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *conn)
+{
+ if (!conn) {
+ return NULL;
+ }
+ return &conn->request_info;
+}
+
+
+/* Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the delimiter and following whitespaces.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ * Delimiters can be quoted with quotechar. */
+static char *
+skip_quoted(char **buf,
+ const char *delimiters,
+ const char *whitespace,
+ char quotechar)
+{
+ char *p, *begin_word, *end_word, *end_whitespace;
+
+ begin_word = *buf;
+ end_word = begin_word + strcspn(begin_word, delimiters);
+
+ /* Check for quotechar */
+ if (end_word > begin_word) {
+ p = end_word - 1;
+ while (*p == quotechar) {
+ /* While the delimiter is quoted, look for the next delimiter. */
+ /* This happens, e.g., in calls from parse_auth_header,
+ * if the user name contains a " character. */
+
+ /* If there is anything beyond end_word, copy it. */
+ if (*end_word != '\0') {
+ size_t end_off = strcspn(end_word + 1, delimiters);
+ memmove(p, end_word, end_off + 1);
+ p += end_off; /* p must correspond to end_word - 1 */
+ end_word += end_off + 1;
+ } else {
+ *p = '\0';
+ break;
+ }
+ }
+ for (p++; p < end_word; p++) {
+ *p = '\0';
+ }
+ }
+
+ if (*end_word == '\0') {
+ *buf = end_word;
+ } else {
+ end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
+
+ for (p = end_word; p < end_whitespace; p++) {
+ *p = '\0';
+ }
+
+ *buf = end_whitespace;
+ }
+
+ return begin_word;
+}
+
+
+/* Simplified version of skip_quoted without quote char
+ * and whitespace == delimiters */
+static char *
+skip(char **buf, const char *delimiters)
+{
+ return skip_quoted(buf, delimiters, delimiters, 0);
+}
+
+
+/* Return HTTP header value, or NULL if not found. */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+ int i;
+ if (ri) {
+ for (i = 0; i < ri->num_headers; i++) {
+ if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
+ return ri->http_headers[i].value;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+ if (!conn) {
+ return NULL;
+ }
+
+ return get_header(&conn->request_info, name);
+}
+
+
+/* A helper function for traversing a comma separated list of values.
+ * It returns a list pointer shifted to the next value, or NULL if the end
+ * of the list found.
+ * Value is stored in val vector. If value has form "x=y", then eq_val
+ * vector is initialized to point to the "y" part, and val vector length
+ * is adjusted to point only to "x". */
+static const char *
+next_option(const char *list, struct vec *val, struct vec *eq_val)
+{
+ int end;
+
+reparse:
+ if (val == NULL || list == NULL || *list == '\0') {
+ /* End of the list */
+ list = NULL;
+ } else {
+ /* Skip over leading LWS */
+ while (*list == ' ' || *list == '\t')
+ list++;
+
+ val->ptr = list;
+ if ((list = strchr(val->ptr, ',')) != NULL) {
+ /* Comma found. Store length and shift the list ptr */
+ val->len = ((size_t)(list - val->ptr));
+ list++;
+ } else {
+ /* This value is the last one */
+ list = val->ptr + strlen(val->ptr);
+ val->len = ((size_t)(list - val->ptr));
+ }
+
+ /* Adjust length for trailing LWS */
+ end = (int)val->len - 1;
+ while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t'))
+ end--;
+ val->len = (size_t)(end + 1);
+
+ if (val->len == 0) {
+ /* Ignore any empty entries. */
+ goto reparse;
+ }
+
+ if (eq_val != NULL) {
+ /* Value has form "x=y", adjust pointers and lengths
+ * so that val points to "x", and eq_val points to "y". */
+ eq_val->len = 0;
+ eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
+ if (eq_val->ptr != NULL) {
+ eq_val->ptr++; /* Skip over '=' character */
+ eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
+ val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
+ }
+ }
+ }
+
+ return list;
+}
+
+/* A helper function for checking if a comma separated list of values contains
+ * the given option (case insensitvely).
+ * 'header' can be NULL, in which case false is returned. */
+static int
+header_has_option(const char *header, const char *option)
+{
+ struct vec opt_vec;
+ struct vec eq_vec;
+
+ assert(option != NULL);
+ assert(option[0] != '\0');
+
+ while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
+ if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Perform case-insensitive match of string against pattern */
+static int
+match_prefix(const char *pattern, size_t pattern_len, const char *str)
+{
+ const char *or_str;
+ size_t i;
+ int j, len, res;
+
+ if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) {
+ res = match_prefix(pattern, (size_t)(or_str - pattern), str);
+ return res > 0 ? res : match_prefix(or_str + 1,
+ (size_t)((pattern + pattern_len)
+ - (or_str + 1)),
+ str);
+ }
+
+ for (i = 0, j = 0; i < pattern_len; i++, j++) {
+ if (pattern[i] == '?' && str[j] != '\0') {
+ continue;
+ } else if (pattern[i] == '$') {
+ return str[j] == '\0' ? j : -1;
+ } else if (pattern[i] == '*') {
+ i++;
+ if (pattern[i] == '*') {
+ i++;
+ len = (int)strlen(str + j);
+ } else {
+ len = (int)strcspn(str + j, "/");
+ }
+ if (i == pattern_len) {
+ return j + len;
+ }
+ do {
+ res = match_prefix(pattern + i, pattern_len - i, str + j + len);
+ } while (res == -1 && len-- > 0);
+ return res == -1 ? -1 : j + res + len;
+ } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
+ return -1;
+ }
+ }
+ return j;
+}
+
+
+/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
+ * This function must tolerate situations when connection info is not
+ * set up, for example if request parsing failed. */
+static int
+should_keep_alive(const struct mg_connection *conn)
+{
+ if (conn != NULL) {
+ const char *http_version = conn->request_info.http_version;
+ const char *header = mg_get_header(conn, "Connection");
+ if (conn->must_close || conn->internal_error || conn->status_code == 401
+ || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0
+ || (header != NULL && !header_has_option(header, "keep-alive"))
+ || (header == NULL && http_version
+ && 0 != strcmp(http_version, "1.1"))) {
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+should_decode_url(const struct mg_connection *conn)
+{
+ if (!conn || !conn->ctx) {
+ return 0;
+ }
+
+ return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0);
+}
+
+
+static const char *
+suggest_connection_header(const struct mg_connection *conn)
+{
+ return should_keep_alive(conn) ? "keep-alive" : "close";
+}
+
+
+static int
+send_no_cache_header(struct mg_connection *conn)
+{
+ /* Send all current and obsolete cache opt-out directives. */
+ return mg_printf(conn,
+ "Cache-Control: no-cache, no-store, "
+ "must-revalidate, private, max-age=0\r\n"
+ "Pragma: no-cache\r\n"
+ "Expires: 0\r\n");
+}
+
+
+static int
+send_static_cache_header(struct mg_connection *conn)
+{
+#if !defined(NO_CACHING)
+ /* Read the server config to check how long a file may be cached.
+ * The configuration is in seconds. */
+ int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]);
+ if (max_age <= 0) {
+ /* 0 means "do not cache". All values <0 are reserved
+ * and may be used differently in the future. */
+ /* If a file should not be cached, do not only send
+ * max-age=0, but also pragmas and Expires headers. */
+ return send_no_cache_header(conn);
+ }
+
+ /* Use "Cache-Control: max-age" instead of "Expires" header.
+ * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
+ /* See also https://www.mnot.net/cache_docs/ */
+ /* According to RFC 2616, Section 14.21, caching times should not exceed
+ * one year. A year with 365 days corresponds to 31536000 seconds, a leap
+ * year to 31622400 seconds. For the moment, we just send whatever has
+ * been configured, still the behavior for >1 year should be considered
+ * as undefined. */
+ return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age);
+#else /* NO_CACHING */
+ return send_no_cache_header(conn);
+#endif /* !NO_CACHING */
+}
+
+
+static void handle_file_based_request(struct mg_connection *conn,
+ const char *path,
+ struct file *filep);
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep);
+
+
+const char *
+mg_get_response_code_text(struct mg_connection *conn, int response_code)
+{
+ /* See IANA HTTP status code assignment:
+ * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+ */
+
+ switch (response_code) {
+ /* RFC2616 Section 10.1 - Informational 1xx */
+ case 100:
+ return "Continue"; /* RFC2616 Section 10.1.1 */
+ case 101:
+ return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
+ case 102:
+ return "Processing"; /* RFC2518 Section 10.1 */
+
+ /* RFC2616 Section 10.2 - Successful 2xx */
+ case 200:
+ return "OK"; /* RFC2616 Section 10.2.1 */
+ case 201:
+ return "Created"; /* RFC2616 Section 10.2.2 */
+ case 202:
+ return "Accepted"; /* RFC2616 Section 10.2.3 */
+ case 203:
+ return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
+ case 204:
+ return "No Content"; /* RFC2616 Section 10.2.5 */
+ case 205:
+ return "Reset Content"; /* RFC2616 Section 10.2.6 */
+ case 206:
+ return "Partial Content"; /* RFC2616 Section 10.2.7 */
+ case 207:
+ return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */
+ case 208:
+ return "Already Reported"; /* RFC5842 Section 7.1 */
+
+ case 226:
+ return "IM used"; /* RFC3229 Section 10.4.1 */
+
+ /* RFC2616 Section 10.3 - Redirection 3xx */
+ case 300:
+ return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
+ case 301:
+ return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
+ case 302:
+ return "Found"; /* RFC2616 Section 10.3.3 */
+ case 303:
+ return "See Other"; /* RFC2616 Section 10.3.4 */
+ case 304:
+ return "Not Modified"; /* RFC2616 Section 10.3.5 */
+ case 305:
+ return "Use Proxy"; /* RFC2616 Section 10.3.6 */
+ case 307:
+ return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
+ case 308:
+ return "Permanent Redirect"; /* RFC7238 Section 3 */
+
+ /* RFC2616 Section 10.4 - Client Error 4xx */
+ case 400:
+ return "Bad Request"; /* RFC2616 Section 10.4.1 */
+ case 401:
+ return "Unauthorized"; /* RFC2616 Section 10.4.2 */
+ case 402:
+ return "Payment Required"; /* RFC2616 Section 10.4.3 */
+ case 403:
+ return "Forbidden"; /* RFC2616 Section 10.4.4 */
+ case 404:
+ return "Not Found"; /* RFC2616 Section 10.4.5 */
+ case 405:
+ return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
+ case 406:
+ return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
+ case 407:
+ return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
+ case 408:
+ return "Request Time-out"; /* RFC2616 Section 10.4.9 */
+ case 409:
+ return "Conflict"; /* RFC2616 Section 10.4.10 */
+ case 410:
+ return "Gone"; /* RFC2616 Section 10.4.11 */
+ case 411:
+ return "Length Required"; /* RFC2616 Section 10.4.12 */
+ case 412:
+ return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
+ case 413:
+ return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
+ case 414:
+ return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
+ case 415:
+ return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
+ case 416:
+ return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */
+ case 417:
+ return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
+
+ case 421:
+ return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
+ case 422:
+ return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
+ * Section 11.2 */
+ case 423:
+ return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
+ case 424:
+ return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
+ * Section 11.4 */
+
+ case 426:
+ return "Upgrade Required"; /* RFC 2817 Section 4 */
+
+ case 428:
+ return "Precondition Required"; /* RFC 6585, Section 3 */
+ case 429:
+ return "Too Many Requests"; /* RFC 6585, Section 4 */
+
+ case 431:
+ return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
+
+ case 451:
+ return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
+ * Section 3 */
+
+ /* RFC2616 Section 10.5 - Server Error 5xx */
+ case 500:
+ return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
+ case 501:
+ return "Not Implemented"; /* RFC2616 Section 10.5.2 */
+ case 502:
+ return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
+ case 503:
+ return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
+ case 504:
+ return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
+ case 505:
+ return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
+ case 506:
+ return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
+ case 507:
+ return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
+ * Section 11.5 */
+ case 508:
+ return "Loop Detected"; /* RFC5842 Section 7.1 */
+
+ case 510:
+ return "Not Extended"; /* RFC 2774, Section 7 */
+ case 511:
+ return "Network Authentication Required"; /* RFC 6585, Section 6 */
+
+ /* Other status codes, not shown in the IANA HTTP status code assignment.
+ * E.g., "de facto" standards due to common use, ... */
+ case 418:
+ return "I am a teapot"; /* RFC2324 Section 2.3.2 */
+ case 419:
+ return "Authentication Timeout"; /* common use */
+ case 420:
+ return "Enhance Your Calm"; /* common use */
+ case 440:
+ return "Login Timeout"; /* common use */
+ case 509:
+ return "Bandwidth Limit Exceeded"; /* common use */
+
+ default:
+ /* This error code is unknown. This should not happen. */
+ if (conn) {
+ mg_cry(conn, "Unknown HTTP response code: %u", response_code);
+ }
+
+ /* Return at least a category according to RFC 2616 Section 10. */
+ if (response_code >= 100 && response_code < 200) {
+ /* Unknown informational status code */
+ return "Information";
+ }
+ if (response_code >= 200 && response_code < 300) {
+ /* Unknown success code */
+ return "Success";
+ }
+ if (response_code >= 300 && response_code < 400) {
+ /* Unknown redirection code */
+ return "Redirection";
+ }
+ if (response_code >= 400 && response_code < 500) {
+ /* Unknown request error code */
+ return "Client Error";
+ }
+ if (response_code >= 500 && response_code < 600) {
+ /* Unknown server error code */
+ return "Server Error";
+ }
+
+ /* Response code not even within reasonable range */
+ return "";
+ }
+}
+
+
+static void send_http_error(struct mg_connection *,
+ int,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(3, 4);
+
+static void
+send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
+{
+ char buf[MG_BUF_LEN];
+ va_list ap;
+ int len, i, page_handler_found, scope, truncated;
+ char date[64];
+ time_t curtime = time(NULL);
+ const char *error_handler = NULL;
+ struct file error_page_file = STRUCT_FILE_INITIALIZER;
+ const char *error_page_file_ext, *tstr;
+
+ const char *status_text = mg_get_response_code_text(conn, status);
+
+ if (conn == NULL) {
+ return;
+ }
+
+ conn->status_code = status;
+ if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL
+ || conn->ctx->callbacks.http_error(conn, status)) {
+ if (!conn->in_error_handler) {
+ /* Send user defined error pages, if defined */
+ error_handler = conn->ctx->config[ERROR_PAGES];
+ error_page_file_ext = conn->ctx->config[INDEX_FILES];
+ page_handler_found = 0;
+ if (error_handler != NULL) {
+ for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
+ switch (scope) {
+ case 1: /* Handler for specific error, e.g. 404 error */
+ mg_snprintf(conn,
+ &truncated,
+ buf,
+ sizeof(buf) - 32,
+ "%serror%03u.",
+ error_handler,
+ status);
+ break;
+ case 2: /* Handler for error group, e.g., 5xx error handler
+ * for all server errors (500-599) */
+ mg_snprintf(conn,
+ &truncated,
+ buf,
+ sizeof(buf) - 32,
+ "%serror%01uxx.",
+ error_handler,
+ status / 100);
+ break;
+ default: /* Handler for all errors */
+ mg_snprintf(conn,
+ &truncated,
+ buf,
+ sizeof(buf) - 32,
+ "%serror.",
+ error_handler);
+ break;
+ }
+
+ /* String truncation in buf may only occur if error_handler
+ * is too long. This string is from the config, not from a
+ * client. */
+ (void)truncated;
+
+ len = (int)strlen(buf);
+
+ tstr = strchr(error_page_file_ext, '.');
+
+ while (tstr) {
+ for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ',';
+ i++)
+ buf[len + i - 1] = tstr[i];
+ buf[len + i - 1] = 0;
+ if (mg_stat(conn, buf, &error_page_file)) {
+ page_handler_found = 1;
+ break;
+ }
+ tstr = strchr(tstr + i, '.');
+ }
+ }
+ }
+
+ if (page_handler_found) {
+ conn->in_error_handler = 1;
+ handle_file_based_request(conn, buf, &error_page_file);
+ conn->in_error_handler = 0;
+ return;
+ }
+ }
+
+ /* No custom error page. Send default error page. */
+ gmt_time_string(date, sizeof(date), &curtime);
+
+ conn->must_close = 1;
+ mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
+ send_no_cache_header(conn);
+ mg_printf(conn,
+ "Date: %s\r\n"
+ "Connection: close\r\n\r\n",
+ date);
+
+ /* Errors 1xx, 204 and 304 MUST NOT send a body */
+ if (status > 199 && status != 204 && status != 304) {
+
+ mg_printf(conn, "Error %d: %s\n", status, status_text);
+
+ if (fmt != NULL) {
+ va_start(ap, fmt);
+ mg_vsnprintf(conn, NULL, buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ mg_write(conn, buf, strlen(buf));
+ DEBUG_TRACE("Error %i - [%s]", status, buf);
+ }
+
+ } else {
+ /* No body allowed. Close the connection. */
+ DEBUG_TRACE("Error %i", status);
+ }
+ }
+}
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static int
+pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
+{
+ (void)unused;
+ *mutex = CreateMutex(NULL, FALSE, NULL);
+ return *mutex == NULL ? -1 : 0;
+}
+
+
+static int
+pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+ return CloseHandle(*mutex) == 0 ? -1 : 0;
+}
+
+
+static int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+ return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1;
+}
+
+
+#ifdef ENABLE_UNUSED_PTHREAD_FUNCTIONS
+static int
+pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+ switch (WaitForSingleObject(*mutex, 0)) {
+ case WAIT_OBJECT_0:
+ return 0;
+ case WAIT_TIMEOUT:
+ return -2; /* EBUSY */
+ }
+ return -1;
+}
+#endif
+
+
+static int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ return ReleaseMutex(*mutex) == 0 ? -1 : 0;
+}
+
+
+#ifndef WIN_PTHREADS_TIME_H
+static int
+clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+ FILETIME ft;
+ ULARGE_INTEGER li;
+ BOOL ok = FALSE;
+ double d;
+ static double perfcnt_per_sec = 0.0;
+
+ if (tp) {
+ memset(tp, 0, sizeof(*tp));
+ if (clk_id == CLOCK_REALTIME) {
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
+ tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+ tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+ ok = TRUE;
+ } else if (clk_id == CLOCK_MONOTONIC) {
+ if (perfcnt_per_sec == 0.0) {
+ QueryPerformanceFrequency((LARGE_INTEGER *)&li);
+ perfcnt_per_sec = 1.0 / li.QuadPart;
+ }
+ if (perfcnt_per_sec != 0.0) {
+ QueryPerformanceCounter((LARGE_INTEGER *)&li);
+ d = li.QuadPart * perfcnt_per_sec;
+ tp->tv_sec = (time_t)d;
+ d -= tp->tv_sec;
+ tp->tv_nsec = (long)(d * 1.0E9);
+ ok = TRUE;
+ }
+ }
+ }
+
+ return ok ? 0 : -1;
+}
+#endif
+
+
+static int
+pthread_cond_init(pthread_cond_t *cv, const void *unused)
+{
+ (void)unused;
+ InitializeCriticalSection(&cv->threadIdSec);
+ cv->waitingthreadcount = 0;
+ cv->waitingthreadhdls =
+ (pthread_t *)mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t));
+ return (cv->waitingthreadhdls != NULL) ? 0 : -1;
+}
+
+
+static int
+pthread_cond_timedwait(pthread_cond_t *cv,
+ pthread_mutex_t *mutex,
+ const struct timespec *abstime)
+{
+ struct mg_workerTLS *tls =
+ (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+ int ok;
+ struct timespec tsnow;
+ int64_t nsnow, nswaitabs, nswaitrel;
+ DWORD mswaitrel;
+
+ EnterCriticalSection(&cv->threadIdSec);
+ assert(cv->waitingthreadcount < MAX_WORKER_THREADS);
+ cv->waitingthreadhdls[cv->waitingthreadcount] =
+ tls->pthread_cond_helper_mutex;
+ cv->waitingthreadcount++;
+ LeaveCriticalSection(&cv->threadIdSec);
+
+ if (abstime) {
+ clock_gettime(CLOCK_REALTIME, &tsnow);
+ nsnow = (((int64_t)tsnow.tv_sec) * 1000000000) + tsnow.tv_nsec;
+ nswaitabs =
+ (((int64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
+ nswaitrel = nswaitabs - nsnow;
+ if (nswaitrel < 0) {
+ nswaitrel = 0;
+ }
+ mswaitrel = (DWORD)(nswaitrel / 1000000);
+ } else {
+ mswaitrel = INFINITE;
+ }
+
+ pthread_mutex_unlock(mutex);
+ ok = (WAIT_OBJECT_0
+ == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
+ pthread_mutex_lock(mutex);
+
+ return ok ? 0 : -1;
+}
+
+
+static int
+pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
+{
+ return pthread_cond_timedwait(cv, mutex, NULL);
+}
+
+
+static int
+pthread_cond_signal(pthread_cond_t *cv)
+{
+ int i;
+ HANDLE wkup = NULL;
+ BOOL ok = FALSE;
+
+ EnterCriticalSection(&cv->threadIdSec);
+ if (cv->waitingthreadcount) {
+ wkup = cv->waitingthreadhdls[0];
+ ok = SetEvent(wkup);
+
+ for (i = 1; i < cv->waitingthreadcount; i++) {
+ cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i];
+ }
+ cv->waitingthreadcount--;
+
+ assert(ok);
+ }
+ LeaveCriticalSection(&cv->threadIdSec);
+
+ return ok ? 0 : 1;
+}
+
+
+static int
+pthread_cond_broadcast(pthread_cond_t *cv)
+{
+ EnterCriticalSection(&cv->threadIdSec);
+ while (cv->waitingthreadcount) {
+ pthread_cond_signal(cv);
+ }
+ LeaveCriticalSection(&cv->threadIdSec);
+
+ return 0;
+}
+
+
+static int
+pthread_cond_destroy(pthread_cond_t *cv)
+{
+ EnterCriticalSection(&cv->threadIdSec);
+ assert(cv->waitingthreadcount == 0);
+ mg_free(cv->waitingthreadhdls);
+ cv->waitingthreadhdls = 0;
+ LeaveCriticalSection(&cv->threadIdSec);
+ DeleteCriticalSection(&cv->threadIdSec);
+
+ return 0;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+
+/* For Windows, change all slashes to backslashes in path names. */
+static void
+change_slashes_to_backslashes(char *path)
+{
+ int i;
+
+ for (i = 0; path[i] != '\0'; i++) {
+ if (path[i] == '/') {
+ path[i] = '\\';
+ }
+
+ /* remove double backslash (check i > 0 to preserve UNC paths,
+ * like \\server\file.txt) */
+ if ((path[i] == '\\') && (i > 0)) {
+ while (path[i + 1] == '\\' || path[i + 1] == '/') {
+ (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
+ }
+ }
+ }
+}
+
+
+static int
+mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
+{
+ int diff;
+
+ do {
+ diff = tolower(*s1) - tolower(*s2);
+ s1++;
+ s2++;
+ } while (diff == 0 && s1[-1] != '\0');
+
+ return diff;
+}
+
+
+/* Encode 'path' which is assumed UTF-8 string, into UNICODE string.
+ * wbuf and wbuf_len is a target buffer and its length. */
+static void
+path_to_unicode(const struct mg_connection *conn,
+ const char *path,
+ wchar_t *wbuf,
+ size_t wbuf_len)
+{
+ char buf[PATH_MAX], buf2[PATH_MAX];
+ wchar_t wbuf2[MAX_PATH + 1];
+ DWORD long_len, err;
+ int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp;
+
+ mg_strlcpy(buf, path, sizeof(buf));
+ change_slashes_to_backslashes(buf);
+
+ /* Convert to Unicode and back. If doubly-converted string does not
+ * match the original, something is fishy, reject. */
+ memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
+ WideCharToMultiByte(
+ CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);
+ if (strcmp(buf, buf2) != 0) {
+ wbuf[0] = L'\0';
+ }
+
+ /* TODO: Add a configuration to switch between case sensitive and
+ * case insensitive URIs for Windows server. */
+ /*
+ if (conn) {
+ if (conn->ctx->config[WINDOWS_CASE_SENSITIVE]) {
+ fcompare = wcscmp;
+ }
+ }
+ */
+ (void)conn; /* conn is currently unused */
+
+ /* Only accept a full file path, not a Windows short (8.3) path. */
+ memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t));
+ long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1);
+ if (long_len == 0) {
+ err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND) {
+ /* File does not exist. This is not always a problem here. */
+ return;
+ }
+ }
+ if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
+ /* Short name is used. */
+ wbuf[0] = L'\0';
+ }
+}
+
+
+#if defined(_WIN32_WCE)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static time_t
+time(time_t *ptime)
+{
+ time_t t;
+ SYSTEMTIME st;
+ FILETIME ft;
+
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &ft);
+ t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+ if (ptime != NULL) {
+ *ptime = t;
+ }
+
+ return t;
+}
+
+
+static struct tm *
+localtime(const time_t *ptime, struct tm *ptm)
+{
+ int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+ FILETIME ft, lft;
+ SYSTEMTIME st;
+ TIME_ZONE_INFORMATION tzinfo;
+
+ if (ptm == NULL) {
+ return NULL;
+ }
+
+ *(int64_t *)&ft = t;
+ FileTimeToLocalFileTime(&ft, &lft);
+ FileTimeToSystemTime(&lft, &st);
+ ptm->tm_year = st.wYear - 1900;
+ ptm->tm_mon = st.wMonth - 1;
+ ptm->tm_wday = st.wDayOfWeek;
+ ptm->tm_mday = st.wDay;
+ ptm->tm_hour = st.wHour;
+ ptm->tm_min = st.wMinute;
+ ptm->tm_sec = st.wSecond;
+ ptm->tm_yday = 0; /* hope nobody uses this */
+ ptm->tm_isdst =
+ GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
+
+ return ptm;
+}
+
+
+static struct tm *
+gmtime(const time_t *ptime, struct tm *ptm)
+{
+ /* FIXME(lsm): fix this. */
+ return localtime(ptime, ptm);
+}
+
+
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+ (void)mg_snprintf(NULL, dst, dst_size, "implement strftime() for WinCE");
+ return 0;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif
+
+
+/* Windows happily opens files with some garbage at the end of file name.
+ * For example, fopen("a.cgi ", "r") on Windows successfully opens
+ * "a.cgi", despite one would expect an error back.
+ * This function returns non-0 if path ends with some garbage. */
+static int
+path_cannot_disclose_cgi(const char *path)
+{
+ static const char *allowed_last_characters = "_-";
+ int last = path[strlen(path) - 1];
+ return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
+}
+
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
+{
+ wchar_t wbuf[PATH_MAX];
+ WIN32_FILE_ATTRIBUTE_DATA info;
+ time_t creation_time;
+
+ if (!filep) {
+ return 0;
+ }
+ memset(filep, 0, sizeof(*filep));
+
+ if (conn && is_file_in_memory(conn, path, filep)) {
+ /* filep->is_directory = 0; filep->gzipped = 0; .. already done by
+ * memset */
+ filep->last_modified = time(NULL);
+ /* last_modified = now ... assumes the file may change during runtime,
+ * so every mg_fopen call may return different data */
+ /* last_modified = conn->ctx.start_time;
+ * May be used it the data does not change during runtime. This allows
+ * browser caching. Since we do not know, we have to assume the file
+ * in memory may change. */
+ return 1;
+ }
+
+ path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+ if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
+ filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
+ filep->last_modified =
+ SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
+ info.ftLastWriteTime.dwHighDateTime);
+
+ /* On Windows, the file creation time can be higher than the
+ * modification time, e.g. when a file is copied.
+ * Since the Last-Modified timestamp is used for caching
+ * it should be based on the most recent timestamp. */
+ creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
+ info.ftCreationTime.dwHighDateTime);
+ if (creation_time > filep->last_modified) {
+ filep->last_modified = creation_time;
+ }
+
+ filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+ /* If file name is fishy, reset the file structure and return
+ * error.
+ * Note it is important to reset, not just return the error, cause
+ * functions like is_file_opened() check the struct. */
+ if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
+ memset(filep, 0, sizeof(*filep));
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+mg_remove(const struct mg_connection *conn, const char *path)
+{
+ wchar_t wbuf[PATH_MAX];
+ path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+ return DeleteFileW(wbuf) ? 0 : -1;
+}
+
+
+static int
+mg_mkdir(const struct mg_connection *conn, const char *path, int mode)
+{
+ wchar_t wbuf[PATH_MAX];
+ (void)mode;
+ path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+ return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
+}
+
+
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+/* Implementation of POSIX opendir/closedir/readdir for Windows. */
+static DIR *
+mg_opendir(const struct mg_connection *conn, const char *name)
+{
+ DIR *dir = NULL;
+ wchar_t wpath[PATH_MAX];
+ DWORD attrs;
+
+ if (name == NULL) {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ } else {
+ path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath));
+ attrs = GetFileAttributesW(wpath);
+ if (attrs != 0xFFFFFFFF && ((attrs & FILE_ATTRIBUTE_DIRECTORY)
+ == FILE_ATTRIBUTE_DIRECTORY)) {
+ (void)wcscat(wpath, L"\\*");
+ dir->handle = FindFirstFileW(wpath, &dir->info);
+ dir->result.d_name[0] = '\0';
+ } else {
+ mg_free(dir);
+ dir = NULL;
+ }
+ }
+
+ return dir;
+}
+
+
+static int
+mg_closedir(DIR *dir)
+{
+ int result = 0;
+
+ if (dir != NULL) {
+ if (dir->handle != INVALID_HANDLE_VALUE)
+ result = FindClose(dir->handle) ? 0 : -1;
+
+ mg_free(dir);
+ } else {
+ result = -1;
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+
+ return result;
+}
+
+
+static struct dirent *
+mg_readdir(DIR *dir)
+{
+ struct dirent *result = 0;
+
+ if (dir) {
+ if (dir->handle != INVALID_HANDLE_VALUE) {
+ result = &dir->result;
+ (void)WideCharToMultiByte(CP_UTF8,
+ 0,
+ dir->info.cFileName,
+ -1,
+ result->d_name,
+ sizeof(result->d_name),
+ NULL,
+ NULL);
+
+ if (!FindNextFileW(dir->handle, &dir->info)) {
+ (void)FindClose(dir->handle);
+ dir->handle = INVALID_HANDLE_VALUE;
+ }
+
+ } else {
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+ } else {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+
+ return result;
+}
+
+
+#ifndef HAVE_POLL
+static int
+poll(struct pollfd *pfd, unsigned int n, int milliseconds)
+{
+ struct timeval tv;
+ fd_set set;
+ unsigned int i;
+ int result;
+ SOCKET maxfd = 0;
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = (milliseconds % 1000) * 1000;
+ FD_ZERO(&set);
+
+ for (i = 0; i < n; i++) {
+ FD_SET((SOCKET)pfd[i].fd, &set);
+ pfd[i].revents = 0;
+
+ if (pfd[i].fd > maxfd) {
+ maxfd = pfd[i].fd;
+ }
+ }
+
+ if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
+ for (i = 0; i < n; i++) {
+ if (FD_ISSET(pfd[i].fd, &set)) {
+ pfd[i].revents = POLLIN;
+ }
+ }
+ }
+
+ return result;
+}
+#endif /* HAVE_POLL */
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+
+static void
+set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */)
+{
+ (void)conn; /* Unused. */
+ (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
+}
+
+
+int
+mg_start_thread(mg_thread_func_t f, void *p)
+{
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+ /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
+ */
+ return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p)
+ == ((uintptr_t)(-1L)))
+ ? -1
+ : 0);
+#else
+ return (
+ (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L)))
+ ? -1
+ : 0);
+#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
+}
+
+
+/* Start a thread storing the thread context. */
+static int
+mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
+ void *p,
+ pthread_t *threadidptr)
+{
+ uintptr_t uip;
+ HANDLE threadhandle;
+ int result = -1;
+
+ uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL);
+ threadhandle = (HANDLE)uip;
+ if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) {
+ *threadidptr = threadhandle;
+ result = 0;
+ }
+
+ return result;
+}
+
+
+/* Wait for a thread to finish. */
+static int
+mg_join_thread(pthread_t threadid)
+{
+ int result;
+ DWORD dwevent;
+
+ result = -1;
+ dwevent = WaitForSingleObject(threadid, INFINITE);
+ if (dwevent == WAIT_FAILED) {
+ DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO);
+ } else {
+ if (dwevent == WAIT_OBJECT_0) {
+ CloseHandle(threadid);
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+#if !defined(NO_SSL_DL)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static HANDLE
+dlopen(const char *dll_name, int flags)
+{
+ wchar_t wbuf[PATH_MAX];
+ (void)flags;
+ path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf));
+ return LoadLibraryW(wbuf);
+}
+
+
+static int
+dlclose(void *handle)
+{
+ int result;
+
+ if (FreeLibrary((HMODULE)handle) != 0) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+
+ return result;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif
+
+
+#if !defined(NO_CGI)
+#define SIGKILL (0)
+
+static int
+kill(pid_t pid, int sig_num)
+{
+ (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
+ (void)CloseHandle((HANDLE)pid);
+ return 0;
+}
+
+
+static void
+trim_trailing_whitespaces(char *s)
+{
+ char *e = s + strlen(s) - 1;
+ while (e > s && isspace(*(unsigned char *)e)) {
+ *e-- = '\0';
+ }
+}
+
+
+static pid_t
+spawn_process(struct mg_connection *conn,
+ const char *prog,
+ char *envblk,
+ char *envp[],
+ int fdin[2],
+ int fdout[2],
+ int fderr[2],
+ const char *dir)
+{
+ HANDLE me;
+ char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
+ cmdline[PATH_MAX], buf[PATH_MAX];
+ int truncated;
+ struct file file = STRUCT_FILE_INITIALIZER;
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi = {0};
+
+ (void)envp;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+
+ me = GetCurrentProcess();
+ DuplicateHandle(me,
+ (HANDLE)_get_osfhandle(fdin[0]),
+ me,
+ &si.hStdInput,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS);
+ DuplicateHandle(me,
+ (HANDLE)_get_osfhandle(fdout[1]),
+ me,
+ &si.hStdOutput,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS);
+ DuplicateHandle(me,
+ (HANDLE)_get_osfhandle(fderr[1]),
+ me,
+ &si.hStdError,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS);
+
+ /* Mark handles that should not be inherited. See
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
+ */
+ SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]),
+ HANDLE_FLAG_INHERIT,
+ 0);
+ SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]),
+ HANDLE_FLAG_INHERIT,
+ 0);
+ SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]),
+ HANDLE_FLAG_INHERIT,
+ 0);
+
+ /* If CGI file is a script, try to read the interpreter line */
+ interp = conn->ctx->config[CGI_INTERPRETER];
+ if (interp == NULL) {
+ buf[0] = buf[1] = '\0';
+
+ /* Read the first line of the script into the buffer */
+ mg_snprintf(
+ conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
+
+ if (truncated) {
+ pi.hProcess = (pid_t)-1;
+ goto spawn_cleanup;
+ }
+
+ if (mg_fopen(conn, cmdline, "r", &file)) {
+ p = (char *)file.membuf;
+ mg_fgets(buf, sizeof(buf), &file, &p);
+ mg_fclose(&file);
+ buf[sizeof(buf) - 1] = '\0';
+ }
+
+ if (buf[0] == '#' && buf[1] == '!') {
+ trim_trailing_whitespaces(buf + 2);
+ } else {
+ buf[2] = '\0';
+ }
+ interp = buf + 2;
+ }
+
+ if (interp[0] != '\0') {
+ GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
+ interp = full_interp;
+ }
+ GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
+
+ if (interp[0] != '\0') {
+ mg_snprintf(conn,
+ &truncated,
+ cmdline,
+ sizeof(cmdline),
+ "\"%s\" \"%s\\%s\"",
+ interp,
+ full_dir,
+ prog);
+ } else {
+ mg_snprintf(conn,
+ &truncated,
+ cmdline,
+ sizeof(cmdline),
+ "\"%s\\%s\"",
+ full_dir,
+ prog);
+ }
+
+ if (truncated) {
+ pi.hProcess = (pid_t)-1;
+ goto spawn_cleanup;
+ }
+
+ DEBUG_TRACE("Running [%s]", cmdline);
+ if (CreateProcessA(NULL,
+ cmdline,
+ NULL,
+ NULL,
+ TRUE,
+ CREATE_NEW_PROCESS_GROUP,
+ envblk,
+ NULL,
+ &si,
+ &pi) == 0) {
+ mg_cry(
+ conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO);
+ pi.hProcess = (pid_t)-1;
+ /* goto spawn_cleanup; */
+ }
+
+spawn_cleanup:
+ (void)CloseHandle(si.hStdOutput);
+ (void)CloseHandle(si.hStdError);
+ (void)CloseHandle(si.hStdInput);
+ if (pi.hThread != NULL) {
+ (void)CloseHandle(pi.hThread);
+ }
+
+ return (pid_t)pi.hProcess;
+}
+#endif /* !NO_CGI */
+
+
+static int
+set_non_blocking_mode(SOCKET sock)
+{
+ unsigned long on = 1;
+ return ioctlsocket(sock, (long)FIONBIO, &on);
+}
+
+#else
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
+{
+ struct stat st;
+ if (!filep) {
+ return 0;
+ }
+ memset(filep, 0, sizeof(*filep));
+
+ if (conn && is_file_in_memory(conn, path, filep)) {
+ return 1;
+ }
+
+ if (0 == stat(path, &st)) {
+ filep->size = (uint64_t)(st.st_size);
+ filep->last_modified = st.st_mtime;
+ filep->is_directory = S_ISDIR(st.st_mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void
+set_close_on_exec(SOCKET fd, struct mg_connection *conn /* may be null */)
+{
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+ if (conn) {
+ mg_cry(conn,
+ "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+ }
+}
+
+
+int
+mg_start_thread(mg_thread_func_t func, void *param)
+{
+ pthread_t thread_id;
+ pthread_attr_t attr;
+ int result;
+
+ (void)pthread_attr_init(&attr);
+ (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+ /* Compile-time option to control stack size,
+ * e.g. -DUSE_STACK_SIZE=16384 */
+ (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
+#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
+
+ result = pthread_create(&thread_id, &attr, func, param);
+ pthread_attr_destroy(&attr);
+
+ return result;
+}
+
+
+/* Start a thread storing the thread context. */
+static int
+mg_start_thread_with_id(mg_thread_func_t func,
+ void *param,
+ pthread_t *threadidptr)
+{
+ pthread_t thread_id;
+ pthread_attr_t attr;
+ int result;
+
+ (void)pthread_attr_init(&attr);
+
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+ /* Compile-time option to control stack size,
+ * e.g. -DUSE_STACK_SIZE=16384 */
+ (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
+#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
+
+ result = pthread_create(&thread_id, &attr, func, param);
+ pthread_attr_destroy(&attr);
+ if ((result == 0) && (threadidptr != NULL)) {
+ *threadidptr = thread_id;
+ }
+ return result;
+}
+
+
+/* Wait for a thread to finish. */
+static int
+mg_join_thread(pthread_t threadid)
+{
+ int result;
+
+ result = pthread_join(threadid, NULL);
+ return result;
+}
+
+
+#ifndef NO_CGI
+static pid_t
+spawn_process(struct mg_connection *conn,
+ const char *prog,
+ char *envblk,
+ char *envp[],
+ int fdin[2],
+ int fdout[2],
+ int fderr[2],
+ const char *dir)
+{
+ pid_t pid;
+ const char *interp;
+
+ (void)envblk;
+
+ if (conn == NULL) {
+ return 0;
+ }
+
+ if ((pid = fork()) == -1) {
+ /* Parent */
+ send_http_error(conn,
+ 500,
+ "Error: Creating CGI process\nfork(): %s",
+ strerror(ERRNO));
+ } else if (pid == 0) {
+ /* Child */
+ if (chdir(dir) != 0) {
+ mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
+ } else if (dup2(fdin[0], 0) == -1) {
+ mg_cry(conn,
+ "%s: dup2(%d, 0): %s",
+ __func__,
+ fdin[0],
+ strerror(ERRNO));
+ } else if (dup2(fdout[1], 1) == -1) {
+ mg_cry(conn,
+ "%s: dup2(%d, 1): %s",
+ __func__,
+ fdout[1],
+ strerror(ERRNO));
+ } else if (dup2(fderr[1], 2) == -1) {
+ mg_cry(conn,
+ "%s: dup2(%d, 2): %s",
+ __func__,
+ fderr[1],
+ strerror(ERRNO));
+ } else {
+ /* Keep stderr and stdout in two different pipes.
+ * Stdout will be sent back to the client,
+ * stderr should go into a server error log. */
+ (void)close(fdin[0]);
+ (void)close(fdout[1]);
+ (void)close(fderr[1]);
+
+ /* Close write end fdin and read end fdout and fderr */
+ (void)close(fdin[1]);
+ (void)close(fdout[0]);
+ (void)close(fderr[0]);
+
+ /* After exec, all signal handlers are restored to their default
+ * values, with one exception of SIGCHLD. According to
+ * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will
+ * leave unchanged after exec if it was set to be ignored. Restore
+ * it to default action. */
+ signal(SIGCHLD, SIG_DFL);
+
+ interp = conn->ctx->config[CGI_INTERPRETER];
+ if (interp == NULL) {
+ (void)execle(prog, prog, NULL, envp);
+ mg_cry(conn,
+ "%s: execle(%s): %s",
+ __func__,
+ prog,
+ strerror(ERRNO));
+ } else {
+ (void)execle(interp, interp, prog, NULL, envp);
+ mg_cry(conn,
+ "%s: execle(%s %s): %s",
+ __func__,
+ interp,
+ prog,
+ strerror(ERRNO));
+ }
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ return pid;
+}
+#endif /* !NO_CGI */
+
+
+static int
+set_non_blocking_mode(SOCKET sock)
+{
+ int flags;
+
+ flags = fcntl(sock, F_GETFL, 0);
+ (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+ return 0;
+}
+#endif /* _WIN32 */
+/* End of initial operating system specific define block. */
+
+
+/* Get a random number (independent of C rand function) */
+static uint64_t
+get_random(void)
+{
+ static uint64_t lfsr = 0; /* Linear feedback shift register */
+ static uint64_t lcg = 0; /* Linear congruential generator */
+ struct timespec now;
+
+ memset(&now, 0, sizeof(now));
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if (lfsr == 0) {
+ /* lfsr will be only 0 if has not been initialized,
+ * so this code is called only once. */
+ lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec)
+ ^ ((uint64_t)(ptrdiff_t)&now) ^ (((uint64_t)time(NULL)) << 33);
+ lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec
+ + (uint64_t)(ptrdiff_t)&now;
+ } else {
+ /* Get the next step of both random number generators. */
+ lfsr = (lfsr >> 1)
+ | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
+ << 63);
+ lcg = lcg * 6364136223846793005 + 1442695040888963407;
+ }
+
+ /* Combining two pseudo-random number generators and a high resolution part
+ * of the current server time will make it hard (impossible?) to guess the
+ * next number. */
+ return (lfsr ^ lcg ^ (uint64_t)now.tv_nsec);
+}
+
+
+/* Write data to the IO channel - opened file descriptor, socket or SSL
+ * descriptor. Return number of bytes written. */
+static int
+push(struct mg_context *ctx,
+ FILE *fp,
+ SOCKET sock,
+ SSL *ssl,
+ const char *buf,
+ int len,
+ double timeout)
+{
+ struct timespec start, now;
+ int n, err;
+
+#ifdef _WIN32
+ typedef int len_t;
+#else
+ typedef size_t len_t;
+#endif
+
+ if (timeout > 0) {
+ memset(&start, 0, sizeof(start));
+ memset(&now, 0, sizeof(now));
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ }
+
+ if (ctx == NULL) {
+ return -1;
+ }
+
+#ifdef NO_SSL
+ if (ssl) {
+ return -1;
+ }
+#endif
+
+ do {
+
+#ifndef NO_SSL
+ if (ssl != NULL) {
+ n = SSL_write(ssl, buf, len);
+ if (n <= 0) {
+ err = SSL_get_error(ssl, n);
+ if ((err == 5 /* SSL_ERROR_SYSCALL */) && (n == -1)) {
+ err = ERRNO;
+ } else {
+ DEBUG_TRACE("SSL_write() failed, error %d", err);
+ return -1;
+ }
+ } else {
+ err = 0;
+ }
+ } else
+#endif
+ if (fp != NULL) {
+ n = (int)fwrite(buf, 1, (size_t)len, fp);
+ if (ferror(fp)) {
+ n = -1;
+ err = ERRNO;
+ } else {
+ err = 0;
+ }
+ } else {
+ n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
+ err = (n < 0) ? ERRNO : 0;
+ }
+
+ if (ctx->stop_flag) {
+ return -1;
+ }
+
+ if ((n > 0) || (n == 0 && len == 0)) {
+ /* some data has been read, or no data was requested */
+ return n;
+ }
+ if (n == 0) {
+ /* shutdown of the socket at client side */
+ return -1;
+ }
+ if (n < 0) {
+ /* socket error - check errno */
+ DEBUG_TRACE("send() failed, error %d", err);
+
+ /* TODO: error handling depending on the error code.
+ * These codes are different between Windows and Linux.
+ */
+ return -1;
+ }
+
+ /* This code is not reached in the moment.
+ * ==> Fix the TODOs above first. */
+
+ if (timeout > 0) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ }
+
+ } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout));
+
+ (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
+ used */
+
+ return -1;
+}
+
+
+static int64_t
+push_all(struct mg_context *ctx,
+ FILE *fp,
+ SOCKET sock,
+ SSL *ssl,
+ const char *buf,
+ int64_t len)
+{
+ double timeout = -1.0;
+ int64_t n, nwritten = 0;
+
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ if (ctx->config[REQUEST_TIMEOUT]) {
+ timeout = atoi(ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+ }
+
+ while (len > 0 && ctx->stop_flag == 0) {
+ n = push(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout);
+ if (n < 0) {
+ if (nwritten == 0) {
+ nwritten = n; /* Propagate the error */
+ }
+ break;
+ } else if (n == 0) {
+ break; /* No more data to write */
+ } else {
+ nwritten += n;
+ len -= n;
+ }
+ }
+
+ return nwritten;
+}
+
+
+/* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
+ * Return negative value on error, or number of bytes read on success. */
+static int
+pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
+{
+ int nread, err;
+ struct timespec start, now;
+
+#ifdef _WIN32
+ typedef int len_t;
+#else
+ typedef size_t len_t;
+#endif
+
+ if (timeout > 0) {
+ memset(&start, 0, sizeof(start));
+ memset(&now, 0, sizeof(now));
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ }
+
+ do {
+ if (fp != NULL) {
+ /* Use read() instead of fread(), because if we're reading from the
+ * CGI pipe, fread() may block until IO buffer is filled up. We
+ * cannot afford to block and must pass all read bytes immediately
+ * to the client. */
+ nread = (int)read(fileno(fp), buf, (size_t)len);
+ err = (nread < 0) ? ERRNO : 0;
+
+#ifndef NO_SSL
+ } else if (conn->ssl != NULL) {
+ nread = SSL_read(conn->ssl, buf, len);
+ if (nread <= 0) {
+ err = SSL_get_error(conn->ssl, nread);
+ if ((err == 5 /* SSL_ERROR_SYSCALL */) && (nread == -1)) {
+ err = ERRNO;
+ } else {
+ DEBUG_TRACE("SSL_read() failed, error %d", err);
+ return -1;
+ }
+ } else {
+ err = 0;
+ }
+#endif
+
+ } else {
+ nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
+ err = (nread < 0) ? ERRNO : 0;
+ }
+
+ if (conn->ctx->stop_flag) {
+ return -1;
+ }
+
+ if ((nread > 0) || (nread == 0 && len == 0)) {
+ /* some data has been read, or no data was requested */
+ return nread;
+ }
+ if (nread == 0) {
+ /* shutdown of the socket at client side */
+ return -1;
+ }
+ if (nread < 0) {
+/* socket error - check errno */
+#ifdef _WIN32
+ if (err == WSAEWOULDBLOCK) {
+ /* standard case if called from close_socket_gracefully */
+ return -1;
+ } else if (err == WSAETIMEDOUT) {
+ /* timeout is handled by the while loop */
+ } else {
+ DEBUG_TRACE("recv() failed, error %d", err);
+ return -1;
+ }
+#else
+ /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
+ * if the timeout is reached and if the socket was set to non-
+ * blocking in close_socket_gracefully, so we can not distinguish
+ * here. We have to wait for the timeout in both cases for now.
+ */
+ if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {
+ /* EAGAIN/EWOULDBLOCK:
+ * standard case if called from close_socket_gracefully
+ * => should return -1 */
+ /* or timeout occured
+ * => the code must stay in the while loop */
+
+ /* EINTR can be generated on a socket with a timeout set even
+ * when SA_RESTART is effective for all relevant signals
+ * (see signal(7)).
+ * => stay in the while loop */
+ } else {
+ DEBUG_TRACE("recv() failed, error %d", err);
+ return -1;
+ }
+#endif
+ }
+ if (timeout > 0) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ }
+ } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout));
+
+ /* Timeout occured, but no data available. */
+ return -1;
+}
+
+
+static int
+pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
+{
+ int n, nread = 0;
+ double timeout = -1.0;
+
+ if (conn->ctx->config[REQUEST_TIMEOUT]) {
+ timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+ }
+
+ while (len > 0 && conn->ctx->stop_flag == 0) {
+ n = pull(fp, conn, buf + nread, len, timeout);
+ if (n < 0) {
+ if (nread == 0) {
+ nread = n; /* Propagate the error */
+ }
+ break;
+ } else if (n == 0) {
+ break; /* No more data to read */
+ } else {
+ conn->consumed_content += n;
+ nread += n;
+ len -= n;
+ }
+ }
+
+ return nread;
+}
+
+
+static void
+discard_unread_request_data(struct mg_connection *conn)
+{
+ char buf[MG_BUF_LEN];
+ size_t to_read;
+ int nread;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ to_read = sizeof(buf);
+
+ if (conn->is_chunked) {
+ /* Chunked encoding: 1=chunk not read completely, 2=chunk read
+ * completely */
+ while (conn->is_chunked == 1) {
+ nread = mg_read(conn, buf, to_read);
+ if (nread <= 0) {
+ break;
+ }
+ }
+
+ } else {
+ /* Not chunked: content length is known */
+ while (conn->consumed_content < conn->content_len) {
+ if (to_read
+ > (size_t)(conn->content_len - conn->consumed_content)) {
+ to_read = (size_t)(conn->content_len - conn->consumed_content);
+ }
+
+ nread = mg_read(conn, buf, to_read);
+ if (nread <= 0) {
+ break;
+ }
+ }
+ }
+}
+
+
+static int
+mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
+{
+ int64_t n, buffered_len, nread;
+ int64_t len64 =
+ (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is
+ * int, we may not read more
+ * bytes */
+ const char *body;
+
+ if (conn == NULL) {
+ return 0;
+ }
+
+ /* If Content-Length is not set for a PUT or POST request, read until
+ * socket is closed */
+ if (conn->consumed_content == 0 && conn->content_len == -1) {
+ conn->content_len = INT64_MAX;
+ conn->must_close = 1;
+ }
+
+ nread = 0;
+ if (conn->consumed_content < conn->content_len) {
+ /* Adjust number of bytes to read. */
+ int64_t left_to_read = conn->content_len - conn->consumed_content;
+ if (left_to_read < len64) {
+ /* Do not read more than the total content length of the request.
+ */
+ len64 = left_to_read;
+ }
+
+ /* Return buffered data */
+ buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
+ - conn->consumed_content;
+ if (buffered_len > 0) {
+ if (len64 < buffered_len) {
+ buffered_len = len64;
+ }
+ body = conn->buf + conn->request_len + conn->consumed_content;
+ memcpy(buf, body, (size_t)buffered_len);
+ len64 -= buffered_len;
+ conn->consumed_content += buffered_len;
+ nread += buffered_len;
+ buf = (char *)buf + buffered_len;
+ }
+
+ /* We have returned all buffered data. Read new data from the remote
+ * socket.
+ */
+ if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
+ nread += n;
+ } else {
+ nread = (nread > 0 ? nread : n);
+ }
+ }
+ return (int)nread;
+}
+
+
+static char
+mg_getc(struct mg_connection *conn)
+{
+ char c;
+ if (conn == NULL) {
+ return 0;
+ }
+ conn->content_len++;
+ if (mg_read_inner(conn, &c, 1) <= 0) {
+ return (char)0;
+ }
+ return c;
+}
+
+
+int
+mg_read(struct mg_connection *conn, void *buf, size_t len)
+{
+ if (len > INT_MAX) {
+ len = INT_MAX;
+ }
+
+ if (conn == NULL) {
+ return 0;
+ }
+
+ if (conn->is_chunked) {
+ size_t all_read = 0;
+
+ while (len > 0) {
+
+ if (conn->is_chunked == 2) {
+ /* No more data left to read */
+ return 0;
+ }
+
+ if (conn->chunk_remainder) {
+ /* copy from the remainder of the last received chunk */
+ long read_ret;
+ size_t read_now =
+ ((conn->chunk_remainder > len) ? (len)
+ : (conn->chunk_remainder));
+
+ conn->content_len += (int)read_now;
+ read_ret =
+ mg_read_inner(conn, (char *)buf + all_read, read_now);
+ all_read += (size_t)read_ret;
+
+ conn->chunk_remainder -= read_now;
+ len -= read_now;
+
+ if (conn->chunk_remainder == 0) {
+ /* the rest of the data in the current chunk has been read
+ */
+ if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) {
+ /* Protocol violation */
+ return -1;
+ }
+ }
+
+ } else {
+ /* fetch a new chunk */
+ int i = 0;
+ char lenbuf[64];
+ char *end = 0;
+ unsigned long chunkSize = 0;
+
+ for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) {
+ lenbuf[i] = mg_getc(conn);
+ if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') {
+ continue;
+ }
+ if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') {
+ lenbuf[i + 1] = 0;
+ chunkSize = strtoul(lenbuf, &end, 16);
+ if (chunkSize == 0) {
+ /* regular end of content */
+ conn->is_chunked = 2;
+ }
+ break;
+ }
+ if (!isalnum(lenbuf[i])) {
+ /* illegal character for chunk length */
+ return -1;
+ }
+ }
+ if ((end == NULL) || (*end != '\r')) {
+ /* chunksize not set correctly */
+ return -1;
+ }
+ if (chunkSize == 0) {
+ break;
+ }
+
+ conn->chunk_remainder = chunkSize;
+ }
+ }
+
+ return (int)all_read;
+ }
+ return mg_read_inner(conn, buf, len);
+}
+
+
+int
+mg_write(struct mg_connection *conn, const void *buf, size_t len)
+{
+ time_t now;
+ int64_t n, total, allowed;
+
+ if (conn == NULL) {
+ return 0;
+ }
+
+ if (conn->throttle > 0) {
+ if ((now = time(NULL)) != conn->last_throttle_time) {
+ conn->last_throttle_time = now;
+ conn->last_throttle_bytes = 0;
+ }
+ allowed = conn->throttle - conn->last_throttle_bytes;
+ if (allowed > (int64_t)len) {
+ allowed = (int64_t)len;
+ }
+ if ((total = push_all(conn->ctx,
+ NULL,
+ conn->client.sock,
+ conn->ssl,
+ (const char *)buf,
+ (int64_t)allowed)) == allowed) {
+ buf = (const char *)buf + total;
+ conn->last_throttle_bytes += total;
+ while (total < (int64_t)len && conn->ctx->stop_flag == 0) {
+ allowed = conn->throttle > (int64_t)len - total
+ ? (int64_t)len - total
+ : conn->throttle;
+ if ((n = push_all(conn->ctx,
+ NULL,
+ conn->client.sock,
+ conn->ssl,
+ (const char *)buf,
+ (int64_t)allowed)) != allowed) {
+ break;
+ }
+ sleep(1);
+ conn->last_throttle_bytes = allowed;
+ conn->last_throttle_time = time(NULL);
+ buf = (const char *)buf + n;
+ total += n;
+ }
+ }
+ } else {
+ total = push_all(conn->ctx,
+ NULL,
+ conn->client.sock,
+ conn->ssl,
+ (const char *)buf,
+ (int64_t)len);
+ }
+ return (int)total;
+}
+
+
+/* Alternative alloc_vprintf() for non-compliant C runtimes */
+static int
+alloc_vprintf2(char **buf, const char *fmt, va_list ap)
+{
+ va_list ap_copy;
+ size_t size = MG_BUF_LEN / 4;
+ int len = -1;
+
+ *buf = NULL;
+ while (len < 0) {
+ if (*buf) {
+ mg_free(*buf);
+ }
+
+ size *= 4;
+ *buf = (char *)mg_malloc(size);
+ if (!*buf) {
+ break;
+ }
+
+ va_copy(ap_copy, ap);
+ len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
+ va_end(ap_copy);
+ (*buf)[size - 1] = 0;
+ }
+
+ return len;
+}
+
+
+/* Print message to buffer. If buffer is large enough to hold the message,
+ * return buffer. If buffer is to small, allocate large enough buffer on heap,
+ * and return allocated buffer. */
+static int
+alloc_vprintf(char **out_buf,
+ char *prealloc_buf,
+ size_t prealloc_size,
+ const char *fmt,
+ va_list ap)
+{
+ va_list ap_copy;
+ int len;
+
+ /* Windows is not standard-compliant, and vsnprintf() returns -1 if
+ * buffer is too small. Also, older versions of msvcrt.dll do not have
+ * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
+ * Therefore, we make two passes: on first pass, get required message
+ * length.
+ * On second pass, actually print the message. */
+ va_copy(ap_copy, ap);
+ len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
+ va_end(ap_copy);
+
+ if (len < 0) {
+ /* C runtime is not standard compliant, vsnprintf() returned -1.
+ * Switch to alternative code path that uses incremental allocations.
+ */
+ va_copy(ap_copy, ap);
+ len = alloc_vprintf2(out_buf, fmt, ap);
+ va_end(ap_copy);
+
+ } else if ((size_t)(len) >= prealloc_size) {
+ /* The pre-allocated buffer not large enough. */
+ /* Allocate a new buffer. */
+ *out_buf = (char *)mg_malloc((size_t)(len) + 1);
+ if (!*out_buf) {
+ /* Allocation failed. Return -1 as "out of memory" error. */
+ return -1;
+ }
+ /* Buffer allocation successful. Store the string there. */
+ va_copy(ap_copy, ap);
+ IGNORE_UNUSED_RESULT(
+ vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
+ va_end(ap_copy);
+
+ } else {
+ /* The pre-allocated buffer is large enough.
+ * Use it to store the string and return the address. */
+ va_copy(ap_copy, ap);
+ IGNORE_UNUSED_RESULT(
+ vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
+ va_end(ap_copy);
+ *out_buf = prealloc_buf;
+ }
+
+ return len;
+}
+
+
+static int
+mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
+{
+ char mem[MG_BUF_LEN];
+ char *buf = NULL;
+ int len;
+
+ if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
+ len = mg_write(conn, buf, (size_t)len);
+ }
+ if (buf != mem && buf != NULL) {
+ mg_free(buf);
+ }
+
+ return len;
+}
+
+
+int
+mg_printf(struct mg_connection *conn, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = mg_vprintf(conn, fmt, ap);
+ va_end(ap);
+
+ return result;
+}
+
+
+int
+mg_url_decode(const char *src,
+ int src_len,
+ char *dst,
+ int dst_len,
+ int is_form_url_encoded)
+{
+ int i, j, a, b;
+#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
+
+ for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
+ if (i < src_len - 2 && src[i] == '%'
+ && isxdigit(*(const unsigned char *)(src + i + 1))
+ && isxdigit(*(const unsigned char *)(src + i + 2))) {
+ a = tolower(*(const unsigned char *)(src + i + 1));
+ b = tolower(*(const unsigned char *)(src + i + 2));
+ dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
+ i += 2;
+ } else if (is_form_url_encoded && src[i] == '+') {
+ dst[j] = ' ';
+ } else {
+ dst[j] = src[i];
+ }
+ }
+
+ dst[j] = '\0'; /* Null-terminate the destination */
+
+ return i >= src_len ? j : -1;
+}
+
+
+int
+mg_get_var(const char *data,
+ size_t data_len,
+ const char *name,
+ char *dst,
+ size_t dst_len)
+{
+ return mg_get_var2(data, data_len, name, dst, dst_len, 0);
+}
+
+
+int
+mg_get_var2(const char *data,
+ size_t data_len,
+ const char *name,
+ char *dst,
+ size_t dst_len,
+ size_t occurrence)
+{
+ const char *p, *e, *s;
+ size_t name_len;
+ int len;
+
+ if (dst == NULL || dst_len == 0) {
+ len = -2;
+ } else if (data == NULL || name == NULL || data_len == 0) {
+ len = -1;
+ dst[0] = '\0';
+ } else {
+ name_len = strlen(name);
+ e = data + data_len;
+ len = -1;
+ dst[0] = '\0';
+
+ /* data is "var1=val1&var2=val2...". Find variable first */
+ for (p = data; p + name_len < e; p++) {
+ if ((p == data || p[-1] == '&') && p[name_len] == '='
+ && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
+ /* Point p to variable value */
+ p += name_len + 1;
+
+ /* Point s to the end of the value */
+ s = (const char *)memchr(p, '&', (size_t)(e - p));
+ if (s == NULL) {
+ s = e;
+ }
+ /* assert(s >= p); */
+ if (s < p) {
+ return -3;
+ }
+
+ /* Decode variable into destination buffer */
+ len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
+
+ /* Redirect error code from -1 to -2 (destination buffer too
+ * small). */
+ if (len == -1) {
+ len = -2;
+ }
+ break;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+int
+mg_get_cookie(const char *cookie_header,
+ const char *var_name,
+ char *dst,
+ size_t dst_size)
+{
+ const char *s, *p, *end;
+ int name_len, len = -1;
+
+ if (dst == NULL || dst_size == 0) {
+ len = -2;
+ } else if (var_name == NULL || (s = cookie_header) == NULL) {
+ len = -1;
+ dst[0] = '\0';
+ } else {
+ name_len = (int)strlen(var_name);
+ end = s + strlen(s);
+ dst[0] = '\0';
+
+ for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
+ if (s[name_len] == '=') {
+ s += name_len + 1;
+ if ((p = strchr(s, ' ')) == NULL) {
+ p = end;
+ }
+ if (p[-1] == ';') {
+ p--;
+ }
+ if (*s == '"' && p[-1] == '"' && p > s + 1) {
+ s++;
+ p--;
+ }
+ if ((size_t)(p - s) < dst_size) {
+ len = (int)(p - s);
+ mg_strlcpy(dst, s, (size_t)len + 1);
+ } else {
+ len = -3;
+ }
+ break;
+ }
+ }
+ }
+ return len;
+}
+
+
+#if defined(USE_WEBSOCKET) || defined(USE_LUA)
+static void
+base64_encode(const unsigned char *src, int src_len, char *dst)
+{
+ static const char *b64 =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i, j, a, b, c;
+
+ for (i = j = 0; i < src_len; i += 3) {
+ a = src[i];
+ b = i + 1 >= src_len ? 0 : src[i + 1];
+ c = i + 2 >= src_len ? 0 : src[i + 2];
+
+ dst[j++] = b64[a >> 2];
+ dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
+ if (i + 1 < src_len) {
+ dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
+ }
+ if (i + 2 < src_len) {
+ dst[j++] = b64[c & 63];
+ }
+ }
+ while (j % 4 != 0) {
+ dst[j++] = '=';
+ }
+ dst[j++] = '\0';
+}
+#endif
+
+
+#if defined(USE_LUA)
+static unsigned char
+b64reverse(char letter)
+{
+ if (letter >= 'A' && letter <= 'Z') {
+ return letter - 'A';
+ }
+ if (letter >= 'a' && letter <= 'z') {
+ return letter - 'a' + 26;
+ }
+ if (letter >= '0' && letter <= '9') {
+ return letter - '0' + 52;
+ }
+ if (letter == '+') {
+ return 62;
+ }
+ if (letter == '/') {
+ return 63;
+ }
+ if (letter == '=') {
+ return 255; /* normal end */
+ }
+ return 254; /* error */
+}
+
+
+static int
+base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len)
+{
+ int i;
+ unsigned char a, b, c, d;
+
+ *dst_len = 0;
+
+ for (i = 0; i < src_len; i += 4) {
+ a = b64reverse(src[i]);
+ if (a >= 254) {
+ return i;
+ }
+
+ b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]);
+ if (b >= 254) {
+ return i + 1;
+ }
+
+ c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]);
+ if (c == 254) {
+ return i + 2;
+ }
+
+ d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]);
+ if (d == 254) {
+ return i + 3;
+ }
+
+ dst[(*dst_len)++] = (a << 2) + (b >> 4);
+ if (c != 255) {
+ dst[(*dst_len)++] = (b << 4) + (c >> 2);
+ if (d != 255) {
+ dst[(*dst_len)++] = (c << 6) + d;
+ }
+ }
+ }
+ return -1;
+}
+#endif
+
+
+static int
+is_put_or_delete_method(const struct mg_connection *conn)
+{
+ if (conn) {
+ const char *s = conn->request_info.request_method;
+ return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
+ || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH"));
+ }
+ return 0;
+}
+
+
+static void
+interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
+ char *filename, /* out: filename */
+ size_t filename_buf_len, /* in: size of filename buffer */
+ struct file *filep, /* out: file structure */
+ int *is_found, /* out: file is found (directly) */
+ int *is_script_resource, /* out: handled by a script? */
+ int *is_websocket_request, /* out: websocket connetion? */
+ int *is_put_or_delete_request /* out: put/delete a file? */
+ )
+{
+/* TODO (high): Restructure this function */
+
+#if !defined(NO_FILES)
+ const char *uri = conn->request_info.local_uri;
+ const char *root = conn->ctx->config[DOCUMENT_ROOT];
+ const char *rewrite;
+ struct vec a, b;
+ int match_len;
+ char gz_path[PATH_MAX];
+ char const *accept_encoding;
+ int truncated;
+#if !defined(NO_CGI) || defined(USE_LUA)
+ char *p;
+#endif
+#else
+ (void)filename_buf_len; /* unused if NO_FILES is defined */
+#endif
+
+ memset(filep, 0, sizeof(*filep));
+ *filename = 0;
+ *is_found = 0;
+ *is_script_resource = 0;
+ *is_put_or_delete_request = is_put_or_delete_method(conn);
+
+#if defined(USE_WEBSOCKET)
+ *is_websocket_request = is_websocket_protocol(conn);
+#if !defined(NO_FILES)
+ if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) {
+ root = conn->ctx->config[WEBSOCKET_ROOT];
+ }
+#endif /* !NO_FILES */
+#else /* USE_WEBSOCKET */
+ *is_websocket_request = 0;
+#endif /* USE_WEBSOCKET */
+
+#if !defined(NO_FILES)
+ /* Note that root == NULL is a regular use case here. This occurs,
+ * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
+ * config is not required. */
+ if (root == NULL) {
+ /* all file related outputs have already been set to 0, just return
+ */
+ return;
+ }
+
+ /* Using buf_len - 1 because memmove() for PATH_INFO may shift part
+ * of the path one byte on the right.
+ * If document_root is NULL, leave the file empty. */
+ mg_snprintf(
+ conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
+
+ if (truncated) {
+ goto interpret_cleanup;
+ }
+
+ rewrite = conn->ctx->config[REWRITE];
+ while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
+ if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
+ mg_snprintf(conn,
+ &truncated,
+ filename,
+ filename_buf_len - 1,
+ "%.*s%s",
+ (int)b.len,
+ b.ptr,
+ uri + match_len);
+ break;
+ }
+ }
+
+ if (truncated) {
+ goto interpret_cleanup;
+ }
+
+ /* Local file path and name, corresponding to requested URI
+ * is now stored in "filename" variable. */
+ if (mg_stat(conn, filename, filep)) {
+#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
+ /* File exists. Check if it is a script type. */
+ if (0
+#if !defined(NO_CGI)
+ || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+ strlen(conn->ctx->config[CGI_EXTENSIONS]),
+ filename) > 0
+#endif
+#if defined(USE_LUA)
+ || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+ strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+ filename) > 0
+#endif
+#if defined(USE_DUKTAPE)
+ || match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+ strlen(
+ conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+ filename) > 0
+#endif
+ ) {
+ /* The request addresses a CGI script or a Lua script. The URI
+ * corresponds to the script itself (like /path/script.cgi),
+ * and there is no additional resource path
+ * (like /path/script.cgi/something).
+ * Requests that modify (replace or delete) a resource, like
+ * PUT and DELETE requests, should replace/delete the script
+ * file.
+ * Requests that read or write from/to a resource, like GET and
+ * POST requests, should call the script and return the
+ * generated response. */
+ *is_script_resource = !*is_put_or_delete_request;
+ }
+#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
+ *is_found = 1;
+ return;
+ }
+
+ /* If we can't find the actual file, look for the file
+ * with the same name but a .gz extension. If we find it,
+ * use that and set the gzipped flag in the file struct
+ * to indicate that the response need to have the content-
+ * encoding: gzip header.
+ * We can only do this if the browser declares support. */
+ if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
+ if (strstr(accept_encoding, "gzip") != NULL) {
+ mg_snprintf(
+ conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
+
+ if (truncated) {
+ goto interpret_cleanup;
+ }
+
+ if (mg_stat(conn, gz_path, filep)) {
+ if (filep) {
+ filep->gzipped = 1;
+ *is_found = 1;
+ }
+ /* Currently gz files can not be scripts. */
+ return;
+ }
+ }
+ }
+
+#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
+ /* Support PATH_INFO for CGI scripts. */
+ for (p = filename + strlen(filename); p > filename + 1; p--) {
+ if (*p == '/') {
+ *p = '\0';
+ if ((0
+#if !defined(NO_CGI)
+ || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+ strlen(conn->ctx->config[CGI_EXTENSIONS]),
+ filename) > 0
+#endif
+#if defined(USE_LUA)
+ || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+ strlen(
+ conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+ filename) > 0
+#endif
+#if defined(USE_DUKTAPE)
+ || match_prefix(
+ conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+ strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+ filename) > 0
+#endif
+ ) && mg_stat(conn, filename, filep)) {
+ /* Shift PATH_INFO block one character right, e.g.
+ * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
+ * conn->path_info is pointing to the local variable "path"
+ * declared in handle_request(), so PATH_INFO is not valid
+ * after handle_request returns. */
+ conn->path_info = p + 1;
+ memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
+ * trailing \0 */
+ p[1] = '/';
+ *is_script_resource = 1;
+ break;
+ } else {
+ *p = '/';
+ }
+ }
+ }
+#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
+#endif /* !defined(NO_FILES) */
+ return;
+
+#if !defined(NO_FILES)
+/* Reset all outputs */
+interpret_cleanup:
+ memset(filep, 0, sizeof(*filep));
+ *filename = 0;
+ *is_found = 0;
+ *is_script_resource = 0;
+ *is_websocket_request = 0;
+ *is_put_or_delete_request = 0;
+#endif /* !defined(NO_FILES) */
+}
+
+
+/* Check whether full request is buffered. Return:
+ * -1 if request is malformed
+ * 0 if request is not yet fully buffered
+ * >0 actual request length, including last \r\n\r\n */
+static int
+get_request_len(const char *buf, int buflen)
+{
+ const char *s, *e;
+ int len = 0;
+
+ for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
+ /* Control characters are not allowed but >=128 is. */
+ if (!isprint(*(const unsigned char *)s) && *s != '\r' && *s != '\n'
+ && *(const unsigned char *)s < 128) {
+ len = -1;
+ break; /* [i_a] abort scan as soon as one malformed character is
+ * found; */
+ /* don't let subsequent \r\n\r\n win us over anyhow */
+ } else if (s[0] == '\n' && s[1] == '\n') {
+ len = (int)(s - buf) + 2;
+ } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') {
+ len = (int)(s - buf) + 3;
+ }
+
+ return len;
+}
+
+
+#if !defined(NO_CACHING)
+/* Convert month to the month number. Return -1 on error, or month number */
+static int
+get_month_index(const char *s)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(month_names); i++) {
+ if (!strcmp(s, month_names[i])) {
+ return (int)i;
+ }
+ }
+
+ return -1;
+}
+
+
+/* Parse UTC date-time string, and return the corresponding time_t value. */
+static time_t
+parse_date_string(const char *datetime)
+{
+ char month_str[32] = {0};
+ int second, minute, hour, day, month, year;
+ time_t result = (time_t)0;
+ struct tm tm;
+
+ if ((sscanf(datetime,
+ "%d/%3s/%d %d:%d:%d",
+ &day,
+ month_str,
+ &year,
+ &hour,
+ &minute,
+ &second) == 6) || (sscanf(datetime,
+ "%d %3s %d %d:%d:%d",
+ &day,
+ month_str,
+ &year,
+ &hour,
+ &minute,
+ &second) == 6)
+ || (sscanf(datetime,
+ "%*3s, %d %3s %d %d:%d:%d",
+ &day,
+ month_str,
+ &year,
+ &hour,
+ &minute,
+ &second) == 6) || (sscanf(datetime,
+ "%d-%3s-%d %d:%d:%d",
+ &day,
+ month_str,
+ &year,
+ &hour,
+ &minute,
+ &second) == 6)) {
+ month = get_month_index(month_str);
+ if ((month >= 0) && (year >= 1970)) {
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ result = timegm(&tm);
+ }
+ }
+
+ return result;
+}
+#endif /* !NO_CACHING */
+
+
+/* Protect against directory disclosure attack by removing '..',
+ * excessive '/' and '\' characters */
+static void
+remove_double_dots_and_double_slashes(char *s)
+{
+ char *p = s;
+
+ while (*s != '\0') {
+ *p++ = *s++;
+ if (s[-1] == '/' || s[-1] == '\\') {
+ /* Skip all following slashes, backslashes and double-dots */
+ while (s[0] != '\0') {
+ if (s[0] == '/' || s[0] == '\\') {
+ s++;
+ } else if (s[0] == '.' && s[1] == '.') {
+ s += 2;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ *p = '\0';
+}
+
+
+static const struct {
+ const char *extension;
+ size_t ext_len;
+ const char *mime_type;
+} builtin_mime_types[] = {
+ /* IANA registered MIME types (http://www.iana.org/assignments/media-types)
+ * application types */
+ {".doc", 4, "application/msword"},
+ {".eps", 4, "application/postscript"},
+ {".exe", 4, "application/octet-stream"},
+ {".js", 3, "application/javascript"},
+ {".json", 5, "application/json"},
+ {".pdf", 4, "application/pdf"},
+ {".ps", 3, "application/postscript"},
+ {".rtf", 4, "application/rtf"},
+ {".xhtml", 6, "application/xhtml+xml"},
+ {".xsl", 4, "application/xml"},
+ {".xslt", 5, "application/xml"},
+
+ /* fonts */
+ {".ttf", 4, "application/font-sfnt"},
+ {".cff", 4, "application/font-sfnt"},
+ {".otf", 4, "application/font-sfnt"},
+ {".aat", 4, "application/font-sfnt"},
+ {".sil", 4, "application/font-sfnt"},
+ {".pfr", 4, "application/font-tdpfr"},
+ {".woff", 5, "application/font-woff"},
+
+ /* audio */
+ {".mp3", 4, "audio/mpeg"},
+ {".oga", 4, "audio/ogg"},
+ {".ogg", 4, "audio/ogg"},
+
+ /* image */
+ {".gif", 4, "image/gif"},
+ {".ief", 4, "image/ief"},
+ {".jpeg", 5, "image/jpeg"},
+ {".jpg", 4, "image/jpeg"},
+ {".jpm", 4, "image/jpm"},
+ {".jpx", 4, "image/jpx"},
+ {".png", 4, "image/png"},
+ {".svg", 4, "image/svg+xml"},
+ {".tif", 4, "image/tiff"},
+ {".tiff", 5, "image/tiff"},
+
+ /* model */
+ {".wrl", 4, "model/vrml"},
+
+ /* text */
+ {".css", 4, "text/css"},
+ {".csv", 4, "text/csv"},
+ {".htm", 4, "text/html"},
+ {".html", 5, "text/html"},
+ {".sgm", 4, "text/sgml"},
+ {".shtm", 5, "text/html"},
+ {".shtml", 6, "text/html"},
+ {".txt", 4, "text/plain"},
+ {".xml", 4, "text/xml"},
+
+ /* video */
+ {".mov", 4, "video/quicktime"},
+ {".mp4", 4, "video/mp4"},
+ {".mpeg", 5, "video/mpeg"},
+ {".mpg", 4, "video/mpeg"},
+ {".ogv", 4, "video/ogg"},
+ {".qt", 3, "video/quicktime"},
+
+ /* not registered types
+ * (http://reference.sitepoint.com/html/mime-types-full,
+ * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */
+ {".arj", 4, "application/x-arj-compressed"},
+ {".gz", 3, "application/x-gunzip"},
+ {".rar", 4, "application/x-arj-compressed"},
+ {".swf", 4, "application/x-shockwave-flash"},
+ {".tar", 4, "application/x-tar"},
+ {".tgz", 4, "application/x-tar-gz"},
+ {".torrent", 8, "application/x-bittorrent"},
+ {".ppt", 4, "application/x-mspowerpoint"},
+ {".xls", 4, "application/x-msexcel"},
+ {".zip", 4, "application/x-zip-compressed"},
+ {".aac",
+ 4,
+ "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */
+ {".aif", 4, "audio/x-aif"},
+ {".m3u", 4, "audio/x-mpegurl"},
+ {".mid", 4, "audio/x-midi"},
+ {".ra", 3, "audio/x-pn-realaudio"},
+ {".ram", 4, "audio/x-pn-realaudio"},
+ {".wav", 4, "audio/x-wav"},
+ {".bmp", 4, "image/bmp"},
+ {".ico", 4, "image/x-icon"},
+ {".pct", 4, "image/x-pct"},
+ {".pict", 5, "image/pict"},
+ {".rgb", 4, "image/x-rgb"},
+ {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */
+ {".asf", 4, "video/x-ms-asf"},
+ {".avi", 4, "video/x-msvideo"},
+ {".m4v", 4, "video/x-m4v"},
+ {NULL, 0, NULL}};
+
+
+const char *
+mg_get_builtin_mime_type(const char *path)
+{
+ const char *ext;
+ size_t i, path_len;
+
+ path_len = strlen(path);
+
+ for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
+ ext = path + (path_len - builtin_mime_types[i].ext_len);
+ if (path_len > builtin_mime_types[i].ext_len
+ && mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
+ return builtin_mime_types[i].mime_type;
+ }
+ }
+
+ return "text/plain";
+}
+
+
+/* Look at the "path" extension and figure what mime type it has.
+ * Store mime type in the vector. */
+static void
+get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
+{
+ struct vec ext_vec, mime_vec;
+ const char *list, *ext;
+ size_t path_len;
+
+ path_len = strlen(path);
+
+ if (ctx == NULL || vec == NULL) {
+ return;
+ }
+
+ /* Scan user-defined mime types first, in case user wants to
+ * override default mime types. */
+ list = ctx->config[EXTRA_MIME_TYPES];
+ while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
+ /* ext now points to the path suffix */
+ ext = path + path_len - ext_vec.len;
+ if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
+ *vec = mime_vec;
+ return;
+ }
+ }
+
+ vec->ptr = mg_get_builtin_mime_type(path);
+ vec->len = strlen(vec->ptr);
+}
+
+
+/* Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+ static const char *hex = "0123456789abcdef";
+
+ for (; len--; p++) {
+ *to++ = hex[p[0] >> 4];
+ *to++ = hex[p[0] & 0x0f];
+ }
+ *to = '\0';
+}
+
+
+/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */
+char *
+mg_md5(char buf[33], ...)
+{
+ md5_byte_t hash[16];
+ const char *p;
+ va_list ap;
+ md5_state_t ctx;
+
+ md5_init(&ctx);
+
+ va_start(ap, buf);
+ while ((p = va_arg(ap, const char *)) != NULL) {
+ md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
+ }
+ va_end(ap);
+
+ md5_finish(&ctx, hash);
+ bin2str(buf, hash, sizeof(hash));
+ return buf;
+}
+
+
+/* Check the user's password, return 1 if OK */
+static int
+check_password(const char *method,
+ const char *ha1,
+ const char *uri,
+ const char *nonce,
+ const char *nc,
+ const char *cnonce,
+ const char *qop,
+ const char *response)
+{
+ char ha2[32 + 1], expected_response[32 + 1];
+
+ /* Some of the parameters may be NULL */
+ if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL
+ || qop == NULL
+ || response == NULL) {
+ return 0;
+ }
+
+ /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
+ if (strlen(response) != 32) {
+ return 0;
+ }
+
+ mg_md5(ha2, method, ":", uri, NULL);
+ mg_md5(expected_response,
+ ha1,
+ ":",
+ nonce,
+ ":",
+ nc,
+ ":",
+ cnonce,
+ ":",
+ qop,
+ ":",
+ ha2,
+ NULL);
+
+ return mg_strcasecmp(response, expected_response) == 0;
+}
+
+
+/* Use the global passwords file, if specified by auth_gpass option,
+ * or search for .htpasswd in the requested directory. */
+static void
+open_auth_file(struct mg_connection *conn, const char *path, struct file *filep)
+{
+ if (conn != NULL && conn->ctx != NULL) {
+ char name[PATH_MAX];
+ const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
+ struct file file = STRUCT_FILE_INITIALIZER;
+ int truncated;
+
+ if (gpass != NULL) {
+ /* Use global passwords file */
+ if (!mg_fopen(conn, gpass, "r", filep)) {
+#ifdef DEBUG
+ mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
+#endif
+ }
+ /* Important: using local struct file to test path for is_directory
+ * flag. If filep is used, mg_stat() makes it appear as if auth file
+ * was opened. */
+ } else if (mg_stat(conn, path, &file) && file.is_directory) {
+ mg_snprintf(conn,
+ &truncated,
+ name,
+ sizeof(name),
+ "%s/%s",
+ path,
+ PASSWORDS_FILE_NAME);
+
+ if (truncated || !mg_fopen(conn, name, "r", filep)) {
+#ifdef DEBUG
+ mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO));
+#endif
+ }
+ } else {
+ /* Try to find .htpasswd in requested directory. */
+ for (p = path, e = p + strlen(p) - 1; e > p; e--) {
+ if (e[0] == '/') {
+ break;
+ }
+ }
+ mg_snprintf(conn,
+ &truncated,
+ name,
+ sizeof(name),
+ "%.*s/%s",
+ (int)(e - p),
+ p,
+ PASSWORDS_FILE_NAME);
+
+ if (truncated || !mg_fopen(conn, name, "r", filep)) {
+#ifdef DEBUG
+ mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO));
+#endif
+ }
+ }
+ }
+}
+
+
+/* Parsed Authorization header */
+struct ah {
+ char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
+};
+
+
+/* Return 1 on success. Always initializes the ah structure. */
+static int
+parse_auth_header(struct mg_connection *conn,
+ char *buf,
+ size_t buf_size,
+ struct ah *ah)
+{
+ char *name, *value, *s;
+ const char *auth_header;
+ uint64_t nonce;
+
+ if (!ah || !conn) {
+ return 0;
+ }
+
+ (void)memset(ah, 0, sizeof(*ah));
+ if ((auth_header = mg_get_header(conn, "Authorization")) == NULL
+ || mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
+ return 0;
+ }
+
+ /* Make modifiable copy of the auth header */
+ (void)mg_strlcpy(buf, auth_header + 7, buf_size);
+ s = buf;
+
+ /* Parse authorization header */
+ for (;;) {
+ /* Gobble initial spaces */
+ while (isspace(*(unsigned char *)s)) {
+ s++;
+ }
+ name = skip_quoted(&s, "=", " ", 0);
+ /* Value is either quote-delimited, or ends at first comma or space. */
+ if (s[0] == '\"') {
+ s++;
+ value = skip_quoted(&s, "\"", " ", '\\');
+ if (s[0] == ',') {
+ s++;
+ }
+ } else {
+ value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses
+ * spaces */
+ }
+ if (*name == '\0') {
+ break;
+ }
+
+ if (!strcmp(name, "username")) {
+ ah->user = value;
+ } else if (!strcmp(name, "cnonce")) {
+ ah->cnonce = value;
+ } else if (!strcmp(name, "response")) {
+ ah->response = value;
+ } else if (!strcmp(name, "uri")) {
+ ah->uri = value;
+ } else if (!strcmp(name, "qop")) {
+ ah->qop = value;
+ } else if (!strcmp(name, "nc")) {
+ ah->nc = value;
+ } else if (!strcmp(name, "nonce")) {
+ ah->nonce = value;
+ }
+ }
+
+#ifndef NO_NONCE_CHECK
+ /* Read the nonce from the response. */
+ if (ah->nonce == NULL) {
+ return 0;
+ }
+ s = NULL;
+ nonce = strtoull(ah->nonce, &s, 10);
+ if ((s == NULL) || (*s != 0)) {
+ return 0;
+ }
+
+ /* Convert the nonce from the client to a number. */
+ nonce ^= conn->ctx->auth_nonce_mask;
+
+ /* The converted number corresponds to the time the nounce has been
+ * created. This should not be earlier than the server start. */
+ /* Server side nonce check is valuable in all situations but one:
+ * if the server restarts frequently, but the client should not see
+ * that, so the server should accept nonces from previous starts. */
+ /* However, the reasonable default is to not accept a nonce from a
+ * previous start, so if anyone changed the access rights between
+ * two restarts, a new login is required. */
+ if (nonce < (uint64_t)conn->ctx->start_time) {
+ /* nonce is from a previous start of the server and no longer valid
+ * (replay attack?) */
+ return 0;
+ }
+ /* Check if the nonce is too high, so it has not (yet) been used by the
+ * server. */
+ if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) {
+ return 0;
+ }
+#endif
+
+ /* CGI needs it as REMOTE_USER */
+ if (ah->user != NULL) {
+ conn->request_info.remote_user = mg_strdup(ah->user);
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static const char *
+mg_fgets(char *buf, size_t size, struct file *filep, char **p)
+{
+ const char *eof;
+ size_t len;
+ const char *memend;
+
+ if (!filep) {
+ return NULL;
+ }
+
+ if (filep->membuf != NULL && *p != NULL) {
+ memend = (const char *)&filep->membuf[filep->size];
+ /* Search for \n from p till the end of stream */
+ eof = (char *)memchr(*p, '\n', (size_t)(memend - *p));
+ if (eof != NULL) {
+ eof += 1; /* Include \n */
+ } else {
+ eof = memend; /* Copy remaining data */
+ }
+ len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p);
+ memcpy(buf, *p, len);
+ buf[len] = '\0';
+ *p += len;
+ return len ? eof : NULL;
+ } else if (filep->fp != NULL) {
+ return fgets(buf, (int)size, filep->fp);
+ } else {
+ return NULL;
+ }
+}
+
+struct read_auth_file_struct {
+ struct mg_connection *conn;
+ struct ah ah;
+ char *domain;
+ char buf[256 + 256 + 40];
+ char *f_user;
+ char *f_domain;
+ char *f_ha1;
+};
+
+
+static int
+read_auth_file(struct file *filep, struct read_auth_file_struct *workdata)
+{
+ char *p;
+ int is_authorized = 0;
+ struct file fp;
+ size_t l;
+
+ if (!filep || !workdata) {
+ return 0;
+ }
+
+ /* Loop over passwords file */
+ p = (char *)filep->membuf;
+ while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) {
+ l = strlen(workdata->buf);
+ while (l > 0) {
+ if (isspace(workdata->buf[l - 1])
+ || iscntrl(workdata->buf[l - 1])) {
+ l--;
+ workdata->buf[l] = 0;
+ } else
+ break;
+ }
+ if (l < 1) {
+ continue;
+ }
+
+ workdata->f_user = workdata->buf;
+
+ if (workdata->f_user[0] == ':') {
+ /* user names may not contain a ':' and may not be empty,
+ * so lines starting with ':' may be used for a special purpose */
+ if (workdata->f_user[1] == '#') {
+ /* :# is a comment */
+ continue;
+ } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
+ if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) {
+ is_authorized = read_auth_file(&fp, workdata);
+ mg_fclose(&fp);
+ } else {
+ mg_cry(workdata->conn,
+ "%s: cannot open authorization file: %s",
+ __func__,
+ workdata->buf);
+ }
+ continue;
+ }
+ /* everything is invalid for the moment (might change in the
+ * future) */
+ mg_cry(workdata->conn,
+ "%s: syntax error in authorization file: %s",
+ __func__,
+ workdata->buf);
+ continue;
+ }
+
+ workdata->f_domain = strchr(workdata->f_user, ':');
+ if (workdata->f_domain == NULL) {
+ mg_cry(workdata->conn,
+ "%s: syntax error in authorization file: %s",
+ __func__,
+ workdata->buf);
+ continue;
+ }
+ *(workdata->f_domain) = 0;
+ (workdata->f_domain)++;
+
+ workdata->f_ha1 = strchr(workdata->f_domain, ':');
+ if (workdata->f_ha1 == NULL) {
+ mg_cry(workdata->conn,
+ "%s: syntax error in authorization file: %s",
+ __func__,
+ workdata->buf);
+ continue;
+ }
+ *(workdata->f_ha1) = 0;
+ (workdata->f_ha1)++;
+
+ if (!strcmp(workdata->ah.user, workdata->f_user)
+ && !strcmp(workdata->domain, workdata->f_domain)) {
+ return check_password(workdata->conn->request_info.request_method,
+ workdata->f_ha1,
+ workdata->ah.uri,
+ workdata->ah.nonce,
+ workdata->ah.nc,
+ workdata->ah.cnonce,
+ workdata->ah.qop,
+ workdata->ah.response);
+ }
+ }
+
+ return is_authorized;
+}
+
+
+/* Authorize against the opened passwords file. Return 1 if authorized. */
+static int
+authorize(struct mg_connection *conn, struct file *filep)
+{
+ struct read_auth_file_struct workdata;
+ char buf[MG_BUF_LEN];
+
+ if (!conn || !conn->ctx) {
+ return 0;
+ }
+
+ memset(&workdata, 0, sizeof(workdata));
+ workdata.conn = conn;
+
+ if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
+ return 0;
+ }
+ workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN];
+
+ return read_auth_file(filep, &workdata);
+}
+
+
+/* Return 1 if request is authorised, 0 otherwise. */
+static int
+check_authorization(struct mg_connection *conn, const char *path)
+{
+ char fname[PATH_MAX];
+ struct vec uri_vec, filename_vec;
+ const char *list;
+ struct file file = STRUCT_FILE_INITIALIZER;
+ int authorized = 1, truncated;
+
+ if (!conn || !conn->ctx) {
+ return 0;
+ }
+
+ list = conn->ctx->config[PROTECT_URI];
+ while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
+ if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
+ mg_snprintf(conn,
+ &truncated,
+ fname,
+ sizeof(fname),
+ "%.*s",
+ (int)filename_vec.len,
+ filename_vec.ptr);
+
+ if (truncated || !mg_fopen(conn, fname, "r", &file)) {
+ mg_cry(conn,
+ "%s: cannot open %s: %s",
+ __func__,
+ fname,
+ strerror(errno));
+ }
+ break;
+ }
+ }
+
+ if (!is_file_opened(&file)) {
+ open_auth_file(conn, path, &file);
+ }
+
+ if (is_file_opened(&file)) {
+ authorized = authorize(conn, &file);
+ mg_fclose(&file);
+ }
+
+ return authorized;
+}
+
+
+static void
+send_authorization_request(struct mg_connection *conn)
+{
+ char date[64];
+ time_t curtime = time(NULL);
+
+ if (conn && conn->ctx) {
+ uint64_t nonce = (uint64_t)(conn->ctx->start_time);
+
+ (void)pthread_mutex_lock(&conn->ctx->nonce_mutex);
+ nonce += conn->ctx->nonce_count;
+ ++conn->ctx->nonce_count;
+ (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex);
+
+ nonce ^= conn->ctx->auth_nonce_mask;
+ conn->status_code = 401;
+ conn->must_close = 1;
+
+ gmt_time_string(date, sizeof(date), &curtime);
+
+ mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n");
+ send_no_cache_header(conn);
+ mg_printf(conn,
+ "Date: %s\r\n"
+ "Connection: %s\r\n"
+ "Content-Length: 0\r\n"
+ "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
+ "nonce=\"%" UINT64_FMT "\"\r\n\r\n",
+ date,
+ suggest_connection_header(conn),
+ conn->ctx->config[AUTHENTICATION_DOMAIN],
+ nonce);
+ }
+}
+
+
+#if !defined(NO_FILES)
+static int
+is_authorized_for_put(struct mg_connection *conn)
+{
+ if (conn) {
+ struct file file = STRUCT_FILE_INITIALIZER;
+ const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
+ int ret = 0;
+
+ if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) {
+ ret = authorize(conn, &file);
+ mg_fclose(&file);
+ }
+
+ return ret;
+ }
+ return 0;
+}
+#endif
+
+
+int
+mg_modify_passwords_file(const char *fname,
+ const char *domain,
+ const char *user,
+ const char *pass)
+{
+ int found, i;
+ char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8];
+ FILE *fp, *fp2;
+
+ found = 0;
+ fp = fp2 = NULL;
+
+ /* Regard empty password as no password - remove user record. */
+ if (pass != NULL && pass[0] == '\0') {
+ pass = NULL;
+ }
+
+ /* Other arguments must not be empty */
+ if (fname == NULL || domain == NULL || user == NULL) {
+ return 0;
+ }
+
+ /* Using the given file format, user name and domain must not contain ':'
+ */
+ if (strchr(user, ':') != NULL) {
+ return 0;
+ }
+ if (strchr(domain, ':') != NULL) {
+ return 0;
+ }
+
+ /* Do not allow control characters like newline in user name and domain.
+ * Do not allow excessively long names either. */
+ for (i = 0; i < 255 && user[i] != 0; i++) {
+ if (iscntrl(user[i])) {
+ return 0;
+ }
+ }
+ if (user[i]) {
+ return 0;
+ }
+ for (i = 0; i < 255 && domain[i] != 0; i++) {
+ if (iscntrl(domain[i])) {
+ return 0;
+ }
+ }
+ if (domain[i]) {
+ return 0;
+ }
+
+ /* The maximum length of the path to the password file is limited */
+ if ((strlen(fname) + 4) >= PATH_MAX) {
+ return 0;
+ }
+
+ /* Create a temporary file name. Length has been checked before. */
+ strcpy(tmp, fname);
+ strcat(tmp, ".tmp");
+
+ /* Create the file if does not exist */
+ /* Use of fopen here is OK, since fname is only ASCII */
+ if ((fp = fopen(fname, "a+")) != NULL) {
+ (void)fclose(fp);
+ }
+
+ /* Open the given file and temporary file */
+ if ((fp = fopen(fname, "r")) == NULL) {
+ return 0;
+ } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
+ fclose(fp);
+ return 0;
+ }
+
+ /* Copy the stuff to temporary file */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) {
+ continue;
+ }
+ u[255] = 0;
+ d[255] = 0;
+
+ if (!strcmp(u, user) && !strcmp(d, domain)) {
+ found++;
+ if (pass != NULL) {
+ mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+ fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+ }
+ } else {
+ fprintf(fp2, "%s", line);
+ }
+ }
+
+ /* If new user, just add it */
+ if (!found && pass != NULL) {
+ mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+ fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+ }
+
+ /* Close files */
+ fclose(fp);
+ fclose(fp2);
+
+ /* Put the temp file in place of real file */
+ IGNORE_UNUSED_RESULT(remove(fname));
+ IGNORE_UNUSED_RESULT(rename(tmp, fname));
+
+ return 1;
+}
+
+
+static int
+is_valid_port(unsigned long port)
+{
+ return port < 0xffff;
+}
+
+
+static int
+mg_inet_pton(int af, const char *src, void *dst, size_t dstlen)
+{
+ struct addrinfo hints, *res, *ressave;
+ int func_ret = 0;
+ int gai_ret;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = af;
+
+ gai_ret = getaddrinfo(src, NULL, &hints, &res);
+ if (gai_ret != 0) {
+ /* gai_strerror could be used to convert gai_ret to a string */
+ /* POSIX return values: see
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
+ */
+ /* Windows return values: see
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
+ */
+ return 0;
+ }
+
+ ressave = res;
+
+ while (res) {
+ if (dstlen >= res->ai_addrlen) {
+ memcpy(dst, res->ai_addr, res->ai_addrlen);
+ func_ret = 1;
+ }
+ res = res->ai_next;
+ }
+
+ freeaddrinfo(ressave);
+ return func_ret;
+}
+
+
+static int
+connect_socket(struct mg_context *ctx /* may be NULL */,
+ const char *host,
+ int port,
+ int use_ssl,
+ char *ebuf,
+ size_t ebuf_len,
+ SOCKET *sock /* output: socket, must not be NULL */,
+ union usa *sa /* output: socket address, must not be NULL */
+ )
+{
+ int ip_ver = 0;
+ *sock = INVALID_SOCKET;
+ memset(sa, 0, sizeof(*sa));
+
+ if (ebuf_len > 0) {
+ *ebuf = 0;
+ }
+
+ if (host == NULL) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "NULL host");
+ return 0;
+ }
+
+ if (port < 0 || !is_valid_port((unsigned)port)) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "invalid port");
+ return 0;
+ }
+
+ if (use_ssl && (SSLv23_client_method == NULL)) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "SSL is not initialized");
+ return 0;
+ }
+
+ if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin))) {
+ sa->sin.sin_port = htons((uint16_t)port);
+ ip_ver = 4;
+#ifdef USE_IPV6
+ } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6))) {
+ sa->sin6.sin6_port = htons((uint16_t)port);
+ ip_ver = 6;
+ } else if (host[0] == '[') {
+ /* While getaddrinfo on Windows will work with [::1],
+ * getaddrinfo on Linux only works with ::1 (without []). */
+ size_t l = strlen(host + 1);
+ char *h = l > 1 ? mg_strdup(host + 1) : NULL;
+ if (h) {
+ h[l - 1] = 0;
+ if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) {
+ sa->sin6.sin6_port = htons((uint16_t)port);
+ ip_ver = 6;
+ }
+ mg_free(h);
+ }
+#endif
+ }
+
+ if (ip_ver == 0) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "host not found");
+ return 0;
+ }
+
+ if (ip_ver == 4) {
+ *sock = socket(PF_INET, SOCK_STREAM, 0);
+ }
+#ifdef USE_IPV6
+ else if (ip_ver == 6) {
+ *sock = socket(PF_INET6, SOCK_STREAM, 0);
+ }
+#endif
+
+ if (*sock == INVALID_SOCKET) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "socket(): %s",
+ strerror(ERRNO));
+ return 0;
+ }
+
+ set_close_on_exec(*sock, fc(ctx));
+
+ if ((ip_ver == 4)
+ && (connect(*sock, (struct sockaddr *)&sa->sin, sizeof(sa->sin))
+ == 0)) {
+ /* connected with IPv4 */
+ return 1;
+ }
+
+#ifdef USE_IPV6
+ if ((ip_ver == 6)
+ && (connect(*sock, (struct sockaddr *)&sa->sin6, sizeof(sa->sin6))
+ == 0)) {
+ /* connected with IPv6 */
+ return 1;
+ }
+#endif
+
+ /* Not connected */
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "connect(%s:%d): %s",
+ host,
+ port,
+ strerror(ERRNO));
+ closesocket(*sock);
+ *sock = INVALID_SOCKET;
+ return 0;
+}
+
+
+int
+mg_url_encode(const char *src, char *dst, size_t dst_len)
+{
+ static const char *dont_escape = "._-$,;~()";
+ static const char *hex = "0123456789abcdef";
+ char *pos = dst;
+ const char *end = dst + dst_len - 1;
+
+ for (; *src != '\0' && pos < end; src++, pos++) {
+ if (isalnum(*(const unsigned char *)src)
+ || strchr(dont_escape, *(const unsigned char *)src) != NULL) {
+ *pos = *src;
+ } else if (pos + 2 < end) {
+ pos[0] = '%';
+ pos[1] = hex[(*(const unsigned char *)src) >> 4];
+ pos[2] = hex[(*(const unsigned char *)src) & 0xf];
+ pos += 2;
+ } else {
+ break;
+ }
+ }
+
+ *pos = '\0';
+ return (*src == '\0') ? (int)(pos - dst) : -1;
+}
+
+
+static void
+print_dir_entry(struct de *de)
+{
+ char size[64], mod[64], href[PATH_MAX];
+ struct tm *tm;
+
+ if (de->file.is_directory) {
+ mg_snprintf(de->conn,
+ NULL, /* Buffer is big enough */
+ size,
+ sizeof(size),
+ "%s",
+ "[DIRECTORY]");
+ } else {
+ /* We use (signed) cast below because MSVC 6 compiler cannot
+ * convert unsigned __int64 to double. Sigh. */
+ if (de->file.size < 1024) {
+ mg_snprintf(de->conn,
+ NULL, /* Buffer is big enough */
+ size,
+ sizeof(size),
+ "%d",
+ (int)de->file.size);
+ } else if (de->file.size < 0x100000) {
+ mg_snprintf(de->conn,
+ NULL, /* Buffer is big enough */
+ size,
+ sizeof(size),
+ "%.1fk",
+ (double)de->file.size / 1024.0);
+ } else if (de->file.size < 0x40000000) {
+ mg_snprintf(de->conn,
+ NULL, /* Buffer is big enough */
+ size,
+ sizeof(size),
+ "%.1fM",
+ (double)de->file.size / 1048576);
+ } else {
+ mg_snprintf(de->conn,
+ NULL, /* Buffer is big enough */
+ size,
+ sizeof(size),
+ "%.1fG",
+ (double)de->file.size / 1073741824);
+ }
+ }
+
+ /* Note: mg_snprintf will not cause a buffer overflow above.
+ * So, string truncation checks are not required here. */
+
+ tm = localtime(&de->file.last_modified);
+ if (tm != NULL) {
+ strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
+ } else {
+ mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
+ mod[sizeof(mod) - 1] = '\0';
+ }
+ mg_url_encode(de->file_name, href, sizeof(href));
+ de->conn->num_bytes_sent +=
+ mg_printf(de->conn,
+ "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+ "<td> %s</td><td> %s</td></tr>\n",
+ de->conn->request_info.local_uri,
+ href,
+ de->file.is_directory ? "/" : "",
+ de->file_name,
+ de->file.is_directory ? "/" : "",
+ mod,
+ size);
+}
+
+
+/* This function is called from send_directory() and used for
+ * sorting directory entries by size, or name, or modification time.
+ * On windows, __cdecl specification is needed in case if project is built
+ * with __stdcall convention. qsort always requires __cdels callback. */
+static int WINCDECL
+compare_dir_entries(const void *p1, const void *p2)
+{
+ if (p1 && p2) {
+ const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
+ const char *query_string = a->conn->request_info.query_string;
+ int cmp_result = 0;
+
+ if (query_string == NULL) {
+ query_string = "na";
+ }
+
+ if (a->file.is_directory && !b->file.is_directory) {
+ return -1; /* Always put directories on top */
+ } else if (!a->file.is_directory && b->file.is_directory) {
+ return 1; /* Always put directories on top */
+ } else if (*query_string == 'n') {
+ cmp_result = strcmp(a->file_name, b->file_name);
+ } else if (*query_string == 's') {
+ cmp_result = a->file.size == b->file.size
+ ? 0
+ : a->file.size > b->file.size ? 1 : -1;
+ } else if (*query_string == 'd') {
+ cmp_result =
+ (a->file.last_modified == b->file.last_modified)
+ ? 0
+ : ((a->file.last_modified > b->file.last_modified) ? 1
+ : -1);
+ }
+
+ return query_string[1] == 'd' ? -cmp_result : cmp_result;
+ }
+ return 0;
+}
+
+
+static int
+must_hide_file(struct mg_connection *conn, const char *path)
+{
+ if (conn && conn->ctx) {
+ const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
+ const char *pattern = conn->ctx->config[HIDE_FILES];
+ return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0
+ || (pattern != NULL
+ && match_prefix(pattern, strlen(pattern), path) > 0);
+ }
+ return 0;
+}
+
+
+static int
+scan_directory(struct mg_connection *conn,
+ const char *dir,
+ void *data,
+ void (*cb)(struct de *, void *))
+{
+ char path[PATH_MAX];
+ struct dirent *dp;
+ DIR *dirp;
+ struct de de;
+ int truncated;
+
+ if ((dirp = mg_opendir(conn, dir)) == NULL) {
+ return 0;
+ } else {
+ de.conn = conn;
+
+ while ((dp = mg_readdir(dirp)) != NULL) {
+ /* Do not show current dir and hidden files */
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
+ || must_hide_file(conn, dp->d_name)) {
+ continue;
+ }
+
+ mg_snprintf(
+ conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
+
+ /* If we don't memset stat structure to zero, mtime will have
+ * garbage and strftime() will segfault later on in
+ * print_dir_entry(). memset is required only if mg_stat()
+ * fails. For more details, see
+ * http://code.google.com/p/mongoose/issues/detail?id=79 */
+ memset(&de.file, 0, sizeof(de.file));
+
+ if (truncated) {
+ /* If the path is not complete, skip processing. */
+ continue;
+ }
+
+ if (!mg_stat(conn, path, &de.file)) {
+ mg_cry(conn,
+ "%s: mg_stat(%s) failed: %s",
+ __func__,
+ path,
+ strerror(ERRNO));
+ }
+ de.file_name = dp->d_name;
+ cb(&de, data);
+ }
+ (void)mg_closedir(dirp);
+ }
+ return 1;
+}
+
+
+#if !defined(NO_FILES)
+static int
+remove_directory(struct mg_connection *conn, const char *dir)
+{
+ char path[PATH_MAX];
+ struct dirent *dp;
+ DIR *dirp;
+ struct de de;
+ int truncated;
+ int ok = 1;
+
+ if ((dirp = mg_opendir(conn, dir)) == NULL) {
+ return 0;
+ } else {
+ de.conn = conn;
+
+ while ((dp = mg_readdir(dirp)) != NULL) {
+ /* Do not show current dir (but show hidden files as they will
+ * also be removed) */
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
+ continue;
+ }
+
+ mg_snprintf(
+ conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
+
+ /* If we don't memset stat structure to zero, mtime will have
+ * garbage and strftime() will segfault later on in
+ * print_dir_entry(). memset is required only if mg_stat()
+ * fails. For more details, see
+ * http://code.google.com/p/mongoose/issues/detail?id=79 */
+ memset(&de.file, 0, sizeof(de.file));
+
+ if (truncated) {
+ /* Do not delete anything shorter */
+ ok = 0;
+ continue;
+ }
+
+ if (!mg_stat(conn, path, &de.file)) {
+ mg_cry(conn,
+ "%s: mg_stat(%s) failed: %s",
+ __func__,
+ path,
+ strerror(ERRNO));
+ ok = 0;
+ }
+ if (de.file.membuf == NULL) {
+ /* file is not in memory */
+ if (de.file.is_directory) {
+ if (remove_directory(conn, path) == 0) {
+ ok = 0;
+ }
+ } else {
+ if (mg_remove(conn, path) == 0) {
+ ok = 0;
+ }
+ }
+ } else {
+ /* file is in memory. It can not be deleted. */
+ ok = 0;
+ }
+ }
+ (void)mg_closedir(dirp);
+
+ IGNORE_UNUSED_RESULT(rmdir(dir));
+ }
+
+ return ok;
+}
+#endif
+
+
+struct dir_scan_data {
+ struct de *entries;
+ unsigned int num_entries;
+ unsigned int arr_size;
+};
+
+
+/* Behaves like realloc(), but frees original pointer on failure */
+static void *
+realloc2(void *ptr, size_t size)
+{
+ void *new_ptr = mg_realloc(ptr, size);
+ if (new_ptr == NULL) {
+ mg_free(ptr);
+ }
+ return new_ptr;
+}
+
+
+static void
+dir_scan_callback(struct de *de, void *data)
+{
+ struct dir_scan_data *dsd = (struct dir_scan_data *)data;
+
+ if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
+ dsd->arr_size *= 2;
+ dsd->entries =
+ (struct de *)realloc2(dsd->entries,
+ dsd->arr_size * sizeof(dsd->entries[0]));
+ }
+ if (dsd->entries == NULL) {
+ /* TODO(lsm, low): propagate an error to the caller */
+ dsd->num_entries = 0;
+ } else {
+ dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
+ dsd->entries[dsd->num_entries].file = de->file;
+ dsd->entries[dsd->num_entries].conn = de->conn;
+ dsd->num_entries++;
+ }
+}
+
+
+static void
+handle_directory_request(struct mg_connection *conn, const char *dir)
+{
+ unsigned int i;
+ int sort_direction;
+ struct dir_scan_data data = {NULL, 0, 128};
+ char date[64];
+ time_t curtime = time(NULL);
+
+ if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
+ send_http_error(conn,
+ 500,
+ "Error: Cannot open directory\nopendir(%s): %s",
+ dir,
+ strerror(ERRNO));
+ return;
+ }
+
+ gmt_time_string(date, sizeof(date), &curtime);
+
+ if (!conn) {
+ return;
+ }
+
+ sort_direction = conn->request_info.query_string != NULL
+ && conn->request_info.query_string[1] == 'd'
+ ? 'a'
+ : 'd';
+
+ conn->must_close = 1;
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+ send_static_cache_header(conn);
+ mg_printf(conn,
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n\r\n",
+ date);
+
+ conn->num_bytes_sent +=
+ mg_printf(conn,
+ "<html><head><title>Index of %s</title>"
+ "<style>th {text-align: left;}</style></head>"
+ "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
+ "<tr><th><a href=\"?n%c\">Name</a></th>"
+ "<th><a href=\"?d%c\">Modified</a></th>"
+ "<th><a href=\"?s%c\">Size</a></th></tr>"
+ "<tr><td colspan=\"3\"><hr></td></tr>",
+ conn->request_info.local_uri,
+ conn->request_info.local_uri,
+ sort_direction,
+ sort_direction,
+ sort_direction);
+
+ /* Print first entry - link to a parent directory */
+ conn->num_bytes_sent +=
+ mg_printf(conn,
+ "<tr><td><a href=\"%s%s\">%s</a></td>"
+ "<td> %s</td><td> %s</td></tr>\n",
+ conn->request_info.local_uri,
+ "..",
+ "Parent directory",
+ "-",
+ "-");
+
+ /* Sort and print directory entries */
+ if (data.entries != NULL) {
+ qsort(data.entries,
+ (size_t)data.num_entries,
+ sizeof(data.entries[0]),
+ compare_dir_entries);
+ for (i = 0; i < data.num_entries; i++) {
+ print_dir_entry(&data.entries[i]);
+ mg_free(data.entries[i].file_name);
+ }
+ mg_free(data.entries);
+ }
+
+ conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
+ conn->status_code = 200;
+}
+
+
+/* Send len bytes from the opened file to the client. */
+static void
+send_file_data(struct mg_connection *conn,
+ struct file *filep,
+ int64_t offset,
+ int64_t len)
+{
+ char buf[MG_BUF_LEN];
+ int to_read, num_read, num_written;
+ int64_t size;
+
+ if (!filep || !conn) {
+ return;
+ }
+
+ /* Sanity check the offset */
+ size = filep->size > INT64_MAX ? INT64_MAX : (int64_t)(filep->size);
+ offset = offset < 0 ? 0 : offset > size ? size : offset;
+
+ if (len > 0 && filep->membuf != NULL && size > 0) {
+ /* file stored in memory */
+ if (len > size - offset) {
+ len = size - offset;
+ }
+ mg_write(conn, filep->membuf + offset, (size_t)len);
+ } else if (len > 0 && filep->fp != NULL) {
+/* file stored on disk */
+#if defined(__linux__)
+ /* sendfile is only available for Linux */
+ if (conn->throttle == 0 && conn->ssl == 0) {
+ off_t sf_offs = (off_t)offset;
+ ssize_t sf_sent;
+ int sf_file = fileno(filep->fp);
+ int loop_cnt = 0;
+
+ do {
+ /* 2147479552 (0x7FFFF000) is a limit found by experiment on
+ * 64 bit Linux (2^31 minus one memory page of 4k?). */
+ size_t sf_tosend =
+ (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
+ sf_sent =
+ sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
+ if (sf_sent > 0) {
+ conn->num_bytes_sent += sf_sent;
+ len -= sf_sent;
+ offset += sf_sent;
+ } else if (loop_cnt == 0) {
+ /* This file can not be sent using sendfile.
+ * This might be the case for pseudo-files in the
+ * /sys/ and /proc/ file system.
+ * Use the regular user mode copy code instead. */
+ break;
+ } else if (sf_sent == 0) {
+ /* No error, but 0 bytes sent. May be EOF? */
+ return;
+ }
+ loop_cnt++;
+
+ } while ((len > 0) && (sf_sent >= 0));
+
+ if (sf_sent > 0) {
+ return; /* OK */
+ }
+
+ /* sf_sent<0 means error, thus fall back to the classic way */
+ /* This is always the case, if sf_file is not a "normal" file,
+ * e.g., for sending data from the output of a CGI process. */
+ offset = (int64_t)sf_offs;
+ }
+#endif
+ if ((offset > 0) && (fseeko(filep->fp, offset, SEEK_SET) != 0)) {
+ mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO));
+ send_http_error(
+ conn,
+ 500,
+ "%s",
+ "Error: Unable to access file at requested position.");
+ } else {
+ while (len > 0) {
+ /* Calculate how much to read from the file in the buffer */
+ to_read = sizeof(buf);
+ if ((int64_t)to_read > len) {
+ to_read = (int)len;
+ }
+
+ /* Read from file, exit the loop on error */
+ if ((num_read = (int)fread(buf, 1, (size_t)to_read, filep->fp))
+ <= 0) {
+ break;
+ }
+
+ /* Send read bytes to the client, exit the loop on error */
+ if ((num_written = mg_write(conn, buf, (size_t)num_read))
+ != num_read) {
+ break;
+ }
+
+ /* Both read and were successful, adjust counters */
+ conn->num_bytes_sent += num_written;
+ len -= num_written;
+ }
+ }
+ }
+}
+
+
+static int
+parse_range_header(const char *header, int64_t *a, int64_t *b)
+{
+ return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
+}
+
+
+static void
+construct_etag(char *buf, size_t buf_len, const struct file *filep)
+{
+ if (filep != NULL && buf != NULL) {
+ mg_snprintf(NULL,
+ NULL, /* All calls to construct_etag use 64 byte buffer */
+ buf,
+ buf_len,
+ "\"%lx.%" INT64_FMT "\"",
+ (unsigned long)filep->last_modified,
+ filep->size);
+ }
+}
+
+
+static void
+fclose_on_exec(struct file *filep, struct mg_connection *conn)
+{
+ if (filep != NULL && filep->fp != NULL) {
+#ifdef _WIN32
+ (void)conn; /* Unused. */
+#else
+ if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
+ mg_cry(conn,
+ "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+#endif
+ }
+}
+
+
+static void
+handle_static_file_request(struct mg_connection *conn,
+ const char *path,
+ struct file *filep,
+ const char *mime_type)
+{
+ char date[64], lm[64], etag[64];
+ char range[128]; /* large enough, so there will be no overflow */
+ const char *msg = "OK", *hdr;
+ time_t curtime = time(NULL);
+ int64_t cl, r1, r2;
+ struct vec mime_vec;
+ int n, truncated;
+ char gz_path[PATH_MAX];
+ const char *encoding = "";
+ const char *cors1, *cors2, *cors3;
+
+ if (conn == NULL || conn->ctx == NULL || filep == NULL) {
+ return;
+ }
+
+ if (mime_type == NULL) {
+ get_mime_type(conn->ctx, path, &mime_vec);
+ } else {
+ mime_vec.ptr = mime_type;
+ mime_vec.len = strlen(mime_type);
+ }
+ if (filep->size > INT64_MAX) {
+ send_http_error(conn,
+ 500,
+ "Error: File size is too large to send\n%" INT64_FMT,
+ filep->size);
+ }
+ cl = (int64_t)filep->size;
+ conn->status_code = 200;
+ range[0] = '\0';
+
+ /* if this file is in fact a pre-gzipped file, rewrite its filename
+ * it's important to rewrite the filename after resolving
+ * the mime type from it, to preserve the actual file's type */
+ if (filep->gzipped) {
+ mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
+
+ if (truncated) {
+ send_http_error(conn,
+ 500,
+ "Error: Path of zipped file too long (%s)",
+ path);
+ return;
+ }
+
+ path = gz_path;
+ encoding = "Content-Encoding: gzip\r\n";
+ }
+
+ if (!mg_fopen(conn, path, "rb", filep)) {
+ send_http_error(conn,
+ 500,
+ "Error: Cannot open file\nfopen(%s): %s",
+ path,
+ strerror(ERRNO));
+ return;
+ }
+
+ fclose_on_exec(filep, conn);
+
+ /* If Range: header specified, act accordingly */
+ r1 = r2 = 0;
+ hdr = mg_get_header(conn, "Range");
+ if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0
+ && r2 >= 0) {
+ /* actually, range requests don't play well with a pre-gzipped
+ * file (since the range is specified in the uncompressed space) */
+ if (filep->gzipped) {
+ send_http_error(
+ conn,
+ 501,
+ "%s",
+ "Error: Range requests in gzipped files are not supported");
+ mg_fclose(filep);
+ return;
+ }
+ conn->status_code = 206;
+ cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1;
+ mg_snprintf(conn,
+ NULL, /* range buffer is big enough */
+ range,
+ sizeof(range),
+ "Content-Range: bytes "
+ "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
+ r1,
+ r1 + cl - 1,
+ filep->size);
+ msg = "Partial Content";
+ }
+
+ hdr = mg_get_header(conn, "Origin");
+ if (hdr) {
+ /* Cross-origin resource sharing (CORS), see
+ * http://www.html5rocks.com/en/tutorials/cors/,
+ * http://www.html5rocks.com/static/images/cors_server_flowchart.png -
+ * preflight is not supported for files. */
+ cors1 = "Access-Control-Allow-Origin: ";
+ cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
+ cors3 = "\r\n";
+ } else {
+ cors1 = cors2 = cors3 = "";
+ }
+
+ /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */
+ gmt_time_string(date, sizeof(date), &curtime);
+ gmt_time_string(lm, sizeof(lm), &filep->last_modified);
+ construct_etag(etag, sizeof(etag), filep);
+
+ (void)mg_printf(conn,
+ "HTTP/1.1 %d %s\r\n"
+ "%s%s%s"
+ "Date: %s\r\n",
+ conn->status_code,
+ msg,
+ cors1,
+ cors2,
+ cors3,
+ date);
+ send_static_cache_header(conn);
+ (void)mg_printf(conn,
+ "Last-Modified: %s\r\n"
+ "Etag: %s\r\n"
+ "Content-Type: %.*s\r\n"
+ "Content-Length: %" INT64_FMT "\r\n"
+ "Connection: %s\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "%s%s\r\n",
+ lm,
+ etag,
+ (int)mime_vec.len,
+ mime_vec.ptr,
+ cl,
+ suggest_connection_header(conn),
+ range,
+ encoding);
+
+ if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
+ send_file_data(conn, filep, r1, cl);
+ }
+ mg_fclose(filep);
+}
+
+
+void
+mg_send_file(struct mg_connection *conn, const char *path)
+{
+ mg_send_mime_file(conn, path, NULL);
+}
+
+
+void
+mg_send_mime_file(struct mg_connection *conn,
+ const char *path,
+ const char *mime_type)
+{
+ struct file file = STRUCT_FILE_INITIALIZER;
+ if (mg_stat(conn, path, &file)) {
+ if (file.is_directory) {
+ if (!conn) {
+ return;
+ }
+ if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING],
+ "yes")) {
+ handle_directory_request(conn, path);
+ } else {
+ send_http_error(conn,
+ 403,
+ "%s",
+ "Error: Directory listing denied");
+ }
+ } else {
+ handle_static_file_request(conn, path, &file, mime_type);
+ }
+ } else {
+ send_http_error(conn, 404, "%s", "Error: File not found");
+ }
+}
+
+
+/* For a given PUT path, create all intermediate subdirectories.
+ * Return 0 if the path itself is a directory.
+ * Return 1 if the path leads to a file.
+ * Return -1 for if the path is too long.
+ * Return -2 if path can not be created.
+*/
+static int
+put_dir(struct mg_connection *conn, const char *path)
+{
+ char buf[PATH_MAX];
+ const char *s, *p;
+ struct file file = STRUCT_FILE_INITIALIZER;
+ size_t len;
+ int res = 1;
+
+ for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
+ len = (size_t)(p - path);
+ if (len >= sizeof(buf)) {
+ /* path too long */
+ res = -1;
+ break;
+ }
+ memcpy(buf, path, len);
+ buf[len] = '\0';
+
+ /* Try to create intermediate directory */
+ DEBUG_TRACE("mkdir(%s)", buf);
+ if (!mg_stat(conn, buf, &file) && mg_mkdir(conn, buf, 0755) != 0) {
+ /* path does not exixt and can not be created */
+ res = -2;
+ break;
+ }
+
+ /* Is path itself a directory? */
+ if (p[1] == '\0') {
+ res = 0;
+ }
+ }
+
+ return res;
+}
+
+
+static void
+remove_bad_file(const struct mg_connection *conn, const char *path)
+{
+ int r = mg_remove(conn, path);
+ if (r != 0) {
+ mg_cry(conn, "%s: Cannot remove invalid file %s", __func__, path);
+ }
+}
+
+
+long long
+mg_store_body(struct mg_connection *conn, const char *path)
+{
+ char buf[MG_BUF_LEN];
+ long long len = 0;
+ int ret, n;
+ struct file fi;
+
+ if (conn->consumed_content != 0) {
+ mg_cry(conn, "%s: Contents already consumed", __func__);
+ return -11;
+ }
+
+ ret = put_dir(conn, path);
+ if (ret < 0) {
+ /* -1 for path too long,
+ * -2 for path can not be created. */
+ return ret;
+ }
+ if (ret != 1) {
+ /* Return 0 means, path itself is a directory. */
+ return 0;
+ }
+
+ if (mg_fopen(conn, path, "w", &fi) == 0) {
+ return -12;
+ }
+
+ ret = mg_read(conn, buf, sizeof(buf));
+ while (ret > 0) {
+ n = (int)fwrite(buf, 1, (size_t)ret, fi.fp);
+ if (n != ret) {
+ mg_fclose(&fi);
+ remove_bad_file(conn, path);
+ return -13;
+ }
+ ret = mg_read(conn, buf, sizeof(buf));
+ }
+
+ /* TODO: mg_fclose should return an error,
+ * and every caller should check and handle it. */
+ if (fclose(fi.fp) != 0) {
+ remove_bad_file(conn, path);
+ return -14;
+ }
+
+ return len;
+}
+
+
+/* Parse HTTP headers from the given buffer, advance buffer to the point
+ * where parsing stopped. */
+static void
+parse_http_headers(char **buf, struct mg_request_info *ri)
+{
+ int i;
+
+ if (!ri) {
+ return;
+ }
+
+ ri->num_headers = 0;
+
+ for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) {
+ char *dp = *buf;
+ while ((*dp != ':') && (*dp != '\r') && (*dp != 0)) {
+ dp++;
+ }
+ if (!*dp) {
+ /* neither : nor \r\n. This is not a valid field. */
+ break;
+ }
+ if (*dp == '\r') {
+ if (dp[1] == '\n') {
+ /* \r\n */
+ ri->http_headers[i].name = *buf;
+ ri->http_headers[i].value = 0;
+ *buf = dp;
+ } else {
+ /* stray \r. This is not valid. */
+ break;
+ }
+ } else {
+ /* (*dp == ':') */
+ *dp = 0;
+ ri->http_headers[i].name = *buf;
+ do {
+ dp++;
+ } while (*dp == ' ');
+
+ ri->http_headers[i].value = dp;
+ *buf = strstr(dp, "\r\n");
+ }
+
+ ri->num_headers = i + 1;
+ if (*buf) {
+ (*buf)[0] = 0;
+ (*buf)[1] = 0;
+ *buf += 2;
+ } else {
+ *buf = dp;
+ break;
+ }
+
+ if (*buf[0] == '\r') {
+ /* This is the end of the header */
+ break;
+ }
+ }
+}
+
+
+static int
+is_valid_http_method(const char *method)
+{
+ return !strcmp(method, "GET") /* HTTP (RFC 2616) */
+ || !strcmp(method, "POST") /* HTTP (RFC 2616) */
+ || !strcmp(method, "HEAD") /* HTTP (RFC 2616) */
+ || !strcmp(method, "PUT") /* HTTP (RFC 2616) */
+ || !strcmp(method, "DELETE") /* HTTP (RFC 2616) */
+ || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */
+ /* TRACE method (RFC 2616) is not supported for security reasons */
+ || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */
+
+ || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */
+ || !strcmp(method, "MKCOL") /* WEBDAV (RFC 2518) */
+
+ /* Unsupported WEBDAV Methods: */
+ /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
+ /* + 11 methods from RFC 3253 */
+ /* ORDERPATCH (RFC 3648) */
+ /* ACL (RFC 3744) */
+ /* SEARCH (RFC 5323) */
+ /* + MicroSoft extensions
+ * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
+
+ /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
+ || !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */
+}
+
+
+/* Parse HTTP request, fill in mg_request_info structure.
+ * This function modifies the buffer by NUL-terminating
+ * HTTP request components, header names and header values. */
+static int
+parse_http_message(char *buf, int len, struct mg_request_info *ri)
+{
+ int is_request, request_length;
+
+ if (!ri) {
+ return 0;
+ }
+
+ request_length = get_request_len(buf, len);
+
+ if (request_length > 0) {
+ /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr,
+ * remote_port */
+ ri->remote_user = ri->request_method = ri->request_uri =
+ ri->http_version = NULL;
+ ri->num_headers = 0;
+
+ buf[request_length - 1] = '\0';
+
+ /* RFC says that all initial whitespaces should be ingored */
+ while (*buf != '\0' && isspace(*(unsigned char *)buf)) {
+ buf++;
+ }
+ ri->request_method = skip(&buf, " ");
+ ri->request_uri = skip(&buf, " ");
+ ri->http_version = skip(&buf, "\r\n");
+
+ /* HTTP message could be either HTTP request or HTTP response, e.g.
+ * "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." */
+ is_request = is_valid_http_method(ri->request_method);
+ if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0)
+ || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
+ request_length = -1;
+ } else {
+ if (is_request) {
+ ri->http_version += 5;
+ }
+ parse_http_headers(&buf, ri);
+ }
+ }
+ return request_length;
+}
+
+
+/* Keep reading the input (either opened file descriptor fd, or socket sock,
+ * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
+ * buffer (which marks the end of HTTP request). Buffer buf may already
+ * have some data. The length of the data is stored in nread.
+ * Upon every read operation, increase nread by the number of bytes read. */
+static int
+read_request(FILE *fp,
+ struct mg_connection *conn,
+ char *buf,
+ int bufsiz,
+ int *nread)
+{
+ int request_len, n = 0;
+ struct timespec last_action_time;
+ double request_timeout;
+
+ if (!conn) {
+ return 0;
+ }
+
+ memset(&last_action_time, 0, sizeof(last_action_time));
+
+ if (conn->ctx->config[REQUEST_TIMEOUT]) {
+ /* value of request_timeout is in seconds, config in milliseconds */
+ request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+ } else {
+ request_timeout = -1.0;
+ }
+
+ request_len = get_request_len(buf, *nread);
+
+ /* first time reading from this connection */
+ clock_gettime(CLOCK_MONOTONIC, &last_action_time);
+
+ while (
+ (conn->ctx->stop_flag == 0) && (*nread < bufsiz) && (request_len == 0)
+ && ((mg_difftimespec(&last_action_time, &(conn->req_time))
+ <= request_timeout) || (request_timeout < 0))
+ && ((n = pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout))
+ > 0)) {
+ *nread += n;
+ /* assert(*nread <= bufsiz); */
+ if (*nread > bufsiz) {
+ return -2;
+ }
+ request_len = get_request_len(buf, *nread);
+ if (request_timeout > 0.0) {
+ clock_gettime(CLOCK_MONOTONIC, &last_action_time);
+ }
+ }
+
+ return (request_len <= 0 && n <= 0) ? -1 : request_len;
+}
+
+#if !defined(NO_FILES)
+/* For given directory path, substitute it to valid index file.
+ * Return 1 if index file has been found, 0 if not found.
+ * If the file is found, it's stats is returned in stp. */
+static int
+substitute_index_file(struct mg_connection *conn,
+ char *path,
+ size_t path_len,
+ struct file *filep)
+{
+ if (conn && conn->ctx) {
+ const char *list = conn->ctx->config[INDEX_FILES];
+ struct file file = STRUCT_FILE_INITIALIZER;
+ struct vec filename_vec;
+ size_t n = strlen(path);
+ int found = 0;
+
+ /* The 'path' given to us points to the directory. Remove all trailing
+ * directory separator characters from the end of the path, and
+ * then append single directory separator character. */
+ while (n > 0 && path[n - 1] == '/') {
+ n--;
+ }
+ path[n] = '/';
+
+ /* Traverse index files list. For each entry, append it to the given
+ * path and see if the file exists. If it exists, break the loop */
+ while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
+ /* Ignore too long entries that may overflow path buffer */
+ if (filename_vec.len > path_len - (n + 2)) {
+ continue;
+ }
+
+ /* Prepare full path to the index file */
+ mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
+
+ /* Does it exist? */
+ if (mg_stat(conn, path, &file)) {
+ /* Yes it does, break the loop */
+ *filep = file;
+ found = 1;
+ break;
+ }
+ }
+
+ /* If no index file exists, restore directory path */
+ if (!found) {
+ path[n] = '\0';
+ }
+
+ return found;
+ }
+ return 0;
+}
+#endif
+
+
+#if !defined(NO_CACHING)
+/* Return True if we should reply 304 Not Modified. */
+static int
+is_not_modified(const struct mg_connection *conn, const struct file *filep)
+{
+ char etag[64];
+ const char *ims = mg_get_header(conn, "If-Modified-Since");
+ const char *inm = mg_get_header(conn, "If-None-Match");
+ construct_etag(etag, sizeof(etag), filep);
+ if (!filep) {
+ return 0;
+ }
+ return (inm != NULL && !mg_strcasecmp(etag, inm))
+ || (ims != NULL && (filep->last_modified <= parse_date_string(ims)));
+}
+#endif /* !NO_CACHING */
+
+
+#if !defined(NO_CGI) || !defined(NO_FILES)
+static int
+forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
+{
+ const char *expect, *body;
+ char buf[MG_BUF_LEN];
+ int to_read, nread, success = 0;
+ int64_t buffered_len;
+ double timeout = -1.0;
+
+ if (!conn) {
+ return 0;
+ }
+ if (conn->ctx->config[REQUEST_TIMEOUT]) {
+ timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+ }
+
+ expect = mg_get_header(conn, "Expect");
+ /* assert(fp != NULL); */
+ if (!fp) {
+ send_http_error(conn, 500, "%s", "Error: NULL File");
+ return 0;
+ }
+
+ if (conn->content_len == -1 && !conn->is_chunked) {
+ /* Content length is not specified by the client. */
+ send_http_error(conn,
+ 411,
+ "%s",
+ "Error: Client did not specify content length");
+ } else if ((expect != NULL)
+ && (mg_strcasecmp(expect, "100-continue") != 0)) {
+ /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */
+ send_http_error(conn,
+ 417,
+ "Error: Can not fulfill expectation %s",
+ expect);
+ } else {
+ if (expect != NULL) {
+ (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
+ conn->status_code = 100;
+ } else {
+ conn->status_code = 200;
+ }
+
+ buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
+ - conn->consumed_content;
+
+ /* assert(buffered_len >= 0); */
+ /* assert(conn->consumed_content == 0); */
+
+ if ((buffered_len < 0) || (conn->consumed_content != 0)) {
+ send_http_error(conn, 500, "%s", "Error: Size mismatch");
+ return 0;
+ }
+
+ if (buffered_len > 0) {
+ if ((int64_t)buffered_len > conn->content_len) {
+ buffered_len = (int)conn->content_len;
+ }
+ body = conn->buf + conn->request_len + conn->consumed_content;
+ push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len);
+ conn->consumed_content += buffered_len;
+ }
+
+ nread = 0;
+ while (conn->consumed_content < conn->content_len) {
+ to_read = sizeof(buf);
+ if ((int64_t)to_read > conn->content_len - conn->consumed_content) {
+ to_read = (int)(conn->content_len - conn->consumed_content);
+ }
+ nread = pull(NULL, conn, buf, to_read, timeout);
+ if (nread <= 0
+ || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) {
+ break;
+ }
+ conn->consumed_content += nread;
+ }
+
+ if (conn->consumed_content == conn->content_len) {
+ success = (nread >= 0);
+ }
+
+ /* Each error code path in this function must send an error */
+ if (!success) {
+ /* NOTE: Maybe some data has already been sent. */
+ /* TODO (low): If some data has been sent, a correct error
+ * reply can no longer be sent, so just close the connection */
+ send_http_error(conn, 500, "%s", "");
+ }
+ }
+
+ return success;
+}
+#endif
+
+#if !defined(NO_CGI)
+/* This structure helps to create an environment for the spawned CGI program.
+ * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
+ * last element must be NULL.
+ * However, on Windows there is a requirement that all these VARIABLE=VALUE\0
+ * strings must reside in a contiguous buffer. The end of the buffer is
+ * marked by two '\0' characters.
+ * We satisfy both worlds: we create an envp array (which is vars), all
+ * entries are actually pointers inside buf. */
+struct cgi_environment {
+ struct mg_connection *conn;
+ /* Data block */
+ char *buf; /* Environment buffer */
+ size_t buflen; /* Space available in buf */
+ size_t bufused; /* Space taken in buf */
+ /* Index block */
+ char **var; /* char **envp */
+ size_t varlen; /* Number of variables available in var */
+ size_t varused; /* Number of variables stored in var */
+};
+
+
+static void addenv(struct cgi_environment *env,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(2, 3);
+
+/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective
+ * pointer into the vars array. Assumes env != NULL and fmt != NULL. */
+static void
+addenv(struct cgi_environment *env, const char *fmt, ...)
+{
+ size_t n, space;
+ int truncated;
+ char *added;
+ va_list ap;
+
+ /* Calculate how much space is left in the buffer */
+ space = (env->buflen - env->bufused);
+
+ /* Calculate an estimate for the required space */
+ n = strlen(fmt) + 2 + 128;
+
+ do {
+ if (space <= n) {
+ /* Allocate new buffer */
+ n = env->buflen + CGI_ENVIRONMENT_SIZE;
+ added = (char *)mg_realloc(env->buf, n);
+ if (!added) {
+ /* Out of memory */
+ mg_cry(env->conn,
+ "%s: Cannot allocate memory for CGI variable [%s]",
+ __func__,
+ fmt);
+ return;
+ }
+ env->buf = added;
+ env->buflen = n;
+ space = (env->buflen - env->bufused);
+ }
+
+ /* Make a pointer to the free space int the buffer */
+ added = env->buf + env->bufused;
+
+ /* Copy VARIABLE=VALUE\0 string into the free space */
+ va_start(ap, fmt);
+ mg_vsnprintf(env->conn, &truncated, added, (size_t)space, fmt, ap);
+ va_end(ap);
+
+ /* Do not add truncated strings to the environment */
+ if (truncated) {
+ /* Reallocate the buffer */
+ space = 0;
+ n = 1;
+ }
+ } while (truncated);
+
+ /* Calculate number of bytes added to the environment */
+ n = strlen(added) + 1;
+ env->bufused += n;
+
+ /* Now update the variable index */
+ space = (env->varlen - env->varused);
+ if (space < 2) {
+ mg_cry(env->conn,
+ "%s: Cannot register CGI variable [%s]",
+ __func__,
+ fmt);
+ return;
+ }
+
+ /* Append a pointer to the added string into the envp array */
+ env->var[env->varused] = added;
+ env->varused++;
+}
+
+
+static void
+prepare_cgi_environment(struct mg_connection *conn,
+ const char *prog,
+ struct cgi_environment *env)
+{
+ const char *s;
+ struct vec var_vec;
+ char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
+ int i, truncated;
+
+ if (conn == NULL || prog == NULL || env == NULL) {
+ return;
+ }
+
+ env->conn = conn;
+ env->buflen = CGI_ENVIRONMENT_SIZE;
+ env->bufused = 0;
+ env->buf = (char *)mg_malloc(env->buflen);
+ env->varlen = MAX_CGI_ENVIR_VARS;
+ env->varused = 0;
+ env->var = (char **)mg_malloc(env->buflen * sizeof(char *));
+
+ addenv(env, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
+ addenv(env, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
+ addenv(env, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
+ addenv(env, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version());
+
+ /* Prepare the environment block */
+ addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
+ addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
+ addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
+
+#if defined(USE_IPV6)
+ if (conn->client.lsa.sa.sa_family == AF_INET6) {
+ addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port));
+ } else
+#endif
+ {
+ addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
+ }
+
+ sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+ addenv(env, "REMOTE_ADDR=%s", src_addr);
+
+ addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
+ addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
+
+ addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
+ addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
+
+ /* SCRIPT_NAME */
+ addenv(env,
+ "SCRIPT_NAME=%.*s",
+ (int)strlen(conn->request_info.local_uri)
+ - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)),
+ conn->request_info.local_uri);
+
+ addenv(env, "SCRIPT_FILENAME=%s", prog);
+ if (conn->path_info == NULL) {
+ addenv(env, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]);
+ } else {
+ addenv(env,
+ "PATH_TRANSLATED=%s%s",
+ conn->ctx->config[DOCUMENT_ROOT],
+ conn->path_info);
+ }
+
+ addenv(env, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
+
+ if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
+ addenv(env, "CONTENT_TYPE=%s", s);
+ }
+ if (conn->request_info.query_string != NULL) {
+ addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
+ }
+ if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
+ addenv(env, "CONTENT_LENGTH=%s", s);
+ }
+ if ((s = getenv("PATH")) != NULL) {
+ addenv(env, "PATH=%s", s);
+ }
+ if (conn->path_info != NULL) {
+ addenv(env, "PATH_INFO=%s", conn->path_info);
+ }
+
+ if (conn->status_code > 0) {
+ /* CGI error handler should show the status code */
+ addenv(env, "STATUS=%d", conn->status_code);
+ }
+
+#if defined(_WIN32)
+ if ((s = getenv("COMSPEC")) != NULL) {
+ addenv(env, "COMSPEC=%s", s);
+ }
+ if ((s = getenv("SYSTEMROOT")) != NULL) {
+ addenv(env, "SYSTEMROOT=%s", s);
+ }
+ if ((s = getenv("SystemDrive")) != NULL) {
+ addenv(env, "SystemDrive=%s", s);
+ }
+ if ((s = getenv("ProgramFiles")) != NULL) {
+ addenv(env, "ProgramFiles=%s", s);
+ }
+ if ((s = getenv("ProgramFiles(x86)")) != NULL) {
+ addenv(env, "ProgramFiles(x86)=%s", s);
+ }
+#else
+ if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
+ addenv(env, "LD_LIBRARY_PATH=%s", s);
+ }
+#endif /* _WIN32 */
+
+ if ((s = getenv("PERLLIB")) != NULL) {
+ addenv(env, "PERLLIB=%s", s);
+ }
+
+ if (conn->request_info.remote_user != NULL) {
+ addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
+ addenv(env, "%s", "AUTH_TYPE=Digest");
+ }
+
+ /* Add all headers as HTTP_* variables */
+ for (i = 0; i < conn->request_info.num_headers; i++) {
+
+ (void)mg_snprintf(conn,
+ &truncated,
+ http_var_name,
+ sizeof(http_var_name),
+ "HTTP_%s",
+ conn->request_info.http_headers[i].name);
+
+ if (truncated) {
+ mg_cry(conn,
+ "%s: HTTP header variable too long [%s]",
+ __func__,
+ conn->request_info.http_headers[i].name);
+ continue;
+ }
+
+ /* Convert variable name into uppercase, and change - to _ */
+ for (p = http_var_name; *p != '\0'; p++) {
+ if (*p == '-') {
+ *p = '_';
+ }
+ *p = (char)toupper(*(unsigned char *)p);
+ }
+
+ addenv(env,
+ "%s=%s",
+ http_var_name,
+ conn->request_info.http_headers[i].value);
+ }
+
+ /* Add user-specified variables */
+ s = conn->ctx->config[CGI_ENVIRONMENT];
+ while ((s = next_option(s, &var_vec, NULL)) != NULL) {
+ addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
+ }
+
+ env->var[env->varused] = NULL;
+ env->buf[env->bufused] = '\0';
+}
+
+
+static void
+handle_cgi_request(struct mg_connection *conn, const char *prog)
+{
+ char *buf;
+ size_t buflen;
+ int headers_len, data_len, i, truncated;
+ int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
+ const char *status, *status_text, *connection_state;
+ char *pbuf, dir[PATH_MAX], *p;
+ struct mg_request_info ri;
+ struct cgi_environment blk;
+ FILE *in = NULL, *out = NULL, *err = NULL;
+ struct file fout = STRUCT_FILE_INITIALIZER;
+ pid_t pid = (pid_t)-1;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ buf = NULL;
+ buflen = 16384;
+ prepare_cgi_environment(conn, prog, &blk);
+
+ /* CGI must be executed in its own directory. 'dir' must point to the
+ * directory containing executable program, 'p' must point to the
+ * executable program name relative to 'dir'. */
+ (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
+
+ if (truncated) {
+ mg_cry(conn, "Error: CGI program \"%s\": Path too long", prog);
+ send_http_error(conn, 500, "Error: %s", "CGI path too long");
+ goto done;
+ }
+
+ if ((p = strrchr(dir, '/')) != NULL) {
+ *p++ = '\0';
+ } else {
+ dir[0] = '.', dir[1] = '\0';
+ p = (char *)prog;
+ }
+
+ if (pipe(fdin) != 0 || pipe(fdout) != 0 || pipe(fderr) != 0) {
+ status = strerror(ERRNO);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Can not create CGI pipes: %s",
+ prog,
+ status);
+ send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", status);
+ goto done;
+ }
+
+ pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir);
+
+ if (pid == (pid_t)-1) {
+ status = strerror(ERRNO);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Can not spawn CGI process: %s",
+ prog,
+ status);
+ send_http_error(conn,
+ 500,
+ "Error: Cannot spawn CGI process [%s]: %s",
+ prog,
+ status);
+ goto done;
+ }
+
+ /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */
+ set_close_on_exec((SOCKET)fdin[0], conn); /* stdin read */
+ set_close_on_exec((SOCKET)fdout[1], conn); /* stdout write */
+ set_close_on_exec((SOCKET)fderr[1], conn); /* stderr write */
+ set_close_on_exec((SOCKET)fdin[1], conn); /* stdin write */
+ set_close_on_exec((SOCKET)fdout[0], conn); /* stdout read */
+ set_close_on_exec((SOCKET)fderr[0], conn); /* stderr read */
+
+ /* Parent closes only one side of the pipes.
+ * If we don't mark them as closed, close() attempt before
+ * return from this function throws an exception on Windows.
+ * Windows does not like when closed descriptor is closed again. */
+ (void)close(fdin[0]);
+ (void)close(fdout[1]);
+ (void)close(fderr[1]);
+ fdin[0] = fdout[1] = fderr[1] = -1;
+
+ if ((in = fdopen(fdin[1], "wb")) == NULL) {
+ status = strerror(ERRNO);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Can not open stdin: %s",
+ prog,
+ status);
+ send_http_error(conn,
+ 500,
+ "Error: CGI can not open fdin\nfopen: %s",
+ status);
+ goto done;
+ }
+
+ if ((out = fdopen(fdout[0], "rb")) == NULL) {
+ status = strerror(ERRNO);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Can not open stdout: %s",
+ prog,
+ status);
+ send_http_error(conn,
+ 500,
+ "Error: CGI can not open fdout\nfopen: %s",
+ status);
+ goto done;
+ }
+
+ if ((err = fdopen(fderr[0], "rb")) == NULL) {
+ status = strerror(ERRNO);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Can not open stderr: %s",
+ prog,
+ status);
+ send_http_error(conn,
+ 500,
+ "Error: CGI can not open fdout\nfopen: %s",
+ status);
+ goto done;
+ }
+
+ setbuf(in, NULL);
+ setbuf(out, NULL);
+ setbuf(err, NULL);
+ fout.fp = out;
+
+ if ((conn->request_info.content_length > 0) || conn->is_chunked) {
+ /* This is a POST/PUT request, or another request with body data. */
+ if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
+ /* Error sending the body data */
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Forward body data failed",
+ prog);
+ goto done;
+ }
+ }
+
+ /* Close so child gets an EOF. */
+ fclose(in);
+ in = NULL;
+ fdin[1] = -1;
+
+ /* Now read CGI reply into a buffer. We need to set correct
+ * status code, thus we need to see all HTTP headers first.
+ * Do not send anything back to client, until we buffer in all
+ * HTTP headers. */
+ data_len = 0;
+ buf = (char *)mg_malloc(buflen);
+ if (buf == NULL) {
+ send_http_error(conn,
+ 500,
+ "Error: Not enough memory for CGI buffer (%u bytes)",
+ (unsigned int)buflen);
+ mg_cry(conn,
+ "Error: CGI program \"%s\": Not enough memory for buffer (%u "
+ "bytes)",
+ prog,
+ (unsigned int)buflen);
+ goto done;
+ }
+ headers_len = read_request(out, conn, buf, (int)buflen, &data_len);
+ if (headers_len <= 0) {
+
+ /* Could not parse the CGI response. Check if some error message on
+ * stderr. */
+ i = pull_all(err, conn, buf, (int)buflen);
+ if (i > 0) {
+ mg_cry(conn,
+ "Error: CGI program \"%s\" sent error "
+ "message: [%.*s]",
+ prog,
+ i,
+ buf);
+ send_http_error(conn,
+ 500,
+ "Error: CGI program \"%s\" sent error "
+ "message: [%.*s]",
+ prog,
+ i,
+ buf);
+ } else {
+ mg_cry(conn,
+ "Error: CGI program sent malformed or too big "
+ "(>%u bytes) HTTP headers: [%.*s]",
+ (unsigned)buflen,
+ data_len,
+ buf);
+
+ send_http_error(conn,
+ 500,
+ "Error: CGI program sent malformed or too big "
+ "(>%u bytes) HTTP headers: [%.*s]",
+ (unsigned)buflen,
+ data_len,
+ buf);
+ }
+
+ goto done;
+ }
+ pbuf = buf;
+ buf[headers_len - 1] = '\0';
+ parse_http_headers(&pbuf, &ri);
+
+ /* Make up and send the status line */
+ status_text = "OK";
+ if ((status = get_header(&ri, "Status")) != NULL) {
+ conn->status_code = atoi(status);
+ status_text = status;
+ while (isdigit(*(const unsigned char *)status_text)
+ || *status_text == ' ') {
+ status_text++;
+ }
+ } else if (get_header(&ri, "Location") != NULL) {
+ conn->status_code = 302;
+ } else {
+ conn->status_code = 200;
+ }
+ connection_state = get_header(&ri, "Connection");
+ if (!header_has_option(connection_state, "keep-alive")) {
+ conn->must_close = 1;
+ }
+ (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
+
+ /* Send headers */
+ for (i = 0; i < ri.num_headers; i++) {
+ mg_printf(conn,
+ "%s: %s\r\n",
+ ri.http_headers[i].name,
+ ri.http_headers[i].value);
+ }
+ mg_write(conn, "\r\n", 2);
+
+ /* Send chunk of data that may have been read after the headers */
+ conn->num_bytes_sent +=
+ mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
+
+ /* Read the rest of CGI output and send to the client */
+ send_file_data(conn, &fout, 0, INT64_MAX);
+
+done:
+ mg_free(blk.var);
+ mg_free(blk.buf);
+
+ if (pid != (pid_t)-1) {
+ kill(pid, SIGKILL);
+#if !defined(_WIN32)
+ {
+ int st;
+ while (waitpid(pid, &st, 0) != -1)
+ ; /* clean zombies */
+ }
+#endif
+ }
+ if (fdin[0] != -1) {
+ close(fdin[0]);
+ }
+ if (fdout[1] != -1) {
+ close(fdout[1]);
+ }
+
+ if (in != NULL) {
+ fclose(in);
+ } else if (fdin[1] != -1) {
+ close(fdin[1]);
+ }
+
+ if (out != NULL) {
+ fclose(out);
+ } else if (fdout[0] != -1) {
+ close(fdout[0]);
+ }
+
+ if (err != NULL) {
+ fclose(err);
+ } else if (fderr[0] != -1) {
+ close(fderr[0]);
+ }
+
+ if (buf != NULL) {
+ mg_free(buf);
+ }
+}
+#endif /* !NO_CGI */
+
+
+#if !defined(NO_FILES)
+static void
+mkcol(struct mg_connection *conn, const char *path)
+{
+ int rc, body_len;
+ struct de de;
+ char date[64];
+ time_t curtime = time(NULL);
+
+ if (conn == NULL) {
+ return;
+ }
+
+ /* TODO (mid): Check the send_http_error situations in this function */
+
+ memset(&de.file, 0, sizeof(de.file));
+ if (!mg_stat(conn, path, &de.file)) {
+ mg_cry(conn,
+ "%s: mg_stat(%s) failed: %s",
+ __func__,
+ path,
+ strerror(ERRNO));
+ }
+
+ if (de.file.last_modified) {
+ /* TODO (high): This check does not seem to make any sense ! */
+ send_http_error(
+ conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+ return;
+ }
+
+ body_len = conn->data_len - conn->request_len;
+ if (body_len > 0) {
+ send_http_error(
+ conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+ return;
+ }
+
+ rc = mg_mkdir(conn, path, 0755);
+
+ if (rc == 0) {
+ conn->status_code = 201;
+ gmt_time_string(date, sizeof(date), &curtime);
+ mg_printf(conn,
+ "HTTP/1.1 %d Created\r\n"
+ "Date: %s\r\n",
+ conn->status_code,
+ date);
+ send_static_cache_header(conn);
+ mg_printf(conn,
+ "Content-Length: 0\r\n"
+ "Connection: %s\r\n\r\n",
+ suggest_connection_header(conn));
+ } else if (rc == -1) {
+ if (errno == EEXIST) {
+ send_http_error(
+ conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+ } else if (errno == EACCES) {
+ send_http_error(
+ conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+ } else if (errno == ENOENT) {
+ send_http_error(
+ conn, 409, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+ } else {
+ send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO));
+ }
+ }
+}
+
+
+static void
+put_file(struct mg_connection *conn, const char *path)
+{
+ struct file file = STRUCT_FILE_INITIALIZER;
+ const char *range;
+ int64_t r1, r2;
+ int rc;
+ char date[64];
+ time_t curtime = time(NULL);
+
+ if (conn == NULL) {
+ return;
+ }
+
+ if (mg_stat(conn, path, &file)) {
+ /* File already exists */
+ conn->status_code = 200;
+
+ if (file.is_directory) {
+ /* This is an already existing directory,
+ * so there is nothing to do for the server. */
+ rc = 0;
+
+ } else {
+ /* File exists and is not a directory. */
+ /* Can it be replaced? */
+
+ if (file.membuf != NULL) {
+ /* This is an "in-memory" file, that can not be replaced */
+ send_http_error(
+ conn,
+ 405,
+ "Error: Put not possible\nReplacing %s is not supported",
+ path);
+ return;
+ }
+
+ /* Check if the server may write this file */
+ if (access(path, W_OK) == 0) {
+ /* Access granted */
+ conn->status_code = 200;
+ rc = 1;
+ } else {
+ send_http_error(
+ conn,
+ 403,
+ "Error: Put not possible\nReplacing %s is not allowed",
+ path);
+ return;
+ }
+ }
+ } else {
+ /* File should be created */
+ conn->status_code = 201;
+ rc = put_dir(conn, path);
+ }
+
+ if (rc == 0) {
+ /* put_dir returns 0 if path is a directory */
+ gmt_time_string(date, sizeof(date), &curtime);
+ mg_printf(conn,
+ "HTTP/1.1 %d %s\r\n",
+ conn->status_code,
+ mg_get_response_code_text(NULL, conn->status_code));
+ send_no_cache_header(conn);
+ mg_printf(conn,
+ "Date: %s\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: %s\r\n\r\n",
+ date,
+ suggest_connection_header(conn));
+
+ /* Request to create a directory has been fulfilled successfully.
+ * No need to put a file. */
+ return;
+ }
+
+ if (rc == -1) {
+ /* put_dir returns -1 if the path is too long */
+ send_http_error(conn,
+ 414,
+ "Error: Path too long\nput_dir(%s): %s",
+ path,
+ strerror(ERRNO));
+ return;
+ }
+
+ if (rc == -2) {
+ /* put_dir returns -2 if the directory can not be created */
+ send_http_error(conn,
+ 500,
+ "Error: Can not create directory\nput_dir(%s): %s",
+ path,
+ strerror(ERRNO));
+ return;
+ }
+
+ /* A file should be created or overwritten. */
+ if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) {
+ mg_fclose(&file);
+ send_http_error(conn,
+ 500,
+ "Error: Can not create file\nfopen(%s): %s",
+ path,
+ strerror(ERRNO));
+ return;
+ }
+
+ fclose_on_exec(&file, conn);
+ range = mg_get_header(conn, "Content-Range");
+ r1 = r2 = 0;
+ if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
+ conn->status_code = 206; /* Partial content */
+ fseeko(file.fp, r1, SEEK_SET);
+ }
+
+ if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) {
+ /* forward_body_data failed.
+ * The error code has already been sent to the client,
+ * and conn->status_code is already set. */
+ mg_fclose(&file);
+ return;
+ }
+
+ gmt_time_string(date, sizeof(date), &curtime);
+ mg_printf(conn,
+ "HTTP/1.1 %d %s\r\n",
+ conn->status_code,
+ mg_get_response_code_text(NULL, conn->status_code));
+ send_no_cache_header(conn);
+ mg_printf(conn,
+ "Date: %s\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: %s\r\n\r\n",
+ date,
+ suggest_connection_header(conn));
+
+ mg_fclose(&file);
+}
+
+
+static void
+delete_file(struct mg_connection *conn, const char *path)
+{
+ struct de de;
+ memset(&de.file, 0, sizeof(de.file));
+ if (!mg_stat(conn, path, &de.file)) {
+ /* mg_stat returns 0 if the file does not exist */
+ send_http_error(conn,
+ 404,
+ "Error: Cannot delete file\nFile %s not found",
+ path);
+ return;
+ }
+
+ if (de.file.membuf != NULL) {
+ /* the file is cached in memory */
+ send_http_error(
+ conn,
+ 405,
+ "Error: Delete not possible\nDeleting %s is not supported",
+ path);
+ return;
+ }
+
+ if (de.file.is_directory) {
+ if (remove_directory(conn, path)) {
+ /* Delete is successful: Return 204 without content. */
+ send_http_error(conn, 204, "%s", "");
+ } else {
+ /* Delete is not successful: Return 500 (Server error). */
+ send_http_error(conn, 500, "Error: Could not delete %s", path);
+ }
+ return;
+ }
+
+ /* This is an existing file (not a directory).
+ * Check if write permission is granted. */
+ if (access(path, W_OK) != 0) {
+ /* File is read only */
+ send_http_error(
+ conn,
+ 403,
+ "Error: Delete not possible\nDeleting %s is not allowed",
+ path);
+ return;
+ }
+
+ /* Try to delete it. */
+ if (mg_remove(conn, path) == 0) {
+ /* Delete was successful: Return 204 without content. */
+ send_http_error(conn, 204, "%s", "");
+ } else {
+ /* Delete not successful (file locked). */
+ send_http_error(conn,
+ 423,
+ "Error: Cannot delete file\nremove(%s): %s",
+ path,
+ strerror(ERRNO));
+ }
+}
+#endif /* !NO_FILES */
+
+
+static void
+send_ssi_file(struct mg_connection *, const char *, struct file *, int);
+
+
+static void
+do_ssi_include(struct mg_connection *conn,
+ const char *ssi,
+ char *tag,
+ int include_level)
+{
+ char file_name[MG_BUF_LEN], path[512], *p;
+ struct file file = STRUCT_FILE_INITIALIZER;
+ size_t len;
+ int truncated = 0;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ /* sscanf() is safe here, since send_ssi_file() also uses buffer
+ * of size MG_BUF_LEN to get the tag. So strlen(tag) is
+ * always < MG_BUF_LEN. */
+ if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
+ /* File name is relative to the webserver root */
+ file_name[511] = 0;
+ (void)mg_snprintf(conn,
+ &truncated,
+ path,
+ sizeof(path),
+ "%s/%s",
+ conn->ctx->config[DOCUMENT_ROOT],
+ file_name);
+
+ } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
+ /* File name is relative to the webserver working directory
+ * or it is absolute system path */
+ file_name[511] = 0;
+ (void)
+ mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
+
+ } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1
+ || sscanf(tag, " \"%511[^\"]\"", file_name) == 1) {
+ /* File name is relative to the currect document */
+ file_name[511] = 0;
+ (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
+
+ if (!truncated) {
+ if ((p = strrchr(path, '/')) != NULL) {
+ p[1] = '\0';
+ }
+ len = strlen(path);
+ (void)mg_snprintf(conn,
+ &truncated,
+ path + len,
+ sizeof(path) - len,
+ "%s",
+ file_name);
+ }
+
+ } else {
+ mg_cry(conn, "Bad SSI #include: [%s]", tag);
+ return;
+ }
+
+ if (truncated) {
+ mg_cry(conn, "SSI #include path length overflow: [%s]", tag);
+ return;
+ }
+
+ if (!mg_fopen(conn, path, "rb", &file)) {
+ mg_cry(conn,
+ "Cannot open SSI #include: [%s]: fopen(%s): %s",
+ tag,
+ path,
+ strerror(ERRNO));
+ } else {
+ fclose_on_exec(&file, conn);
+ if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
+ strlen(conn->ctx->config[SSI_EXTENSIONS]),
+ path) > 0) {
+ send_ssi_file(conn, path, &file, include_level + 1);
+ } else {
+ send_file_data(conn, &file, 0, INT64_MAX);
+ }
+ mg_fclose(&file);
+ }
+}
+
+
+#if !defined(NO_POPEN)
+static void
+do_ssi_exec(struct mg_connection *conn, char *tag)
+{
+ char cmd[1024] = "";
+ struct file file = STRUCT_FILE_INITIALIZER;
+
+ if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
+ mg_cry(conn, "Bad SSI #exec: [%s]", tag);
+ } else {
+ cmd[1023] = 0;
+ if ((file.fp = popen(cmd, "r")) == NULL) {
+ mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
+ } else {
+ send_file_data(conn, &file, 0, INT64_MAX);
+ pclose(file.fp);
+ }
+ }
+}
+#endif /* !NO_POPEN */
+
+
+static int
+mg_fgetc(struct file *filep, int offset)
+{
+ if (filep == NULL) {
+ return EOF;
+ }
+ if (filep->membuf != NULL && offset >= 0
+ && ((unsigned int)(offset)) < filep->size) {
+ return ((const unsigned char *)filep->membuf)[offset];
+ } else if (filep->fp != NULL) {
+ return fgetc(filep->fp);
+ } else {
+ return EOF;
+ }
+}
+
+
+static void
+send_ssi_file(struct mg_connection *conn,
+ const char *path,
+ struct file *filep,
+ int include_level)
+{
+ char buf[MG_BUF_LEN];
+ int ch, offset, len, in_ssi_tag;
+
+ if (include_level > 10) {
+ mg_cry(conn, "SSI #include level is too deep (%s)", path);
+ return;
+ }
+
+ in_ssi_tag = len = offset = 0;
+ while ((ch = mg_fgetc(filep, offset)) != EOF) {
+ if (in_ssi_tag && ch == '>') {
+ in_ssi_tag = 0;
+ buf[len++] = (char)ch;
+ buf[len] = '\0';
+ /* assert(len <= (int) sizeof(buf)); */
+ if (len > (int)sizeof(buf)) {
+ break;
+ }
+ if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
+ /* Not an SSI tag, pass it */
+ (void)mg_write(conn, buf, (size_t)len);
+ } else {
+ if (!memcmp(buf + 5, "include", 7)) {
+ do_ssi_include(conn, path, buf + 12, include_level);
+#if !defined(NO_POPEN)
+ } else if (!memcmp(buf + 5, "exec", 4)) {
+ do_ssi_exec(conn, buf + 9);
+#endif /* !NO_POPEN */
+ } else {
+ mg_cry(conn,
+ "%s: unknown SSI "
+ "command: \"%s\"",
+ path,
+ buf);
+ }
+ }
+ len = 0;
+ } else if (in_ssi_tag) {
+ if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
+ /* Not an SSI tag */
+ in_ssi_tag = 0;
+ } else if (len == (int)sizeof(buf) - 2) {
+ mg_cry(conn, "%s: SSI tag is too large", path);
+ len = 0;
+ }
+ buf[len++] = (char)(ch & 0xff);
+ } else if (ch == '<') {
+ in_ssi_tag = 1;
+ if (len > 0) {
+ mg_write(conn, buf, (size_t)len);
+ }
+ len = 0;
+ buf[len++] = (char)(ch & 0xff);
+ } else {
+ buf[len++] = (char)(ch & 0xff);
+ if (len == (int)sizeof(buf)) {
+ mg_write(conn, buf, (size_t)len);
+ len = 0;
+ }
+ }
+ }
+
+ /* Send the rest of buffered data */
+ if (len > 0) {
+ mg_write(conn, buf, (size_t)len);
+ }
+}
+
+
+static void
+handle_ssi_file_request(struct mg_connection *conn,
+ const char *path,
+ struct file *filep)
+{
+ char date[64];
+ time_t curtime = time(NULL);
+ const char *cors1, *cors2, *cors3;
+
+ if (conn == NULL || path == NULL || filep == NULL) {
+ return;
+ }
+
+ if (mg_get_header(conn, "Origin")) {
+ /* Cross-origin resource sharing (CORS). */
+ cors1 = "Access-Control-Allow-Origin: ";
+ cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
+ cors3 = "\r\n";
+ } else {
+ cors1 = cors2 = cors3 = "";
+ }
+
+ if (!mg_fopen(conn, path, "rb", filep)) {
+ /* File exists (precondition for calling this function),
+ * but can not be opened by the server. */
+ send_http_error(conn,
+ 500,
+ "Error: Cannot read file\nfopen(%s): %s",
+ path,
+ strerror(ERRNO));
+ } else {
+ conn->must_close = 1;
+ gmt_time_string(date, sizeof(date), &curtime);
+ fclose_on_exec(filep, conn);
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+ send_no_cache_header(conn);
+ mg_printf(conn,
+ "%s%s%s"
+ "Date: %s\r\n"
+ "Content-Type: text/html\r\n"
+ "Connection: %s\r\n\r\n",
+ cors1,
+ cors2,
+ cors3,
+ date,
+ suggest_connection_header(conn));
+ send_ssi_file(conn, path, filep, 0);
+ mg_fclose(filep);
+ }
+}
+
+
+#if !defined(NO_FILES)
+static void
+send_options(struct mg_connection *conn)
+{
+ char date[64];
+ time_t curtime = time(NULL);
+
+ if (!conn) {
+ return;
+ }
+
+ conn->status_code = 200;
+ conn->must_close = 1;
+ gmt_time_string(date, sizeof(date), &curtime);
+
+ mg_printf(conn,
+ "HTTP/1.1 200 OK\r\n"
+ "Date: %s\r\n"
+ /* TODO: "Cache-Control" (?) */
+ "Connection: %s\r\n"
+ "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, "
+ "PROPFIND, MKCOL\r\n"
+ "DAV: 1\r\n\r\n",
+ date,
+ suggest_connection_header(conn));
+}
+
+
+/* Writes PROPFIND properties for a collection element */
+static void
+print_props(struct mg_connection *conn, const char *uri, struct file *filep)
+{
+ char mtime[64];
+
+ if (conn == NULL || uri == NULL || filep == NULL) {
+ return;
+ }
+
+ gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
+ conn->num_bytes_sent +=
+ mg_printf(conn,
+ "<d:response>"
+ "<d:href>%s</d:href>"
+ "<d:propstat>"
+ "<d:prop>"
+ "<d:resourcetype>%s</d:resourcetype>"
+ "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
+ "<d:getlastmodified>%s</d:getlastmodified>"
+ "</d:prop>"
+ "<d:status>HTTP/1.1 200 OK</d:status>"
+ "</d:propstat>"
+ "</d:response>\n",
+ uri,
+ filep->is_directory ? "<d:collection/>" : "",
+ filep->size,
+ mtime);
+}
+
+
+static void
+print_dav_dir_entry(struct de *de, void *data)
+{
+ char href[PATH_MAX];
+ char href_encoded[PATH_MAX];
+ int truncated;
+
+ struct mg_connection *conn = (struct mg_connection *)data;
+ if (!de || !conn) {
+ return;
+ }
+ mg_snprintf(conn,
+ &truncated,
+ href,
+ sizeof(href),
+ "%s%s",
+ conn->request_info.local_uri,
+ de->file_name);
+
+ if (!truncated) {
+ mg_url_encode(href, href_encoded, PATH_MAX - 1);
+ print_props(conn, href_encoded, &de->file);
+ }
+}
+
+
+static void
+handle_propfind(struct mg_connection *conn,
+ const char *path,
+ struct file *filep)
+{
+ const char *depth = mg_get_header(conn, "Depth");
+ char date[64];
+ time_t curtime = time(NULL);
+
+ gmt_time_string(date, sizeof(date), &curtime);
+
+ if (!conn || !path || !filep || !conn->ctx) {
+ return;
+ }
+
+ conn->must_close = 1;
+ conn->status_code = 207;
+ mg_printf(conn,
+ "HTTP/1.1 207 Multi-Status\r\n"
+ "Date: %s\r\n",
+ date);
+ send_static_cache_header(conn);
+ mg_printf(conn,
+ "Connection: %s\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n\r\n",
+ suggest_connection_header(conn));
+
+ conn->num_bytes_sent +=
+ mg_printf(conn,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<d:multistatus xmlns:d='DAV:'>\n");
+
+ /* Print properties for the requested resource itself */
+ print_props(conn, conn->request_info.local_uri, filep);
+
+ /* If it is a directory, print directory entries too if Depth is not 0 */
+ if (filep && filep->is_directory
+ && !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")
+ && (depth == NULL || strcmp(depth, "0") != 0)) {
+ scan_directory(conn, path, conn, &print_dav_dir_entry);
+ }
+
+ conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
+}
+#endif
+
+void
+mg_lock_connection(struct mg_connection *conn)
+{
+ if (conn) {
+ (void)pthread_mutex_lock(&conn->mutex);
+ }
+}
+
+void
+mg_unlock_connection(struct mg_connection *conn)
+{
+ if (conn) {
+ (void)pthread_mutex_unlock(&conn->mutex);
+ }
+}
+
+void
+mg_lock_context(struct mg_context *ctx)
+{
+ if (ctx) {
+ (void)pthread_mutex_lock(&ctx->nonce_mutex);
+ }
+}
+
+void
+mg_unlock_context(struct mg_context *ctx)
+{
+ if (ctx) {
+ (void)pthread_mutex_unlock(&ctx->nonce_mutex);
+ }
+}
+
+#if defined(USE_TIMERS)
+#include "timer.inl"
+#endif /* USE_TIMERS */
+
+#ifdef USE_LUA
+#include "mod_lua.inl"
+#endif /* USE_LUA */
+
+#ifdef USE_DUKTAPE
+#include "mod_duktape.inl"
+#endif /* USE_DUKTAPE */
+
+#if defined(USE_WEBSOCKET)
+
+/* START OF SHA-1 code
+ * Copyright(c) By Steve Reid <steve@edmweb.com> */
+#define SHA1HANDSOFF
+
+/* According to current tests (May 2015), the <solarisfixes.h> is not required.
+ *
+ * #if defined(__sun)
+ * #include "solarisfixes.h"
+ * #endif
+ */
+
+
+static int
+is_big_endian(void)
+{
+ static const int n = 1;
+ return ((char *)&n)[0] == 0;
+}
+
+
+union char64long16 {
+ unsigned char c[64];
+ uint32_t l[16];
+};
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+
+static uint32_t
+blk0(union char64long16 *block, int i)
+{
+ /* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */
+ if (!is_big_endian()) {
+ block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00)
+ | (rol(block->l[i], 8) & 0x00FF00FF);
+ }
+ return block->l[i];
+}
+
+#define blk(i) \
+ (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] \
+ ^ block->l[(i + 2) & 15] ^ block->l[i & 15], \
+ 1))
+#define R0(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
+ w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w = rol(w, 30);
+
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+
+static void
+SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+ uint32_t a, b, c, d, e;
+ union char64long16 block[1];
+
+ memcpy(block, buffer, 64);
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ a = b = c = d = e = 0;
+ memset(block, '\0', sizeof(block));
+}
+
+
+static void
+SHA1Init(SHA1_CTX *context)
+{
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+static void
+SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len)
+{
+ uint32_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j) {
+ context->count[1]++;
+ }
+ context->count[1] += (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ SHA1Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+static void
+SHA1Final(unsigned char digest[20], SHA1_CTX *context)
+{
+ unsigned i;
+ unsigned char finalcount[8], c;
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8);
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)((context->state[i >> 2]
+ >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* END OF SHA1 CODE */
+
+
+static int
+send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
+{
+ static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ const char *protocol = NULL;
+ char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
+ SHA1_CTX sha_ctx;
+ int truncated;
+
+ /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */
+ mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic);
+ if (truncated) {
+ conn->must_close = 1;
+ return 0;
+ }
+
+ SHA1Init(&sha_ctx);
+ SHA1Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf));
+ SHA1Final((unsigned char *)sha, &sha_ctx);
+ base64_encode((unsigned char *)sha, sizeof(sha), b64_sha);
+ mg_printf(conn,
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n",
+ b64_sha);
+ protocol = mg_get_header(conn, "Sec-WebSocket-Protocol");
+ if (protocol) {
+ /* The protocol is a comma seperated list of names. */
+ /* The server must only return one value from this list. */
+ /* First check if it is a list or just a single value. */
+ const char *sep = strchr(protocol, ',');
+ if (sep == NULL) {
+ /* Just a single protocol -> accept it. */
+ mg_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", protocol);
+ } else {
+ /* Multiple protocols -> accept the first one. */
+ /* This is just a quick fix if the client offers multiple
+ * protocols. In order to get the behavior intended by
+ * RFC 6455 (https://tools.ietf.org/rfc/rfc6455.txt), it is
+ * required to have a list of websocket subprotocols accepted
+ * by the server. Then the server must either select a subprotocol
+ * supported by client and server, or the server has to abort the
+ * handshake by not returning a Sec-Websocket-Protocol header if
+ * no subprotocol is acceptable.
+ */
+ mg_printf(conn,
+ "Sec-WebSocket-Protocol: %.*s\r\n\r\n",
+ (int)(sep - protocol),
+ protocol);
+ }
+ /* TODO: Real subprotocol negotiation instead of just taking the first
+ * websocket subprotocol suggested by the client. */
+ } else {
+ mg_printf(conn, "%s", "\r\n");
+ }
+
+ return 1;
+}
+
+
+static void
+read_websocket(struct mg_connection *conn,
+ mg_websocket_data_handler ws_data_handler,
+ void *callback_data)
+{
+ /* Pointer to the beginning of the portion of the incoming websocket
+ * message queue.
+ * The original websocket upgrade request is never removed, so the queue
+ * begins after it. */
+ unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
+ int n, error, exit_by_callback;
+
+ /* body_len is the length of the entire queue in bytes
+ * len is the length of the current message
+ * data_len is the length of the current message's data payload
+ * header_len is the length of the current message's header */
+ size_t i, len, mask_len = 0, data_len = 0, header_len, body_len;
+
+ /* "The masking key is a 32-bit value chosen at random by the client."
+ * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
+ */
+ unsigned char mask[4];
+
+ /* data points to the place where the message is stored when passed to
+ * the
+ * websocket_data callback. This is either mem on the stack, or a
+ * dynamically allocated buffer if it is too large. */
+ char mem[4096];
+ char *data = mem;
+ unsigned char mop; /* mask flag and opcode */
+ double timeout = -1.0;
+
+ if (conn->ctx->config[WEBSOCKET_TIMEOUT]) {
+ timeout = atoi(conn->ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
+ }
+ if ((timeout <= 0.0) && (conn->ctx->config[REQUEST_TIMEOUT])) {
+ timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+ }
+
+ mg_set_thread_name("wsock");
+
+ /* Loop continuously, reading messages from the socket, invoking the
+ * callback, and waiting repeatedly until an error occurs. */
+ while (!conn->ctx->stop_flag) {
+ header_len = 0;
+ assert(conn->data_len >= conn->request_len);
+ if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
+ len = buf[1] & 127;
+ mask_len = buf[1] & 128 ? 4 : 0;
+ if (len < 126 && body_len >= mask_len) {
+ data_len = len;
+ header_len = 2 + mask_len;
+ } else if (len == 126 && body_len >= 4 + mask_len) {
+ header_len = 4 + mask_len;
+ data_len = ((((size_t)buf[2]) << 8) + buf[3]);
+ } else if (body_len >= 10 + mask_len) {
+ header_len = 10 + mask_len;
+ data_len = (((uint64_t)ntohl(*(uint32_t *)(void *)&buf[2]))
+ << 32) + ntohl(*(uint32_t *)(void *)&buf[6]);
+ }
+ }
+
+ if (header_len > 0 && body_len >= header_len) {
+ /* Allocate space to hold websocket payload */
+ data = mem;
+ if (data_len > sizeof(mem)) {
+ data = (char *)mg_malloc(data_len);
+ if (data == NULL) {
+ /* Allocation failed, exit the loop and then close the
+ * connection */
+ mg_cry(conn, "websocket out of memory; closing connection");
+ break;
+ }
+ }
+
+ /* Copy the mask before we shift the queue and destroy it */
+ if (mask_len > 0) {
+ memcpy(mask, buf + header_len - mask_len, sizeof(mask));
+ } else {
+ memset(mask, 0, sizeof(mask));
+ }
+
+ /* Read frame payload from the first message in the queue into
+ * data and advance the queue by moving the memory in place. */
+ assert(body_len >= header_len);
+ if (data_len + header_len > body_len) {
+ mop = buf[0]; /* current mask and opcode */
+ /* Overflow case */
+ len = body_len - header_len;
+ memcpy(data, buf + header_len, len);
+ error = 0;
+ while (len < data_len) {
+ n = pull(
+ NULL, conn, data + len, (int)(data_len - len), timeout);
+ if (n <= 0) {
+ error = 1;
+ break;
+ }
+ len += (size_t)n;
+ }
+ if (error) {
+ mg_cry(conn, "Websocket pull failed; closing connection");
+ break;
+ }
+ conn->data_len = conn->request_len;
+ } else {
+ mop = buf[0]; /* current mask and opcode, overwritten by
+ * memmove() */
+ /* Length of the message being read at the front of the
+ * queue */
+ len = data_len + header_len;
+
+ /* Copy the data payload into the data pointer for the
+ * callback */
+ memcpy(data, buf + header_len, data_len);
+
+ /* Move the queue forward len bytes */
+ memmove(buf, buf + len, body_len - len);
+
+ /* Mark the queue as advanced */
+ conn->data_len -= (int)len;
+ }
+
+ /* Apply mask if necessary */
+ if (mask_len > 0) {
+ for (i = 0; i < data_len; ++i) {
+ data[i] ^= mask[i & 3];
+ }
+ }
+
+ /* Exit the loop if callback signals to exit (server side),
+ * or "connection close" opcode received (client side). */
+ exit_by_callback = 0;
+ if ((ws_data_handler != NULL)
+ && !ws_data_handler(conn, mop, data, data_len, callback_data)) {
+ exit_by_callback = 1;
+ }
+
+ if (data != mem) {
+ mg_free(data);
+ }
+
+ if (exit_by_callback
+ || ((mop & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE)) {
+ /* Opcode == 8, connection close */
+ break;
+ }
+
+ /* Not breaking the loop, process next websocket frame. */
+ } else {
+ /* Read from the socket into the next available location in the
+ * message queue. */
+ if ((n = pull(NULL,
+ conn,
+ conn->buf + conn->data_len,
+ conn->buf_size - conn->data_len,
+ timeout)) <= 0) {
+ /* Error, no bytes read */
+ break;
+ }
+ conn->data_len += n;
+ }
+ }
+
+ mg_set_thread_name("worker");
+}
+
+
+static int
+mg_websocket_write_exec(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t dataLen,
+ uint32_t masking_key)
+{
+ unsigned char header[14];
+ size_t headerLen = 1;
+
+ int retval = -1;
+
+ header[0] = 0x80 + (opcode & 0xF);
+
+ /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */
+ if (dataLen < 126) {
+ /* inline 7-bit length field */
+ header[1] = (unsigned char)dataLen;
+ headerLen = 2;
+ } else if (dataLen <= 0xFFFF) {
+ /* 16-bit length field */
+ header[1] = 126;
+ *(uint16_t *)(void *)(header + 2) = htons((uint16_t)dataLen);
+ headerLen = 4;
+ } else {
+ /* 64-bit length field */
+ header[1] = 127;
+ *(uint32_t *)(void *)(header + 2) = htonl((uint64_t)dataLen >> 32);
+ *(uint32_t *)(void *)(header + 6) = htonl(dataLen & 0xFFFFFFFF);
+ headerLen = 10;
+ }
+
+ if (masking_key) {
+ /* add mask */
+ header[1] |= 0x80;
+ *(uint32_t *)(void *)(header + headerLen) = masking_key;
+ headerLen += 4;
+ }
+
+
+ /* Note that POSIX/Winsock's send() is threadsafe
+ * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
+ * but mongoose's mg_printf/mg_write is not (because of the loop in
+ * push(), although that is only a problem if the packet is large or
+ * outgoing buffer is full). */
+ (void)mg_lock_connection(conn);
+ retval = mg_write(conn, header, headerLen);
+ if (dataLen > 0) {
+ retval = mg_write(conn, data, dataLen);
+ }
+ mg_unlock_connection(conn);
+
+ return retval;
+}
+
+int
+mg_websocket_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t dataLen)
+{
+ return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
+}
+
+
+static void
+mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out)
+{
+ size_t i = 0;
+
+ i = 0;
+ if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
+ /* Convert in 32 bit words, if data is 4 byte aligned */
+ while (i < (in_len - 3)) {
+ *(uint32_t *)(void *)(out + i) =
+ *(uint32_t *)(void *)(in + i) ^ masking_key;
+ i += 4;
+ }
+ }
+ if (i != in_len) {
+ /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/
+ while (i < in_len) {
+ *(uint8_t *)(void *)(out + i) =
+ *(uint8_t *)(void *)(in + i)
+ ^ *(((uint8_t *)&masking_key) + (i % 4));
+ i++;
+ }
+ }
+}
+
+
+int
+mg_websocket_client_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t dataLen)
+{
+ int retval = -1;
+ char *masked_data = (char *)mg_malloc(((dataLen + 7) / 4) * 4);
+ uint32_t masking_key = (uint32_t)get_random();
+
+ if (masked_data == NULL) {
+ /* Return -1 in an error case */
+ mg_cry(conn,
+ "Cannot allocate buffer for masked websocket response: "
+ "Out of memory");
+ return -1;
+ }
+
+ mask_data(data, dataLen, masking_key, masked_data);
+
+ retval = mg_websocket_write_exec(
+ conn, opcode, masked_data, dataLen, masking_key);
+ mg_free(masked_data);
+
+ return retval;
+}
+
+
+static void
+handle_websocket_request(struct mg_connection *conn,
+ const char *path,
+ int is_callback_resource,
+ mg_websocket_connect_handler ws_connect_handler,
+ mg_websocket_ready_handler ws_ready_handler,
+ mg_websocket_data_handler ws_data_handler,
+ mg_websocket_close_handler ws_close_handler,
+ void *cbData)
+{
+ const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key");
+ const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
+ int lua_websock = 0;
+
+#if !defined(USE_LUA)
+ (void)path;
+#endif
+
+ /* Step 1: Check websocket protocol version. */
+ /* Step 1.1: Check Sec-WebSocket-Key. */
+ if (!websock_key) {
+ /* The RFC standard version (https://tools.ietf.org/html/rfc6455)
+ * requires a Sec-WebSocket-Key header.
+ */
+ /* It could be the hixie draft version
+ * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76).
+ */
+ const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1");
+ const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2");
+ char key3[8];
+
+ if ((key1 != NULL) && (key2 != NULL)) {
+ /* This version uses 8 byte body data in a GET request */
+ conn->content_len = 8;
+ if (8 == mg_read(conn, key3, 8)) {
+ /* This is the hixie version */
+ send_http_error(conn,
+ 426,
+ "%s",
+ "Protocol upgrade to RFC 6455 required");
+ return;
+ }
+ }
+ /* This is an unknown version */
+ send_http_error(conn, 400, "%s", "Malformed websocket request");
+ return;
+ }
+
+ /* Step 1.2: Check websocket protocol version. */
+ /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */
+ if (version == NULL || strcmp(version, "13") != 0) {
+ /* Reject wrong versions */
+ send_http_error(conn, 426, "%s", "Protocol upgrade required");
+ return;
+ }
+
+ /* Step 1.3: Could check for "Host", but we do not really nead this
+ * value for anything, so just ignore it. */
+
+ /* Step 2: If a callback is responsible, call it. */
+ if (is_callback_resource) {
+ if (ws_connect_handler != NULL
+ && ws_connect_handler(conn, cbData) != 0) {
+ /* C callback has returned non-zero, do not proceed with
+ * handshake.
+ */
+ /* Note that C callbacks are no longer called when Lua is
+ * responsible, so C can no longer filter callbacks for Lua. */
+ return;
+ }
+ }
+#if defined(USE_LUA)
+ /* Step 3: No callback. Check if Lua is responsible. */
+ else {
+ /* Step 3.1: Check if Lua is responsible. */
+ if (conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
+ lua_websock =
+ match_prefix(conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS],
+ strlen(
+ conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]),
+ path);
+ }
+
+ if (lua_websock) {
+ /* Step 3.2: Lua is responsible: call it. */
+ conn->lua_websocket_state = lua_websocket_new(path, conn);
+ if (!conn->lua_websocket_state) {
+ /* Lua rejected the new client */
+ return;
+ }
+ }
+ }
+#endif
+
+ /* Step 4: Check if there is a responsible websocket handler. */
+ if (!is_callback_resource && !lua_websock) {
+ /* There is no callback, an Lua is not responsible either. */
+ /* Reply with a 404 Not Found or with nothing at all?
+ * TODO (mid): check the websocket standards, how to reply to
+ * requests to invalid websocket addresses. */
+ send_http_error(conn, 404, "%s", "Not found");
+ return;
+ }
+
+ /* Step 5: The websocket connection has been accepted */
+ if (!send_websocket_handshake(conn, websock_key)) {
+ send_http_error(conn, 500, "%s", "Websocket handshake failed");
+ return;
+ }
+
+ /* Step 6: Call the ready handler */
+ if (is_callback_resource) {
+ if (ws_ready_handler != NULL) {
+ ws_ready_handler(conn, cbData);
+ }
+#if defined(USE_LUA)
+ } else if (lua_websock) {
+ if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
+ /* the ready handler returned false */
+ return;
+ }
+#endif
+ }
+
+ /* Step 7: Enter the read loop */
+ if (is_callback_resource) {
+ read_websocket(conn, ws_data_handler, cbData);
+#if defined(USE_LUA)
+ } else if (lua_websock) {
+ read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
+#endif
+ }
+
+ /* Step 8: Call the close handler */
+ if (ws_close_handler) {
+ ws_close_handler(conn, cbData);
+ }
+}
+
+
+static int
+is_websocket_protocol(const struct mg_connection *conn)
+{
+ const char *upgrade, *connection;
+
+ /* A websocket protocoll has the following HTTP headers:
+ *
+ * Connection: Upgrade
+ * Upgrade: Websocket
+ */
+
+ upgrade = mg_get_header(conn, "Upgrade");
+ if (upgrade == NULL) {
+ return 0; /* fail early, don't waste time checking other header
+ * fields
+ */
+ }
+ if (!mg_strcasestr(upgrade, "websocket")) {
+ return 0;
+ }
+
+ connection = mg_get_header(conn, "Connection");
+ if (connection == NULL) {
+ return 0;
+ }
+ if (!mg_strcasestr(connection, "upgrade")) {
+ return 0;
+ }
+
+ /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
+ * "Sec-WebSocket-Version" are also required.
+ * Don't check them here, since even an unsupported websocket protocol
+ * request still IS a websocket request (in contrast to a standard HTTP
+ * request). It will fail later in handle_websocket_request.
+ */
+
+ return 1;
+}
+#endif /* !USE_WEBSOCKET */
+
+
+static int
+isbyte(int n)
+{
+ return n >= 0 && n <= 255;
+}
+
+
+static int
+parse_net(const char *spec, uint32_t *net, uint32_t *mask)
+{
+ int n, a, b, c, d, slash = 32, len = 0;
+
+ if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5
+ || sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && isbyte(a)
+ && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0
+ && slash < 33) {
+ len = n;
+ *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8)
+ | (uint32_t)d;
+ *mask = slash ? 0xffffffffU << (32 - slash) : 0;
+ }
+
+ return len;
+}
+
+
+static int
+set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
+{
+ int throttle = 0;
+ struct vec vec, val;
+ uint32_t net, mask;
+ char mult;
+ double v;
+
+ while ((spec = next_option(spec, &vec, &val)) != NULL) {
+ mult = ',';
+ if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0
+ || (lowercase(&mult) != 'k' && lowercase(&mult) != 'm'
+ && mult != ',')) {
+ continue;
+ }
+ v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576
+ : 1;
+ if (vec.len == 1 && vec.ptr[0] == '*') {
+ throttle = (int)v;
+ } else if (parse_net(vec.ptr, &net, &mask) > 0) {
+ if ((remote_ip & mask) == net) {
+ throttle = (int)v;
+ }
+ } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
+ throttle = (int)v;
+ }
+ }
+
+ return throttle;
+}
+
+
+static uint32_t
+get_remote_ip(const struct mg_connection *conn)
+{
+ if (!conn) {
+ return 0;
+ }
+ return ntohl(*(const uint32_t *)&conn->client.rsa.sin.sin_addr);
+}
+
+
+/* The mg_upload function is superseeded by mg_handle_form_request. */
+#include "handle_form.inl"
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* Implement the deprecated mg_upload function by calling the new
+ * mg_handle_form_request function. While mg_upload could only handle
+ * HTML forms sent as POST request in multipart/form-data format
+ * containing only file input elements, mg_handle_form_request can
+ * handle all form input elements and all standard request methods. */
+struct mg_upload_user_data {
+ struct mg_connection *conn;
+ const char *destination_dir;
+ int num_uploaded_files;
+};
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_found(const char *key,
+ const char *filename,
+ char *path,
+ size_t pathlen,
+ void *user_data)
+{
+ int truncated = 0;
+ struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
+ (void)key;
+
+ if (!filename) {
+ mg_cry(fud->conn, "%s: No filename set", __func__);
+ return FORM_FIELD_STORAGE_ABORT;
+ }
+ mg_snprintf(fud->conn,
+ &truncated,
+ path,
+ pathlen - 1,
+ "%s/%s",
+ fud->destination_dir,
+ filename);
+ if (!truncated) {
+ mg_cry(fud->conn, "%s: File path too long", __func__);
+ return FORM_FIELD_STORAGE_ABORT;
+ }
+ return FORM_FIELD_STORAGE_STORE;
+}
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_get(const char *key,
+ const char *value,
+ size_t value_size,
+ void *user_data)
+{
+ /* Function should never be called */
+ (void)key;
+ (void)value;
+ (void)value_size;
+ (void)user_data;
+
+ return 0;
+}
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_stored(const char *path, long long file_size, void *user_data)
+{
+ struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
+ (void)file_size;
+
+ fud->num_uploaded_files++;
+ fud->conn->ctx->callbacks.upload(fud->conn, path);
+
+ return 0;
+}
+
+
+/* Deprecated function mg_upload - use mg_handle_form_request instead. */
+int
+mg_upload(struct mg_connection *conn, const char *destination_dir)
+{
+ struct mg_upload_user_data fud = {conn, destination_dir, 0};
+ struct mg_form_data_handler fdh = {mg_upload_field_found,
+ mg_upload_field_get,
+ mg_upload_field_stored,
+ 0};
+ int ret;
+
+ fdh.user_data = (void *)&fud;
+ ret = mg_handle_form_request(conn, &fdh);
+
+ if (ret < 0) {
+ mg_cry(conn, "%s: Error while parsing the request", __func__);
+ }
+
+ return fud.num_uploaded_files;
+}
+#endif
+
+
+static int
+get_first_ssl_listener_index(const struct mg_context *ctx)
+{
+ unsigned int i;
+ int idx = -1;
+ if (ctx) {
+ for (i = 0; idx == -1 && i < ctx->num_listening_sockets; i++) {
+ idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
+ }
+ }
+ return idx;
+}
+
+
+static void
+redirect_to_https_port(struct mg_connection *conn, int ssl_index)
+{
+ char host[1025];
+ const char *host_header;
+ size_t hostlen;
+
+ host_header = mg_get_header(conn, "Host");
+ hostlen = sizeof(host);
+ if (host_header != NULL) {
+ char *pos;
+
+ mg_strlcpy(host, host_header, hostlen);
+ host[hostlen - 1] = '\0';
+ pos = strchr(host, ':');
+ if (pos != NULL) {
+ *pos = '\0';
+ }
+ } else {
+ /* Cannot get host from the Host: header.
+ * Fallback to our IP address. */
+ if (conn) {
+ sockaddr_to_string(host, hostlen, &conn->client.lsa);
+ }
+ }
+
+ /* Send host, port, uri and (if it exists) ?query_string */
+ if (conn) {
+ mg_printf(conn,
+ "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n",
+ host,
+ (int)ntohs(
+ conn->ctx->listening_sockets[ssl_index].lsa.sin.sin_port),
+ conn->request_info.local_uri,
+ (conn->request_info.query_string == NULL) ? "" : "?",
+ (conn->request_info.query_string == NULL)
+ ? ""
+ : conn->request_info.query_string);
+ }
+}
+
+
+static void
+mg_set_handler_type(struct mg_context *ctx,
+ const char *uri,
+ int handler_type,
+ int is_delete_request,
+ mg_request_handler handler,
+ mg_websocket_connect_handler connect_handler,
+ mg_websocket_ready_handler ready_handler,
+ mg_websocket_data_handler data_handler,
+ mg_websocket_close_handler close_handler,
+ mg_authorization_handler auth_handler,
+ void *cbdata)
+{
+ struct mg_handler_info *tmp_rh, **lastref;
+ size_t urilen = strlen(uri);
+
+ if (handler_type == WEBSOCKET_HANDLER) {
+ /* assert(handler == NULL); */
+ /* assert(is_delete_request || connect_handler!=NULL ||
+ * ready_handler!=NULL || data_handler!=NULL ||
+ * close_handler!=NULL);
+ */
+ /* assert(auth_handler == NULL); */
+ if (handler != NULL) {
+ return;
+ }
+ if (!is_delete_request && connect_handler == NULL
+ && ready_handler == NULL
+ && data_handler == NULL
+ && close_handler == NULL) {
+ return;
+ }
+ if (auth_handler != NULL) {
+ return;
+ }
+ } else if (handler_type == REQUEST_HANDLER) {
+ /* assert(connect_handler==NULL && ready_handler==NULL &&
+ * data_handler==NULL && close_handler==NULL); */
+ /* assert(is_delete_request || (handler!=NULL));
+ */
+ /* assert(auth_handler == NULL); */
+ if (connect_handler != NULL || ready_handler != NULL
+ || data_handler != NULL
+ || close_handler != NULL) {
+ return;
+ }
+ if (!is_delete_request && (handler == NULL)) {
+ return;
+ }
+ if (auth_handler != NULL) {
+ return;
+ }
+ } else { /* AUTH_HANDLER */
+ /* assert(handler == NULL); */
+ /* assert(connect_handler==NULL && ready_handler==NULL &&
+ * data_handler==NULL && close_handler==NULL); */
+ /* assert(auth_handler != NULL); */
+ if (handler != NULL) {
+ return;
+ }
+ if (connect_handler != NULL || ready_handler != NULL
+ || data_handler != NULL
+ || close_handler != NULL) {
+ return;
+ }
+ if (!is_delete_request && (auth_handler == NULL)) {
+ return;
+ }
+ }
+
+ if (!ctx) {
+ return;
+ }
+
+ mg_lock_context(ctx);
+
+ /* first try to find an existing handler */
+ lastref = &(ctx->handlers);
+ for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type == handler_type) {
+ if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
+ if (!is_delete_request) {
+ /* update existing handler */
+ if (handler_type == REQUEST_HANDLER) {
+ tmp_rh->handler = handler;
+ } else if (handler_type == WEBSOCKET_HANDLER) {
+ tmp_rh->connect_handler = connect_handler;
+ tmp_rh->ready_handler = ready_handler;
+ tmp_rh->data_handler = data_handler;
+ tmp_rh->close_handler = close_handler;
+ } else { /* AUTH_HANDLER */
+ tmp_rh->auth_handler = auth_handler;
+ }
+ tmp_rh->cbdata = cbdata;
+ } else {
+ /* remove existing handler */
+ *lastref = tmp_rh->next;
+ mg_free(tmp_rh->uri);
+ mg_free(tmp_rh);
+ }
+ mg_unlock_context(ctx);
+ return;
+ }
+ }
+ lastref = &(tmp_rh->next);
+ }
+
+ if (is_delete_request) {
+ /* no handler to set, this was a remove request to a non-existing
+ * handler */
+ mg_unlock_context(ctx);
+ return;
+ }
+
+ tmp_rh =
+ (struct mg_handler_info *)mg_calloc(sizeof(struct mg_handler_info), 1);
+ if (tmp_rh == NULL) {
+ mg_unlock_context(ctx);
+ mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM");
+ return;
+ }
+ tmp_rh->uri = mg_strdup(uri);
+ if (!tmp_rh->uri) {
+ mg_unlock_context(ctx);
+ mg_free(tmp_rh);
+ mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM");
+ return;
+ }
+ tmp_rh->uri_len = urilen;
+ if (handler_type == REQUEST_HANDLER) {
+ tmp_rh->handler = handler;
+ } else if (handler_type == WEBSOCKET_HANDLER) {
+ tmp_rh->connect_handler = connect_handler;
+ tmp_rh->ready_handler = ready_handler;
+ tmp_rh->data_handler = data_handler;
+ tmp_rh->close_handler = close_handler;
+ } else { /* AUTH_HANDLER */
+ tmp_rh->auth_handler = auth_handler;
+ }
+ tmp_rh->cbdata = cbdata;
+ tmp_rh->handler_type = handler_type;
+ tmp_rh->next = NULL;
+
+ *lastref = tmp_rh;
+ mg_unlock_context(ctx);
+}
+
+
+void
+mg_set_request_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_request_handler handler,
+ void *cbdata)
+{
+ mg_set_handler_type(ctx,
+ uri,
+ REQUEST_HANDLER,
+ handler == NULL,
+ handler,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cbdata);
+}
+
+
+void
+mg_set_websocket_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_websocket_connect_handler connect_handler,
+ mg_websocket_ready_handler ready_handler,
+ mg_websocket_data_handler data_handler,
+ mg_websocket_close_handler close_handler,
+ void *cbdata)
+{
+ int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
+ && (data_handler == NULL)
+ && (close_handler == NULL);
+ mg_set_handler_type(ctx,
+ uri,
+ WEBSOCKET_HANDLER,
+ is_delete_request,
+ NULL,
+ connect_handler,
+ ready_handler,
+ data_handler,
+ close_handler,
+ NULL,
+ cbdata);
+}
+
+
+void
+mg_set_auth_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_request_handler handler,
+ void *cbdata)
+{
+ mg_set_handler_type(ctx,
+ uri,
+ AUTH_HANDLER,
+ handler == NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ handler,
+ cbdata);
+}
+
+
+static int
+get_request_handler(struct mg_connection *conn,
+ int handler_type,
+ mg_request_handler *handler,
+ mg_websocket_connect_handler *connect_handler,
+ mg_websocket_ready_handler *ready_handler,
+ mg_websocket_data_handler *data_handler,
+ mg_websocket_close_handler *close_handler,
+ mg_authorization_handler *auth_handler,
+ void **cbdata)
+{
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ if (request_info) {
+ const char *uri = request_info->local_uri;
+ size_t urilen = strlen(uri);
+ struct mg_handler_info *tmp_rh;
+
+ if (!conn || !conn->ctx) {
+ return 0;
+ }
+
+ mg_lock_context(conn->ctx);
+
+ /* first try for an exact match */
+ for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+ tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type == handler_type) {
+ if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
+ if (handler_type == WEBSOCKET_HANDLER) {
+ *connect_handler = tmp_rh->connect_handler;
+ *ready_handler = tmp_rh->ready_handler;
+ *data_handler = tmp_rh->data_handler;
+ *close_handler = tmp_rh->close_handler;
+ } else if (handler_type == REQUEST_HANDLER) {
+ *handler = tmp_rh->handler;
+ } else { /* AUTH_HANDLER */
+ *auth_handler = tmp_rh->auth_handler;
+ }
+ *cbdata = tmp_rh->cbdata;
+ mg_unlock_context(conn->ctx);
+ return 1;
+ }
+ }
+ }
+
+ /* next try for a partial match, we will accept uri/something */
+ for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+ tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type == handler_type) {
+ if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/'
+ && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
+ if (handler_type == WEBSOCKET_HANDLER) {
+ *connect_handler = tmp_rh->connect_handler;
+ *ready_handler = tmp_rh->ready_handler;
+ *data_handler = tmp_rh->data_handler;
+ *close_handler = tmp_rh->close_handler;
+ } else if (handler_type == REQUEST_HANDLER) {
+ *handler = tmp_rh->handler;
+ } else { /* AUTH_HANDLER */
+ *auth_handler = tmp_rh->auth_handler;
+ }
+ *cbdata = tmp_rh->cbdata;
+ mg_unlock_context(conn->ctx);
+ return 1;
+ }
+ }
+ }
+
+ /* finally try for pattern match */
+ for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+ tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type == handler_type) {
+ if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
+ if (handler_type == WEBSOCKET_HANDLER) {
+ *connect_handler = tmp_rh->connect_handler;
+ *ready_handler = tmp_rh->ready_handler;
+ *data_handler = tmp_rh->data_handler;
+ *close_handler = tmp_rh->close_handler;
+ } else if (handler_type == REQUEST_HANDLER) {
+ *handler = tmp_rh->handler;
+ } else { /* AUTH_HANDLER */
+ *auth_handler = tmp_rh->auth_handler;
+ }
+ *cbdata = tmp_rh->cbdata;
+ mg_unlock_context(conn->ctx);
+ return 1;
+ }
+ }
+ }
+
+ mg_unlock_context(conn->ctx);
+ }
+ return 0; /* none found */
+}
+
+
+#if defined(USE_WEBSOCKET) && defined(MG_LEGACY_INTERFACE)
+static int
+deprecated_websocket_connect_wrapper(const struct mg_connection *conn,
+ void *cbdata)
+{
+ struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+ if (pcallbacks->websocket_connect) {
+ return pcallbacks->websocket_connect(conn);
+ }
+ /* No handler set - assume "OK" */
+ return 0;
+}
+
+
+static void
+deprecated_websocket_ready_wrapper(struct mg_connection *conn, void *cbdata)
+{
+ struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+ if (pcallbacks->websocket_ready) {
+ pcallbacks->websocket_ready(conn);
+ }
+}
+
+
+static int
+deprecated_websocket_data_wrapper(struct mg_connection *conn,
+ int bits,
+ char *data,
+ size_t len,
+ void *cbdata)
+{
+ struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+ if (pcallbacks->websocket_data) {
+ return pcallbacks->websocket_data(conn, bits, data, len);
+ }
+ /* No handler set - assume "OK" */
+ return 1;
+}
+#endif
+
+
+/* This is the heart of the Civetweb's logic.
+ * This function is called when the request is read, parsed and validated,
+ * and Civetweb must decide what action to take: serve a file, or
+ * a directory, or call embedded function, etcetera. */
+static void
+handle_request(struct mg_connection *conn)
+{
+ if (conn) {
+ struct mg_request_info *ri = &conn->request_info;
+ char path[PATH_MAX];
+ int uri_len, ssl_index;
+ int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
+ is_put_or_delete_request = 0, is_callback_resource = 0;
+ int i;
+ struct file file = STRUCT_FILE_INITIALIZER;
+ mg_request_handler callback_handler = NULL;
+ mg_websocket_connect_handler ws_connect_handler = NULL;
+ mg_websocket_ready_handler ws_ready_handler = NULL;
+ mg_websocket_data_handler ws_data_handler = NULL;
+ mg_websocket_close_handler ws_close_handler = NULL;
+ void *callback_data = NULL;
+ mg_authorization_handler auth_handler = NULL;
+ void *auth_callback_data = NULL;
+#if !defined(NO_FILES)
+ time_t curtime = time(NULL);
+ char date[64];
+#endif
+
+ path[0] = 0;
+
+ if (!ri) {
+ return;
+ }
+
+ /* 1. get the request url */
+ /* 1.1. split into url and query string */
+ if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
+ != NULL) {
+ *((char *)conn->request_info.query_string++) = '\0';
+ }
+ uri_len = (int)strlen(ri->local_uri);
+
+ /* 1.2. decode url (if config says so) */
+ if (should_decode_url(conn)) {
+ mg_url_decode(
+ ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0);
+ }
+
+ /* 1.3. clean URIs, so a path like allowed_dir/../forbidden_file is
+ * not possible */
+ remove_double_dots_and_double_slashes((char *)ri->local_uri);
+
+ /* step 1. completed, the url is known now */
+ DEBUG_TRACE("URL: %s", ri->local_uri);
+
+ /* 2. do a https redirect, if required */
+ if (!conn->client.is_ssl && conn->client.ssl_redir) {
+ ssl_index = get_first_ssl_listener_index(conn->ctx);
+ if (ssl_index >= 0) {
+ redirect_to_https_port(conn, ssl_index);
+ } else {
+ /* A http to https forward port has been specified,
+ * but no https port to forward to. */
+ send_http_error(conn,
+ 503,
+ "%s",
+ "Error: SSL forward not configured properly");
+ mg_cry(conn, "Can not redirect to SSL, no SSL port available");
+ }
+ return;
+ }
+
+ /* 3. if this ip has limited speed, set it for this connection */
+ conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
+ get_remote_ip(conn),
+ ri->local_uri);
+
+ /* 4. call a "handle everything" callback, if registered */
+ if (conn->ctx->callbacks.begin_request != NULL) {
+ /* Note that since V1.7 the "begin_request" function is called
+ * before an authorization check. If an authorization check is
+ * required, use a request_handler instead. */
+ i = conn->ctx->callbacks.begin_request(conn);
+ if (i > 0) {
+ /* callback already processed the request. Store the
+ return value as a status code for the access log. */
+ conn->status_code = i;
+ return;
+ } else if (i == 0) {
+ /* civetweb should process the request */
+ } else {
+ /* unspecified - may change with the next version */
+ return;
+ }
+ }
+
+ /* request not yet handled by a handler or redirect, so the request
+ * is processed here */
+
+ /* 5. interpret the url to find out how the request must be handled
+ */
+ /* 5.1. first test, if the request targets the regular http(s)://
+ * protocol namespace or the websocket ws(s):// protocol namespace.
+ */
+ is_websocket_request = is_websocket_protocol(conn);
+
+ /* 5.2. check if the request will be handled by a callback */
+ if (get_request_handler(conn,
+ is_websocket_request ? WEBSOCKET_HANDLER
+ : REQUEST_HANDLER,
+ &callback_handler,
+ &ws_connect_handler,
+ &ws_ready_handler,
+ &ws_data_handler,
+ &ws_close_handler,
+ NULL,
+ &callback_data)) {
+ /* 5.2.1. A callback will handle this request. All requests
+ * handled
+ * by a callback have to be considered as requests to a script
+ * resource. */
+ is_callback_resource = 1;
+ is_script_resource = 1;
+ is_put_or_delete_request = is_put_or_delete_method(conn);
+ } else {
+ no_callback_resource:
+ /* 5.2.2. No callback is responsible for this request. The URI
+ * addresses a file based resource (static content or Lua/cgi
+ * scripts in the file system). */
+ is_callback_resource = 0;
+ interpret_uri(conn,
+ path,
+ sizeof(path),
+ &file,
+ &is_found,
+ &is_script_resource,
+ &is_websocket_request,
+ &is_put_or_delete_request);
+ }
+
+ /* 6. authorization check */
+ /* 6.1. a custom authorization handler is installed */
+ if (get_request_handler(conn,
+ AUTH_HANDLER,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &auth_handler,
+ &auth_callback_data)) {
+ if (!auth_handler(conn, auth_callback_data)) {
+ return;
+ }
+ } else if (is_put_or_delete_request && !is_script_resource
+ && !is_callback_resource) {
+/* 6.2. this request is a PUT/DELETE to a real file */
+/* 6.2.1. thus, the server must have real files */
+#if defined(NO_FILES)
+ if (1) {
+#else
+ if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
+#endif
+ /* This server does not have any real files, thus the
+ * PUT/DELETE methods are not valid. */
+ send_http_error(conn,
+ 405,
+ "%s method not allowed",
+ conn->request_info.request_method);
+ return;
+ }
+
+#if !defined(NO_FILES)
+ /* 6.2.2. Check if put authorization for static files is
+ * available.
+ */
+ if (!is_authorized_for_put(conn)) {
+ send_authorization_request(conn);
+ return;
+ }
+#endif
+
+ } else {
+ /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
+ * or it is a PUT or DELETE request to a resource that does not
+ * correspond to a file. Check authorization. */
+ if (!check_authorization(conn, path)) {
+ send_authorization_request(conn);
+ return;
+ }
+ }
+
+ /* request is authorized or does not need authorization */
+
+ /* 7. check if there are request handlers for this uri */
+ if (is_callback_resource) {
+ if (!is_websocket_request) {
+ i = callback_handler(conn, callback_data);
+ if (i > 0) {
+ /* Do nothing, callback has served the request. Store
+ * the
+ * return value as status code for the log and discard
+ * all
+ * data from the client not used by the callback. */
+ conn->status_code = i;
+ discard_unread_request_data(conn);
+ } else {
+ /* TODO (high): what if the handler did NOT handle the
+ * request */
+ /* The last version did handle this as a file request,
+ * but
+ * since a file request is not always a script resource,
+ * the authorization check might be different */
+ interpret_uri(conn,
+ path,
+ sizeof(path),
+ &file,
+ &is_found,
+ &is_script_resource,
+ &is_websocket_request,
+ &is_put_or_delete_request);
+ callback_handler = NULL;
+
+ /* TODO (very low): goto is deprecated but for the
+ * moment,
+ * a goto is simpler than some curious loop. */
+ /* The situation "callback does not handle the request"
+ * needs to be reconsidered anyway. */
+ goto no_callback_resource;
+ }
+ } else {
+#if defined(USE_WEBSOCKET)
+ handle_websocket_request(conn,
+ path,
+ is_callback_resource,
+ ws_connect_handler,
+ ws_ready_handler,
+ ws_data_handler,
+ ws_close_handler,
+ callback_data);
+#endif
+ }
+ return;
+ }
+
+/* 8. handle websocket requests */
+#if defined(USE_WEBSOCKET)
+ if (is_websocket_request) {
+ if (is_script_resource) {
+ /* Websocket Lua script */
+ handle_websocket_request(conn,
+ path,
+ 0 /* Lua Script */,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &conn->ctx->callbacks);
+ } else {
+#if defined(MG_LEGACY_INTERFACE)
+ handle_websocket_request(
+ conn,
+ path,
+ !is_script_resource /* could be deprecated global callback */,
+ deprecated_websocket_connect_wrapper,
+ deprecated_websocket_ready_wrapper,
+ deprecated_websocket_data_wrapper,
+ NULL,
+ &conn->ctx->callbacks);
+#else
+ send_http_error(conn, 404, "%s", "Not found");
+#endif
+ }
+ return;
+ } else
+#endif
+
+#if defined(NO_FILES)
+ /* 9a. In case the server uses only callbacks, this uri is
+ * unknown.
+ * Then, all request handling ends here. */
+ send_http_error(conn, 404, "%s", "Not Found");
+
+#else
+ /* 9b. This request is either for a static file or resource handled
+ * by a script file. Thus, a DOCUMENT_ROOT must exist. */
+ if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
+ send_http_error(conn, 404, "%s", "Not Found");
+ return;
+ }
+
+ /* 10. File is handled by a script. */
+ if (is_script_resource) {
+ handle_file_based_request(conn, path, &file);
+ return;
+ }
+
+ /* 11. Handle put/delete/mkcol requests */
+ if (is_put_or_delete_request) {
+ /* 11.1. PUT method */
+ if (!strcmp(ri->request_method, "PUT")) {
+ put_file(conn, path);
+ return;
+ }
+ /* 11.2. DELETE method */
+ if (!strcmp(ri->request_method, "DELETE")) {
+ delete_file(conn, path);
+ return;
+ }
+ /* 11.3. MKCOL method */
+ if (!strcmp(ri->request_method, "MKCOL")) {
+ mkcol(conn, path);
+ return;
+ }
+ /* 11.4. PATCH method
+ * This method is not supported for static resources,
+ * only for scripts (Lua, CGI) and callbacks. */
+ send_http_error(conn,
+ 405,
+ "%s method not allowed",
+ conn->request_info.request_method);
+ return;
+ }
+
+ /* 11. File does not exist, or it was configured that it should be
+ * hidden */
+ if (!is_found || (must_hide_file(conn, path))) {
+ send_http_error(conn, 404, "%s", "Not found");
+ return;
+ }
+
+ /* 12. Directory uris should end with a slash */
+ if (file.is_directory && ri->local_uri[uri_len - 1] != '/') {
+ gmt_time_string(date, sizeof(date), &curtime);
+ mg_printf(conn,
+ "HTTP/1.1 301 Moved Permanently\r\n"
+ "Location: %s/\r\n"
+ "Date: %s\r\n"
+ /* "Cache-Control: private\r\n" (= default) */
+ "Content-Length: 0\r\n"
+ "Connection: %s\r\n\r\n",
+ ri->request_uri,
+ date,
+ suggest_connection_header(conn));
+ return;
+ }
+
+ /* 13. Handle other methods than GET/HEAD */
+ /* 13.1. Handle PROPFIND */
+ if (!strcmp(ri->request_method, "PROPFIND")) {
+ handle_propfind(conn, path, &file);
+ return;
+ }
+ /* 13.2. Handle OPTIONS for files */
+ if (!strcmp(ri->request_method, "OPTIONS")) {
+ /* This standard handler is only used for real files.
+ * Scripts should support the OPTIONS method themselves, to allow a
+ * maximum flexibility.
+ * Lua and CGI scripts may fully support CORS this way (including
+ * preflights). */
+ send_options(conn);
+ return;
+ }
+ /* 13.3. everything but GET and HEAD (e.g. POST) */
+ if (0 != strcmp(ri->request_method, "GET")
+ && 0 != strcmp(ri->request_method, "HEAD")) {
+ send_http_error(conn,
+ 405,
+ "%s method not allowed",
+ conn->request_info.request_method);
+ return;
+ }
+
+ /* 14. directories */
+ if (file.is_directory) {
+ if (substitute_index_file(conn, path, sizeof(path), &file)) {
+ /* 14.1. use a substitute file */
+ /* TODO (high): substitute index may be a script resource.
+ * define what should be possible in this case. */
+ } else {
+ /* 14.2. no substitute file */
+ if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING],
+ "yes")) {
+ handle_directory_request(conn, path);
+ } else {
+ send_http_error(conn,
+ 403,
+ "%s",
+ "Error: Directory listing denied");
+ }
+ return;
+ }
+ }
+
+ handle_file_based_request(conn, path, &file);
+#endif /* !defined(NO_FILES) */
+
+#if 0
+ /* Perform redirect and auth checks before calling begin_request()
+ * handler.
+ * Otherwise, begin_request() would need to perform auth checks and
+ * redirects. */
+#endif
+ }
+ return;
+}
+
+
+static void
+handle_file_based_request(struct mg_connection *conn,
+ const char *path,
+ struct file *file)
+{
+ if (!conn || !conn->ctx) {
+ return;
+ }
+
+ if (0) {
+#ifdef USE_LUA
+ } else if (match_prefix(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
+ strlen(
+ conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
+ path) > 0) {
+ /* Lua server page: an SSI like page containing mostly plain html
+ * code
+ * plus some tags with server generated contents. */
+ handle_lsp_request(conn, path, file, NULL);
+ } else if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+ strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+ path) > 0) {
+ /* Lua in-server module script: a CGI like script used to generate
+ * the
+ * entire reply. */
+ mg_exec_lua_script(conn, path, NULL);
+#endif
+#if defined(USE_DUKTAPE)
+ } else if (match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+ strlen(
+ conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+ path) > 0) {
+ /* Call duktape to generate the page */
+ mg_exec_duktape_script(conn, path);
+#endif
+#if !defined(NO_CGI)
+ } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+ strlen(conn->ctx->config[CGI_EXTENSIONS]),
+ path) > 0) {
+ /* CGI scripts may support all HTTP methods */
+ handle_cgi_request(conn, path);
+#endif /* !NO_CGI */
+ } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
+ strlen(conn->ctx->config[SSI_EXTENSIONS]),
+ path) > 0) {
+ handle_ssi_file_request(conn, path, file);
+#if !defined(NO_CACHING)
+ } else if ((!conn->in_error_handler) && is_not_modified(conn, file)) {
+ /* Send 304 "Not Modified" - this must not send any body data */
+ send_http_error(conn, 304, "%s", "");
+#endif /* !NO_CACHING */
+ } else {
+ handle_static_file_request(conn, path, file, NULL);
+ }
+}
+
+
+static void
+close_all_listening_sockets(struct mg_context *ctx)
+{
+ unsigned int i;
+ if (!ctx) {
+ return;
+ }
+
+ for (i = 0; i < ctx->num_listening_sockets; i++) {
+ closesocket(ctx->listening_sockets[i].sock);
+ ctx->listening_sockets[i].sock = INVALID_SOCKET;
+ }
+ mg_free(ctx->listening_sockets);
+ ctx->listening_sockets = NULL;
+ mg_free(ctx->listening_ports);
+ ctx->listening_ports = NULL;
+}
+
+
+/* Valid listening port specification is: [ip_address:]port[s]
+ * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
+ * Examples for IPv6: [::]:80, [::1]:80,
+ * [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:443s
+ * see https://tools.ietf.org/html/rfc3513#section-2.2 */
+static int
+parse_port_string(const struct vec *vec, struct socket *so)
+{
+ unsigned int a, b, c, d, port;
+ int ch, len;
+#if defined(USE_IPV6)
+ char buf[100] = {0};
+#endif
+
+ /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
+ * Also, all-zeroes in the socket address means binding to all addresses
+ * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
+ memset(so, 0, sizeof(*so));
+ so->lsa.sin.sin_family = AF_INET;
+
+ if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len)
+ == 5) {
+ /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
+ so->lsa.sin.sin_addr.s_addr =
+ htonl((a << 24) | (b << 16) | (c << 8) | d);
+ so->lsa.sin.sin_port = htons((uint16_t)port);
+#if defined(USE_IPV6)
+ } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
+ && mg_inet_pton(
+ AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) {
+ /* IPv6 address, examples: see above */
+ /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
+ */
+ so->lsa.sin6.sin6_port = htons((uint16_t)port);
+#endif
+ } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) {
+ /* If only port is specified, bind to IPv4, INADDR_ANY */
+ so->lsa.sin.sin_port = htons((uint16_t)port);
+ } else {
+ /* Parsing failure. Make port invalid. */
+ port = 0;
+ len = 0;
+ }
+
+ /* sscanf and the option splitting code ensure the following condition
+ */
+ if ((len < 0) && ((unsigned)len > (unsigned)vec->len)) {
+ return 0;
+ }
+ ch = vec->ptr[len]; /* Next character after the port number */
+ so->is_ssl = (ch == 's');
+ so->ssl_redir = (ch == 'r');
+
+ /* Make sure the port is valid and vector ends with 's', 'r' or ',' */
+ return is_valid_port(port)
+ && (ch == '\0' || ch == 's' || ch == 'r' || ch == ',');
+}
+
+
+static int
+set_ports_option(struct mg_context *ctx)
+{
+ const char *list;
+ int on = 1;
+#if defined(USE_IPV6)
+ int off = 0;
+#endif
+ struct vec vec;
+ struct socket so, *ptr;
+
+ in_port_t *portPtr;
+ union usa usa;
+ socklen_t len;
+
+ int portsTotal = 0;
+ int portsOk = 0;
+
+ if (!ctx) {
+ return 0;
+ }
+
+ memset(&so, 0, sizeof(so));
+ memset(&usa, 0, sizeof(usa));
+ len = sizeof(usa);
+ list = ctx->config[LISTENING_PORTS];
+ while ((list = next_option(list, &vec, NULL)) != NULL) {
+
+ portsTotal++;
+
+ if (!parse_port_string(&vec, &so)) {
+ mg_cry(fc(ctx),
+ "%.*s: invalid port spec (entry %i). Expecting list of: %s",
+ (int)vec.len,
+ vec.ptr,
+ portsTotal,
+ "[IP_ADDRESS:]PORT[s|r]");
+ continue;
+ }
+
+ if (so.is_ssl && ctx->ssl_ctx == NULL) {
+
+ mg_cry(fc(ctx),
+ "Cannot add SSL socket (entry %i). Is -ssl_certificate "
+ "option set?",
+ portsTotal);
+ continue;
+ }
+
+ if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))
+ == INVALID_SOCKET) {
+
+ mg_cry(fc(ctx), "cannot create socket (entry %i)", portsTotal);
+ continue;
+ }
+
+#ifdef _WIN32
+ /* Windows SO_REUSEADDR lets many procs binds to a
+ * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
+ * if someone already has the socket -- DTL */
+ /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
+ * Windows might need a few seconds before
+ * the same port can be used again in the
+ * same process, so a short Sleep may be
+ * required between mg_stop and mg_start.
+ */
+ if (setsockopt(so.sock,
+ SOL_SOCKET,
+ SO_EXCLUSIVEADDRUSE,
+ (SOCK_OPT_TYPE)&on,
+ sizeof(on)) != 0) {
+
+ mg_cry(fc(ctx),
+ "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
+ portsTotal);
+ }
+#else
+ if (setsockopt(so.sock,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (SOCK_OPT_TYPE)&on,
+ sizeof(on)) != 0) {
+
+ mg_cry(fc(ctx),
+ "cannot set socket option SO_REUSEADDR (entry %i)",
+ portsTotal);
+ }
+#endif
+
+#if defined(USE_IPV6)
+ if (so.lsa.sa.sa_family == AF_INET6
+ && setsockopt(so.sock,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ (void *)&off,
+ sizeof(off)) != 0) {
+
+ mg_cry(fc(ctx),
+ "cannot set socket option IPV6_V6ONLY (entry %i)",
+ portsTotal);
+ }
+#endif
+
+ if (so.lsa.sa.sa_family == AF_INET) {
+
+ len = sizeof(so.lsa.sin);
+ if (bind(so.sock, &so.lsa.sa, len) != 0) {
+ mg_cry(fc(ctx),
+ "cannot bind to %.*s: %d (%s)",
+ (int)vec.len,
+ vec.ptr,
+ (int)ERRNO,
+ strerror(errno));
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ continue;
+ }
+ }
+#if defined(USE_IPV6)
+ else if (so.lsa.sa.sa_family == AF_INET6) {
+
+ len = sizeof(so.lsa.sin6);
+ if (bind(so.sock, &so.lsa.sa, len) != 0) {
+ mg_cry(fc(ctx),
+ "cannot bind to IPv6 %.*s: %d (%s)",
+ (int)vec.len,
+ vec.ptr,
+ (int)ERRNO,
+ strerror(errno));
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ continue;
+ }
+ }
+#endif
+ else {
+ mg_cry(fc(ctx),
+ "cannot bind: address family not supported (entry %i)",
+ portsTotal);
+ continue;
+ }
+
+ if (listen(so.sock, SOMAXCONN) != 0) {
+
+ mg_cry(fc(ctx),
+ "cannot listen to %.*s: %d (%s)",
+ (int)vec.len,
+ vec.ptr,
+ (int)ERRNO,
+ strerror(errno));
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ continue;
+ }
+
+ if (getsockname(so.sock, &(usa.sa), &len) != 0) {
+
+ int err = (int)ERRNO;
+ mg_cry(fc(ctx),
+ "call to getsockname failed %.*s: %d (%s)",
+ (int)vec.len,
+ vec.ptr,
+ err,
+ strerror(errno));
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ continue;
+ }
+
+ if ((ptr = (struct socket *)
+ mg_realloc(ctx->listening_sockets,
+ (ctx->num_listening_sockets + 1)
+ * sizeof(ctx->listening_sockets[0]))) == NULL) {
+
+ mg_cry(fc(ctx), "%s", "Out of memory");
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ continue;
+ }
+
+ if ((portPtr =
+ (in_port_t *)mg_realloc(ctx->listening_ports,
+ (ctx->num_listening_sockets + 1)
+ * sizeof(ctx->listening_ports[0])))
+ == NULL) {
+
+ mg_cry(fc(ctx), "%s", "Out of memory");
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ mg_free(ptr);
+ continue;
+ }
+
+ set_close_on_exec(so.sock, fc(ctx));
+ ctx->listening_sockets = ptr;
+ ctx->listening_sockets[ctx->num_listening_sockets] = so;
+ ctx->listening_ports = portPtr;
+ ctx->listening_ports[ctx->num_listening_sockets] =
+ ntohs(usa.sin.sin_port);
+ ctx->num_listening_sockets++;
+ portsOk++;
+ }
+
+ if (portsOk != portsTotal) {
+ close_all_listening_sockets(ctx);
+ portsOk = 0;
+ }
+
+ return portsOk;
+}
+
+
+static const char *
+header_val(const struct mg_connection *conn, const char *header)
+{
+ const char *header_value;
+
+ if ((header_value = mg_get_header(conn, header)) == NULL) {
+ return "-";
+ } else {
+ return header_value;
+ }
+}
+
+
+static void
+log_access(const struct mg_connection *conn)
+{
+ const struct mg_request_info *ri;
+ struct file fi;
+ char date[64], src_addr[IP_ADDR_STR_LEN];
+ struct tm *tm;
+
+ const char *referer;
+ const char *user_agent;
+
+ char buf[4096];
+
+ if (!conn || !conn->ctx) {
+ return;
+ }
+
+ if (conn->ctx->config[ACCESS_LOG_FILE] != NULL) {
+ if (mg_fopen(conn, conn->ctx->config[ACCESS_LOG_FILE], "a+", &fi)
+ == 0) {
+ fi.fp = NULL;
+ }
+ } else {
+ fi.fp = NULL;
+ }
+
+ if (fi.fp == NULL && conn->ctx->callbacks.log_message == NULL) {
+ return;
+ }
+
+ tm = localtime(&conn->conn_birth_time);
+ if (tm != NULL) {
+ strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
+ } else {
+ mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
+ date[sizeof(date) - 1] = '\0';
+ }
+
+ ri = &conn->request_info;
+
+ sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+ referer = header_val(conn, "Referer");
+ user_agent = header_val(conn, "User-Agent");
+
+ mg_snprintf(conn,
+ NULL, /* Ignore truncation in access log */
+ buf,
+ sizeof(buf),
+ "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT " %s %s",
+ src_addr,
+ ri->remote_user == NULL ? "-" : ri->remote_user,
+ date,
+ ri->request_method ? ri->request_method : "-",
+ ri->request_uri ? ri->request_uri : "-",
+ ri->query_string ? "?" : "",
+ ri->query_string ? ri->query_string : "",
+ ri->http_version,
+ conn->status_code,
+ conn->num_bytes_sent,
+ referer,
+ user_agent);
+
+ if (conn->ctx->callbacks.log_access) {
+ conn->ctx->callbacks.log_access(conn, buf);
+ }
+
+ if (fi.fp) {
+ flockfile(fi.fp);
+ fprintf(fi.fp, "%s\n", buf);
+ fflush(fi.fp);
+ funlockfile(fi.fp);
+ mg_fclose(&fi);
+ }
+}
+
+
+/* Verify given socket address against the ACL.
+ * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
+ */
+static int
+check_acl(struct mg_context *ctx, uint32_t remote_ip)
+{
+ int allowed, flag;
+ uint32_t net, mask;
+ struct vec vec;
+
+ if (ctx) {
+ const char *list = ctx->config[ACCESS_CONTROL_LIST];
+
+ /* If any ACL is set, deny by default */
+ allowed = list == NULL ? '+' : '-';
+
+ while ((list = next_option(list, &vec, NULL)) != NULL) {
+ flag = vec.ptr[0];
+ if ((flag != '+' && flag != '-')
+ || parse_net(&vec.ptr[1], &net, &mask) == 0) {
+ mg_cry(fc(ctx),
+ "%s: subnet must be [+|-]x.x.x.x[/x]",
+ __func__);
+ return -1;
+ }
+
+ if (net == (remote_ip & mask)) {
+ allowed = flag;
+ }
+ }
+
+ return allowed == '+';
+ }
+ return -1;
+}
+
+
+#if !defined(_WIN32)
+static int
+set_uid_option(struct mg_context *ctx)
+{
+ struct passwd *pw;
+ if (ctx) {
+ const char *uid = ctx->config[RUN_AS_USER];
+ int success = 0;
+
+ if (uid == NULL) {
+ success = 1;
+ } else {
+ if ((pw = getpwnam(uid)) == NULL) {
+ mg_cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
+ } else if (setgid(pw->pw_gid) == -1) {
+ mg_cry(fc(ctx),
+ "%s: setgid(%s): %s",
+ __func__,
+ uid,
+ strerror(errno));
+ } else if (setgroups(0, NULL)) {
+ mg_cry(fc(ctx),
+ "%s: setgroups(): %s",
+ __func__,
+ strerror(errno));
+ } else if (setuid(pw->pw_uid) == -1) {
+ mg_cry(fc(ctx),
+ "%s: setuid(%s): %s",
+ __func__,
+ uid,
+ strerror(errno));
+ } else {
+ success = 1;
+ }
+ }
+
+ return success;
+ }
+ return 0;
+}
+#endif /* !_WIN32 */
+
+
+static void
+tls_dtor(void *key)
+{
+ struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
+ /* key == pthread_getspecific(sTlsKey); */
+
+ if (tls) {
+ if (tls->is_master == 2) {
+ tls->is_master = -3; /* Mark memory as dead */
+ mg_free(tls);
+ }
+ }
+ pthread_setspecific(sTlsKey, NULL);
+}
+
+
+#if !defined(NO_SSL)
+
+/* Must be set if sizeof(pthread_t) > sizeof(unsigned long) */
+static unsigned long
+ssl_id_callback(void)
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#else
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
+ * or not, so one of the two conditions will be unreachable by construction.
+ * Unfortunately the C standard does not define a way to check this at
+ * compile time, since the #if preprocessor conditions can not use the sizeof
+ * operator as an argument. */
+#endif
+
+ if (sizeof(pthread_t) > sizeof(unsigned long)) {
+ /* This is the problematic case for CRYPTO_set_id_callback:
+ * The OS pthread_t can not be cast to unsigned long. */
+ struct mg_workerTLS *tls =
+ (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+ if (tls == NULL) {
+ /* SSL called from an unknown thread: Create some thread index.
+ */
+ tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
+ tls->is_master = -2; /* -2 means "3rd party thread" */
+ tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+ pthread_setspecific(sTlsKey, tls);
+ }
+ return tls->thread_idx;
+ } else {
+ /* pthread_t may be any data type, so a simple cast to unsigned long
+ * can rise a warning/error, depending on the platform.
+ * Here memcpy is used as an anything-to-anything cast. */
+ unsigned long ret = 0;
+ pthread_t t = pthread_self();
+ memcpy(&ret, &t, sizeof(pthread_t));
+ return ret;
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+}
+
+
+static int ssl_use_pem_file(struct mg_context *ctx, const char *pem);
+static const char *ssl_error(void);
+
+
+static int
+refresh_trust(struct mg_connection *conn)
+{
+ static int reload_lock = 0;
+ static long int data_check = 0;
+
+ struct stat cert_buf;
+ long int t;
+ char *pem;
+ int should_verify_peer;
+
+ if ((pem = conn->ctx->config[SSL_CERTIFICATE]) == NULL
+ && conn->ctx->callbacks.init_ssl == NULL) {
+ return 0;
+ }
+
+ t = data_check;
+ if (stat(pem, &cert_buf) != -1) {
+ t = (long int)cert_buf.st_mtime;
+ }
+
+ if (data_check != t) {
+ data_check = t;
+
+ should_verify_peer =
+ (conn->ctx->config[SSL_DO_VERIFY_PEER] != NULL)
+ && (mg_strcasecmp(conn->ctx->config[SSL_DO_VERIFY_PEER], "yes")
+ == 0);
+
+ if (should_verify_peer) {
+ char *ca_path = conn->ctx->config[SSL_CA_PATH];
+ char *ca_file = conn->ctx->config[SSL_CA_FILE];
+ if (SSL_CTX_load_verify_locations(conn->ctx->ssl_ctx,
+ ca_file,
+ ca_path) != 1) {
+ mg_cry(fc(conn->ctx),
+ "SSL_CTX_load_verify_locations error: %s "
+ "ssl_verify_peer requires setting "
+ "either ssl_ca_path or ssl_ca_file. Is any of them "
+ "present in "
+ "the .conf file?",
+ ssl_error());
+ return 0;
+ }
+ }
+
+ if (!reload_lock) {
+ reload_lock = 1;
+ if (ssl_use_pem_file(conn->ctx, pem) == 0) {
+ return 0;
+ }
+ reload_lock = 0;
+ }
+ }
+ /* lock while cert is reloading */
+ while (reload_lock) {
+ sleep(1);
+ }
+
+ return 1;
+}
+
+
+static pthread_mutex_t *ssl_mutexes;
+
+
+static int
+sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *))
+{
+ int ret, err;
+ int short_trust;
+
+ if (!conn) {
+ return 0;
+ }
+
+ short_trust =
+ (conn->ctx->config[SSL_SHORT_TRUST] != NULL)
+ && (mg_strcasecmp(conn->ctx->config[SSL_SHORT_TRUST], "yes") == 0);
+
+ if (short_trust) {
+ int trust_ret = refresh_trust(conn);
+ if (!trust_ret) {
+ return trust_ret;
+ }
+ }
+
+ conn->ssl = SSL_new(s);
+ if (conn->ssl == NULL) {
+ return 0;
+ }
+
+ ret = SSL_set_fd(conn->ssl, conn->client.sock);
+ if (ret != 1) {
+ err = SSL_get_error(conn->ssl, ret);
+ (void)err; /* TODO: set some error message */
+ SSL_free(conn->ssl);
+ conn->ssl = NULL;
+ /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+ /* see
+ * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+ ERR_remove_state(0);
+ return 0;
+ }
+
+ ret = func(conn->ssl);
+ if (ret != 1) {
+ err = SSL_get_error(conn->ssl, ret);
+ (void)err; /* TODO: set some error message */
+ SSL_free(conn->ssl);
+ conn->ssl = NULL;
+ /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+ /* see
+ * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+ ERR_remove_state(0);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* Return OpenSSL error message (from CRYPTO lib) */
+static const char *
+ssl_error(void)
+{
+ unsigned long err;
+ err = ERR_get_error();
+ return err == 0 ? "" : ERR_error_string(err, NULL);
+}
+
+
+static void
+ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
+{
+ (void)line;
+ (void)file;
+
+ if (mode & 1) {
+ /* 1 is CRYPTO_LOCK */
+ (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
+ } else {
+ (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
+ }
+}
+
+
+#if !defined(NO_SSL_DL)
+static void *
+load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw)
+{
+ union {
+ void *p;
+ void (*fp)(void);
+ } u;
+ void *dll_handle;
+ struct ssl_func *fp;
+
+ if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
+ mg_cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
+ return NULL;
+ }
+
+ for (fp = sw; fp->name != NULL; fp++) {
+#ifdef _WIN32
+ /* GetProcAddress() returns pointer to function */
+ u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
+#else
+ /* dlsym() on UNIX returns void *. ISO C forbids casts of data
+ * pointers to function pointers. We need to use a union to make a
+ * cast. */
+ u.p = dlsym(dll_handle, fp->name);
+#endif /* _WIN32 */
+ if (u.fp == NULL) {
+ mg_cry(fc(ctx),
+ "%s: %s: cannot find %s",
+ __func__,
+ dll_name,
+ fp->name);
+ dlclose(dll_handle);
+ return NULL;
+ } else {
+ fp->ptr = u.fp;
+ }
+ }
+
+ return dll_handle;
+}
+
+
+static void *ssllib_dll_handle; /* Store the ssl library handle. */
+static void *cryptolib_dll_handle; /* Store the crypto library handle. */
+
+#endif /* NO_SSL_DL */
+
+
+#if defined(SSL_ALREADY_INITIALIZED)
+static int cryptolib_users = 1; /* Reference counter for crypto library. */
+#else
+static int cryptolib_users = 0; /* Reference counter for crypto library. */
+#endif
+
+
+static int
+initialize_ssl(struct mg_context *ctx)
+{
+ int i;
+ size_t size;
+
+#if !defined(NO_SSL_DL)
+ if (!cryptolib_dll_handle) {
+ cryptolib_dll_handle = load_dll(ctx, CRYPTO_LIB, crypto_sw);
+ if (!cryptolib_dll_handle) {
+ return 0;
+ }
+ }
+#endif /* NO_SSL_DL */
+
+ if (mg_atomic_inc(&cryptolib_users) > 1) {
+ return 1;
+ }
+
+ /* Initialize locking callbacks, needed for thread safety.
+ * http://www.openssl.org/support/faq.html#PROG1
+ */
+ i = CRYPTO_num_locks();
+ if (i < 0) {
+ i = 0;
+ }
+ size = sizeof(pthread_mutex_t) * ((size_t)(i));
+ if ((ssl_mutexes = (pthread_mutex_t *)mg_malloc(size)) == NULL) {
+ mg_cry(fc(ctx),
+ "%s: cannot allocate mutexes: %s",
+ __func__,
+ ssl_error());
+ return 0;
+ }
+
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr);
+ }
+
+ CRYPTO_set_locking_callback(&ssl_locking_callback);
+ CRYPTO_set_id_callback(&ssl_id_callback);
+
+ return 1;
+}
+
+
+static int
+ssl_use_pem_file(struct mg_context *ctx, const char *pem)
+{
+ if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0) {
+ mg_cry(fc(ctx),
+ "%s: cannot open certificate file %s: %s",
+ __func__,
+ pem,
+ ssl_error());
+ return 0;
+ }
+
+ /* could use SSL_CTX_set_default_passwd_cb_userdata */
+ if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
+ mg_cry(fc(ctx),
+ "%s: cannot open private key file %s: %s",
+ __func__,
+ pem,
+ ssl_error());
+ return 0;
+ }
+
+ if (SSL_CTX_check_private_key(ctx->ssl_ctx) == 0) {
+ mg_cry(fc(ctx),
+ "%s: certificate and private key do not match: %s",
+ __func__,
+ pem);
+ return 0;
+ }
+
+ if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem) == 0) {
+ mg_cry(fc(ctx),
+ "%s: cannot use certificate chain file %s: %s",
+ __func__,
+ pem,
+ ssl_error());
+ return 0;
+ }
+ return 1;
+}
+
+
+static long
+ssl_get_protocol(int version_id)
+{
+ long ret = SSL_OP_ALL;
+ if (version_id > 0)
+ ret |= SSL_OP_NO_SSLv2;
+ if (version_id > 1)
+ ret |= SSL_OP_NO_SSLv3;
+ if (version_id > 2)
+ ret |= SSL_OP_NO_TLSv1;
+ if (version_id > 3)
+ ret |= SSL_OP_NO_TLSv1_1;
+ return ret;
+}
+
+
+/* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */
+static int
+set_ssl_option(struct mg_context *ctx)
+{
+ const char *pem;
+ int callback_ret;
+ int should_verify_peer;
+ const char *ca_path;
+ const char *ca_file;
+ int use_default_verify_paths;
+ int verify_depth;
+ time_t now_rt = time(NULL);
+ struct timespec now_mt;
+ md5_byte_t ssl_context_id[16];
+ md5_state_t md5state;
+ int protocol_ver;
+
+ /* If PEM file is not specified and the init_ssl callback
+ * is not specified, skip SSL initialization. */
+ if (!ctx) {
+ return 0;
+ }
+ if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL
+ && ctx->callbacks.init_ssl == NULL) {
+ return 1;
+ }
+
+ if (!initialize_ssl(ctx)) {
+ return 0;
+ }
+
+#if !defined(NO_SSL_DL)
+ if (!ssllib_dll_handle) {
+ ssllib_dll_handle = load_dll(ctx, SSL_LIB, ssl_sw);
+ if (!ssllib_dll_handle) {
+ return 0;
+ }
+ }
+#endif /* NO_SSL_DL */
+
+ /* Initialize SSL library */
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+ mg_cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
+ return 0;
+ }
+
+ SSL_CTX_clear_options(ctx->ssl_ctx,
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
+ | SSL_OP_NO_TLSv1_1);
+ protocol_ver = atoi(ctx->config[SSL_PROTOCOL_VERSION]);
+ SSL_CTX_set_options(ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
+ SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
+
+ /* If a callback has been specified, call it. */
+ callback_ret =
+ (ctx->callbacks.init_ssl == NULL)
+ ? 0
+ : (ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data));
+
+ /* If callback returns 0, civetweb sets up the SSL certificate.
+ * If it returns 1, civetweb assumes the calback already did this.
+ * If it returns -1, initializing ssl fails. */
+ if (callback_ret < 0) {
+ mg_cry(fc(ctx), "SSL callback returned error: %i", callback_ret);
+ return 0;
+ }
+ if (callback_ret > 0) {
+ if (pem != NULL) {
+ (void)SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
+ }
+ return 1;
+ }
+
+ /* Use some UID as session context ID. */
+ md5_init(&md5state);
+ md5_append(&md5state, (const md5_byte_t *)&now_rt, sizeof(now_rt));
+ clock_gettime(CLOCK_MONOTONIC, &now_mt);
+ md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
+ md5_append(&md5state,
+ (const md5_byte_t *)ctx->config[LISTENING_PORTS],
+ strlen(ctx->config[LISTENING_PORTS]));
+ md5_append(&md5state, (const md5_byte_t *)ctx, sizeof(*ctx));
+ md5_finish(&md5state, ssl_context_id);
+
+ SSL_CTX_set_session_id_context(ctx->ssl_ctx,
+ (const unsigned char *)&ssl_context_id,
+ sizeof(ssl_context_id));
+
+ if (pem != NULL) {
+ if (!ssl_use_pem_file(ctx, pem)) {
+ return 0;
+ }
+ }
+
+ should_verify_peer =
+ (ctx->config[SSL_DO_VERIFY_PEER] != NULL)
+ && (mg_strcasecmp(ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0);
+
+ use_default_verify_paths =
+ (ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
+ && (mg_strcasecmp(ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes") == 0);
+
+ if (should_verify_peer) {
+ ca_path = ctx->config[SSL_CA_PATH];
+ ca_file = ctx->config[SSL_CA_FILE];
+ if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_path)
+ != 1) {
+ mg_cry(fc(ctx),
+ "SSL_CTX_load_verify_locations error: %s "
+ "ssl_verify_peer requires setting "
+ "either ssl_ca_path or ssl_ca_file. Is any of them "
+ "present in "
+ "the .conf file?",
+ ssl_error());
+ return 0;
+ }
+
+ SSL_CTX_set_verify(ctx->ssl_ctx,
+ SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ NULL);
+
+ if (use_default_verify_paths
+ && SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1) {
+ mg_cry(fc(ctx),
+ "SSL_CTX_set_default_verify_paths error: %s",
+ ssl_error());
+ return 0;
+ }
+
+ if (ctx->config[SSL_VERIFY_DEPTH]) {
+ verify_depth = atoi(ctx->config[SSL_VERIFY_DEPTH]);
+ SSL_CTX_set_verify_depth(ctx->ssl_ctx, verify_depth);
+ }
+ }
+
+ if (ctx->config[SSL_CIPHER_LIST] != NULL) {
+ if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config[SSL_CIPHER_LIST])
+ != 1) {
+ mg_cry(fc(ctx), "SSL_CTX_set_cipher_list error: %s", ssl_error());
+ }
+ }
+
+ return 1;
+}
+
+
+static void
+uninitialize_ssl(struct mg_context *ctx)
+{
+ int i;
+ (void)ctx;
+
+ if (mg_atomic_dec(&cryptolib_users) == 0) {
+
+ /* Shutdown according to
+ * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
+ * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
+ */
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_id_callback(NULL);
+ ENGINE_cleanup();
+ CONF_modules_unload(1);
+ ERR_free_strings();
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_state(0);
+
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ pthread_mutex_destroy(&ssl_mutexes[i]);
+ }
+ mg_free(ssl_mutexes);
+ ssl_mutexes = NULL;
+ }
+}
+#endif /* !NO_SSL */
+
+
+static int
+set_gpass_option(struct mg_context *ctx)
+{
+ if (ctx) {
+ struct file file = STRUCT_FILE_INITIALIZER;
+ const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
+ if (path != NULL && !mg_stat(fc(ctx), path, &file)) {
+ mg_cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+set_acl_option(struct mg_context *ctx)
+{
+ return check_acl(ctx, (uint32_t)0x7f000001UL) != -1;
+}
+
+
+static void
+reset_per_request_attributes(struct mg_connection *conn)
+{
+ if (!conn) {
+ return;
+ }
+ conn->path_info = NULL;
+ conn->num_bytes_sent = conn->consumed_content = 0;
+ conn->status_code = -1;
+ conn->is_chunked = 0;
+ conn->must_close = conn->request_len = conn->throttle = 0;
+ conn->request_info.content_length = -1;
+ conn->request_info.remote_user = NULL;
+ conn->request_info.request_method = NULL;
+ conn->request_info.request_uri = NULL;
+ conn->request_info.local_uri = NULL;
+ conn->request_info.uri = NULL; /* TODO: cleanup uri,
+ * local_uri and request_uri */
+ conn->request_info.http_version = NULL;
+ conn->request_info.num_headers = 0;
+ conn->data_len = 0;
+ conn->chunk_remainder = 0;
+ conn->internal_error = 0;
+}
+
+
+static int
+set_sock_timeout(SOCKET sock, int milliseconds)
+{
+ int r0 = 0, r1, r2;
+
+#ifdef _WIN32
+ /* Windows specific */
+
+ DWORD tv = (DWORD)milliseconds;
+
+#else
+ /* Linux, ... (not Windows) */
+
+ struct timeval tv;
+
+/* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
+ * max. time waiting for the acknowledged of TCP data before the connection
+ * will be forcefully closed and ETIMEDOUT is returned to the application.
+ * If this option is not set, the default timeout of 20-30 minutes is used.
+*/
+/* #define TCP_USER_TIMEOUT (18) */
+
+#if defined(TCP_USER_TIMEOUT)
+ unsigned int uto = (unsigned int)milliseconds;
+ r0 = setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
+#endif
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = (milliseconds * 1000) % 1000000;
+
+#endif /* _WIN32 */
+
+ r1 = setsockopt(
+ sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
+ r2 = setsockopt(
+ sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
+
+ return r0 || r1 || r2;
+}
+
+
+static int
+set_tcp_nodelay(SOCKET sock, int nodelay_on)
+{
+ if (setsockopt(sock,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (SOCK_OPT_TYPE)&nodelay_on,
+ sizeof(nodelay_on)) != 0) {
+ /* Error */
+ return 1;
+ }
+ /* OK */
+ return 0;
+}
+
+
+static void
+close_socket_gracefully(struct mg_connection *conn)
+{
+#if defined(_WIN32)
+ char buf[MG_BUF_LEN];
+ int n;
+#endif
+ struct linger linger;
+
+ if (!conn) {
+ return;
+ }
+
+ /* Set linger option to avoid socket hanging out after close. This
+ * prevent
+ * ephemeral port exhaust problem under high QPS. */
+ linger.l_onoff = 1;
+ linger.l_linger = 1;
+
+ if (setsockopt(conn->client.sock,
+ SOL_SOCKET,
+ SO_LINGER,
+ (char *)&linger,
+ sizeof(linger)) != 0) {
+ mg_cry(conn,
+ "%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+
+ /* Send FIN to the client */
+ shutdown(conn->client.sock, SHUT_WR);
+ set_non_blocking_mode(conn->client.sock);
+
+#if defined(_WIN32)
+ /* Read and discard pending incoming data. If we do not do that and
+ * close
+ * the socket, the data in the send buffer may be discarded. This
+ * behaviour is seen on Windows, when client keeps sending data
+ * when server decides to close the connection; then when client
+ * does recv() it gets no data back. */
+ do {
+ n = pull(
+ NULL, conn, buf, sizeof(buf), 1E-10 /* TODO: allow 0 as timeout */);
+ } while (n > 0);
+#endif
+
+ /* Now we know that our FIN is ACK-ed, safe to close */
+ closesocket(conn->client.sock);
+ conn->client.sock = INVALID_SOCKET;
+}
+
+
+static void
+close_connection(struct mg_connection *conn)
+{
+ if (!conn || !conn->ctx) {
+ return;
+ }
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ if (conn->lua_websocket_state) {
+ lua_websocket_close(conn, conn->lua_websocket_state);
+ conn->lua_websocket_state = NULL;
+ }
+#endif
+
+ /* call the connection_close callback if assigned */
+ if ((conn->ctx->callbacks.connection_close != NULL)
+ && (conn->ctx->context_type == 1)) {
+ conn->ctx->callbacks.connection_close(conn);
+ }
+
+ mg_lock_connection(conn);
+
+ conn->must_close = 1;
+
+#ifndef NO_SSL
+ if (conn->ssl != NULL) {
+ /* Run SSL_shutdown twice to ensure completly close SSL connection
+ */
+ SSL_shutdown(conn->ssl);
+ SSL_free(conn->ssl);
+ /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+ /* see
+ * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+ ERR_remove_state(0);
+ conn->ssl = NULL;
+ }
+#endif
+ if (conn->client.sock != INVALID_SOCKET) {
+ close_socket_gracefully(conn);
+ conn->client.sock = INVALID_SOCKET;
+ }
+
+ mg_unlock_connection(conn);
+}
+
+void
+mg_close_connection(struct mg_connection *conn)
+{
+ struct mg_context *client_ctx = NULL;
+ unsigned int i;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ if (conn->ctx->context_type == 2) {
+ client_ctx = conn->ctx;
+ /* client context: loops must end */
+ conn->ctx->stop_flag = 1;
+ }
+
+#ifndef NO_SSL
+ if (conn->client_ssl_ctx != NULL) {
+ SSL_CTX_free((SSL_CTX *)conn->client_ssl_ctx);
+ }
+#endif
+ close_connection(conn);
+ if (client_ctx != NULL) {
+ /* join worker thread and free context */
+ for (i = 0; i < client_ctx->cfg_worker_threads; i++) {
+ if (client_ctx->workerthreadids[i] != 0) {
+ mg_join_thread(client_ctx->workerthreadids[i]);
+ }
+ }
+ mg_free(client_ctx->workerthreadids);
+ mg_free(client_ctx);
+ (void)pthread_mutex_destroy(&conn->mutex);
+ mg_free(conn);
+ }
+}
+
+
+static struct mg_connection *
+mg_connect_client_impl(const struct mg_client_options *client_options,
+ int use_ssl,
+ char *ebuf,
+ size_t ebuf_len)
+{
+ static struct mg_context fake_ctx;
+ struct mg_connection *conn = NULL;
+ SOCKET sock;
+ union usa sa;
+
+ if (!connect_socket(&fake_ctx,
+ client_options->host,
+ client_options->port,
+ use_ssl,
+ ebuf,
+ ebuf_len,
+ &sock,
+ &sa)) {
+ ;
+ } else if ((conn = (struct mg_connection *)
+ mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "calloc(): %s",
+ strerror(ERRNO));
+ closesocket(sock);
+#ifndef NO_SSL
+ } else if (use_ssl
+ && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
+ == NULL) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "SSL_CTX_new error");
+ closesocket(sock);
+ mg_free(conn);
+ conn = NULL;
+#endif /* NO_SSL */
+
+ } else {
+
+#ifdef USE_IPV6
+ socklen_t len = (sa.sa.sa_family == AF_INET)
+ ? sizeof(conn->client.rsa.sin)
+ : sizeof(conn->client.rsa.sin6);
+ struct sockaddr *psa =
+ (sa.sa.sa_family == AF_INET)
+ ? (struct sockaddr *)&(conn->client.rsa.sin)
+ : (struct sockaddr *)&(conn->client.rsa.sin6);
+#else
+ socklen_t len = sizeof(conn->client.rsa.sin);
+ struct sockaddr *psa = (struct sockaddr *)&(conn->client.rsa.sin);
+#endif
+
+ conn->buf_size = MAX_REQUEST_SIZE;
+ conn->buf = (char *)(conn + 1);
+ conn->ctx = &fake_ctx;
+ conn->client.sock = sock;
+ conn->client.lsa = sa;
+
+ if (getsockname(sock, psa, &len) != 0) {
+ mg_cry(conn,
+ "%s: getsockname() failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+
+ conn->client.is_ssl = use_ssl ? 1 : 0;
+ (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
+
+#ifndef NO_SSL
+ if (use_ssl) {
+ fake_ctx.ssl_ctx = conn->client_ssl_ctx;
+
+ /* TODO: Check ssl_verify_peer and ssl_ca_path here.
+ * SSL_CTX_set_verify call is needed to switch off server
+ * certificate checking, which is off by default in OpenSSL and
+ * on in yaSSL. */
+ /* TODO: SSL_CTX_set_verify(conn->client_ssl_ctx,
+ * SSL_VERIFY_PEER, verify_ssl_server); */
+
+ if (client_options->client_cert) {
+ if (!ssl_use_pem_file(&fake_ctx, client_options->client_cert)) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "Can not use SSL client certificate");
+ SSL_CTX_free(conn->client_ssl_ctx);
+ closesocket(sock);
+ mg_free(conn);
+ conn = NULL;
+ }
+ }
+
+ if (client_options->server_cert) {
+ SSL_CTX_load_verify_locations(conn->client_ssl_ctx,
+ client_options->server_cert,
+ NULL);
+ SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, NULL);
+ } else {
+ SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ if (!sslize(conn, conn->client_ssl_ctx, SSL_connect)) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "SSL connection error");
+ SSL_CTX_free(conn->client_ssl_ctx);
+ closesocket(sock);
+ mg_free(conn);
+ conn = NULL;
+ }
+ }
+#endif
+ }
+
+ return conn;
+}
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+ char *error_buffer,
+ size_t error_buffer_size)
+{
+ return mg_connect_client_impl(client_options,
+ 1,
+ error_buffer,
+ error_buffer_size);
+}
+
+
+struct mg_connection *
+mg_connect_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size)
+{
+ struct mg_client_options opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.host = host;
+ opts.port = port;
+ return mg_connect_client_impl(&opts,
+ use_ssl,
+ error_buffer,
+ error_buffer_size);
+}
+
+
+static const struct {
+ const char *proto;
+ size_t proto_len;
+ unsigned default_port;
+} abs_uri_protocols[] = {{"http://", 7, 80},
+ {"https://", 8, 443},
+ {"ws://", 5, 80},
+ {"wss://", 6, 443},
+ {NULL, 0, 0}};
+
+
+/* Check if the uri is valid.
+ * return 0 for invalid uri,
+ * return 1 for *,
+ * return 2 for relative uri,
+ * return 3 for absolute uri without port,
+ * return 4 for absolute uri with port */
+static int
+get_uri_type(const char *uri)
+{
+ int i;
+ char *hostend, *portbegin, *portend;
+ unsigned long port;
+
+ /* According to the HTTP standard
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
+ * URI can be an asterisk (*) or should start with slash (relative uri),
+ * or it should start with the protocol (absolute uri). */
+ if (uri[0] == '*' && uri[1] == '\0') {
+ /* asterisk */
+ return 1;
+ }
+ if (uri[0] == '/') {
+ /* relative uri */
+ return 2;
+ }
+
+ /* It could be an absolute uri: */
+ /* This function only checks if the uri is valid, not if it is
+ * addressing the current server. So civetweb can also be used
+ * as a proxy server. */
+ for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
+ if (mg_strncasecmp(uri,
+ abs_uri_protocols[i].proto,
+ abs_uri_protocols[i].proto_len) == 0) {
+
+ hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
+ if (!hostend) {
+ return 0;
+ }
+ portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':');
+ if (!portbegin) {
+ return 3;
+ }
+
+ port = strtoul(portbegin + 1, &portend, 10);
+ if ((portend != hostend) || !port || !is_valid_port(port)) {
+ return 0;
+ }
+
+ return 4;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Return NULL or the relative uri at the current server */
+static const char *
+get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
+{
+ const char *server_domain;
+ size_t server_domain_len;
+ size_t request_domain_len = 0;
+ unsigned long port = 0;
+ int i;
+ const char *hostbegin = NULL;
+ const char *hostend = NULL;
+ const char *portbegin;
+ char *portend;
+
+ /* DNS is case insensitive, so use case insensitive string compare here
+ */
+ server_domain = conn->ctx->config[AUTHENTICATION_DOMAIN];
+ if (!server_domain) {
+ return 0;
+ }
+ server_domain_len = strlen(server_domain);
+ if (!server_domain_len) {
+ return 0;
+ }
+
+ for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
+ if (mg_strncasecmp(uri,
+ abs_uri_protocols[i].proto,
+ abs_uri_protocols[i].proto_len) == 0) {
+
+ hostbegin = uri + abs_uri_protocols[i].proto_len;
+ hostend = strchr(hostbegin, '/');
+ if (!hostend) {
+ return 0;
+ }
+ portbegin = strchr(hostbegin, ':');
+ if ((!portbegin) || (portbegin > hostend)) {
+ port = abs_uri_protocols[i].default_port;
+ request_domain_len = (size_t)(hostend - hostbegin);
+ } else {
+ port = strtoul(portbegin + 1, &portend, 10);
+ if ((portend != hostend) || !port || !is_valid_port(port)) {
+ return 0;
+ }
+ request_domain_len = (size_t)(portbegin - hostbegin);
+ }
+ /* protocol found, port set */
+ break;
+ }
+ }
+
+ if (!port) {
+ /* port remains 0 if the protocol is not found */
+ return 0;
+ }
+
+#if defined(USE_IPV6)
+ if (conn->client.lsa.sa.sa_family == AF_INET6) {
+ if (ntohs(conn->client.lsa.sin6.sin6_port) != port) {
+ /* Request is directed to a different port */
+ return 0;
+ }
+ } else
+#endif
+ {
+ if (ntohs(conn->client.lsa.sin.sin_port) != port) {
+ /* Request is directed to a different port */
+ return 0;
+ }
+ }
+
+ if ((request_domain_len != server_domain_len)
+ || (0 != memcmp(server_domain, hostbegin, server_domain_len))) {
+ /* Request is directed to another server */
+ return 0;
+ }
+
+ return hostend;
+}
+
+
+static int
+getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
+{
+ const char *cl;
+
+ if (ebuf_len > 0) {
+ ebuf[0] = '\0';
+ }
+ *err = 0;
+
+ reset_per_request_attributes(conn);
+
+ if (!conn) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Internal error");
+ *err = 500;
+ return 0;
+ }
+ /* Set the time the request was received. This value should be used for
+ * timeouts. */
+ clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
+
+ conn->request_len =
+ read_request(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
+ /* assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
+ */
+ if (conn->request_len >= 0 && conn->data_len < conn->request_len) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Invalid request size");
+ *err = 500;
+ return 0;
+ }
+
+ if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Request Too Large");
+ *err = 413;
+ return 0;
+ } else if (conn->request_len <= 0) {
+ if (conn->data_len > 0) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Client sent malformed request");
+ *err = 400;
+ } else {
+ /* Server did not send anything -> just close the connection */
+ conn->must_close = 1;
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Client did not send a request");
+ *err = 0;
+ }
+ return 0;
+ } else if (parse_http_message(conn->buf,
+ conn->buf_size,
+ &conn->request_info) <= 0) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Bad Request");
+ *err = 400;
+ return 0;
+ } else {
+ /* Message is a valid request or response */
+ if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
+ /* Request/response has content length set */
+ char *endptr = NULL;
+ conn->content_len = strtoll(cl, &endptr, 10);
+ if (endptr == cl) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Bad Request");
+ *err = 411;
+ return 0;
+ }
+ /* Publish the content length back to the request info. */
+ conn->request_info.content_length = conn->content_len;
+ } else if ((cl = get_header(&conn->request_info, "Transfer-Encoding"))
+ != NULL
+ && !mg_strcasecmp(cl, "chunked")) {
+ conn->is_chunked = 1;
+ } else if (!mg_strcasecmp(conn->request_info.request_method, "POST")
+ || !mg_strcasecmp(conn->request_info.request_method,
+ "PUT")) {
+ /* POST or PUT request without content length set */
+ conn->content_len = -1;
+ } else if (!mg_strncasecmp(conn->request_info.request_method,
+ "HTTP/",
+ 5)) {
+ /* Response without content length set */
+ conn->content_len = -1;
+ } else {
+ /* Other request */
+ conn->content_len = 0;
+ }
+ }
+ return 1;
+}
+
+
+int
+mg_get_response(struct mg_connection *conn,
+ char *ebuf,
+ size_t ebuf_len,
+ int timeout)
+{
+ if (conn) {
+ /* Implementation of API function for HTTP clients */
+ int err, ret;
+ struct mg_context *octx = conn->ctx;
+ struct mg_context rctx = *(conn->ctx);
+ char txt[32]; /* will not overflow */
+
+ if (timeout >= 0) {
+ mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
+ rctx.config[REQUEST_TIMEOUT] = txt;
+ set_sock_timeout(conn->client.sock, timeout);
+ } else {
+ rctx.config[REQUEST_TIMEOUT] = NULL;
+ }
+
+ conn->ctx = &rctx;
+ ret = getreq(conn, ebuf, ebuf_len, &err);
+ conn->ctx = octx;
+
+ /* TODO: 1) uri is deprecated;
+ * 2) here, ri.uri is the http response code */
+ conn->request_info.uri = conn->request_info.request_uri;
+
+ /* TODO (mid): Define proper return values - maybe return length?
+ * For the first test use <0 for error and >0 for OK */
+ return (ret == 0) ? -1 : +1;
+ }
+ return -1;
+}
+
+
+struct mg_connection *
+mg_download(const char *host,
+ int port,
+ int use_ssl,
+ char *ebuf,
+ size_t ebuf_len,
+ const char *fmt,
+ ...)
+{
+ struct mg_connection *conn;
+ va_list ap;
+ int i;
+ int reqerr;
+
+ va_start(ap, fmt);
+ ebuf[0] = '\0';
+
+ /* open a connection */
+ conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
+
+ if (conn != NULL) {
+ i = mg_vprintf(conn, fmt, ap);
+ if (i <= 0) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ ebuf_len,
+ "%s",
+ "Error sending request");
+ } else {
+ getreq(conn, ebuf, ebuf_len, &reqerr);
+
+ /* TODO: 1) uri is deprecated;
+ * 2) here, ri.uri is the http response code */
+ conn->request_info.uri = conn->request_info.request_uri;
+ }
+ }
+
+ /* if an error occured, close the connection */
+ if (ebuf[0] != '\0' && conn != NULL) {
+ mg_close_connection(conn);
+ conn = NULL;
+ }
+
+ va_end(ap);
+ return conn;
+}
+
+
+struct websocket_client_thread_data {
+ struct mg_connection *conn;
+ mg_websocket_data_handler data_handler;
+ mg_websocket_close_handler close_handler;
+ void *callback_data;
+};
+
+
+#if defined(USE_WEBSOCKET)
+#ifdef _WIN32
+static unsigned __stdcall websocket_client_thread(void *data)
+#else
+static void *
+websocket_client_thread(void *data)
+#endif
+{
+ struct websocket_client_thread_data *cdata =
+ (struct websocket_client_thread_data *)data;
+
+ mg_set_thread_name("ws-client");
+
+ if (cdata->conn->ctx) {
+ if (cdata->conn->ctx->callbacks.init_thread) {
+ /* 3 indicates a websocket client thread */
+ /* TODO: check if conn->ctx can be set */
+ cdata->conn->ctx->callbacks.init_thread(cdata->conn->ctx, 3);
+ }
+ }
+
+ read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
+
+ DEBUG_TRACE("%s", "Websocket client thread exited\n");
+
+ if (cdata->close_handler != NULL) {
+ cdata->close_handler(cdata->conn, cdata->callback_data);
+ }
+
+ mg_free((void *)cdata);
+
+#ifdef _WIN32
+ return 0;
+#else
+ return NULL;
+#endif
+}
+#endif
+
+
+struct mg_connection *
+mg_connect_websocket_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data)
+{
+ struct mg_connection *conn = NULL;
+
+#if defined(USE_WEBSOCKET)
+ struct mg_context *newctx = NULL;
+ struct websocket_client_thread_data *thread_data;
+ static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
+ static const char *handshake_req;
+
+ if (origin != NULL) {
+ handshake_req = "GET %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Origin: %s\r\n"
+ "\r\n";
+ } else {
+ handshake_req = "GET %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "\r\n";
+ }
+
+ /* Establish the client connection and request upgrade */
+ conn = mg_download(host,
+ port,
+ use_ssl,
+ error_buffer,
+ error_buffer_size,
+ handshake_req,
+ path,
+ host,
+ magic,
+ origin);
+
+ /* Connection object will be null if something goes wrong */
+ if (conn == NULL || (strcmp(conn->request_info.request_uri, "101") != 0)) {
+ if (!*error_buffer) {
+ /* if there is a connection, but it did not return 101,
+ * error_buffer is not yet set */
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ error_buffer,
+ error_buffer_size,
+ "Unexpected server reply");
+ }
+ DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
+ if (conn != NULL) {
+ mg_free(conn);
+ conn = NULL;
+ }
+ return conn;
+ }
+
+ /* For client connections, mg_context is fake. Since we need to set a
+ * callback function, we need to create a copy and modify it. */
+ newctx = (struct mg_context *)mg_malloc(sizeof(struct mg_context));
+ memcpy(newctx, conn->ctx, sizeof(struct mg_context));
+ newctx->user_data = user_data;
+ newctx->context_type = 2; /* client context type */
+ newctx->cfg_worker_threads = 1; /* one worker thread will be created */
+ newctx->workerthreadids =
+ (pthread_t *)mg_calloc(newctx->cfg_worker_threads, sizeof(pthread_t));
+ conn->ctx = newctx;
+ thread_data = (struct websocket_client_thread_data *)
+ mg_calloc(sizeof(struct websocket_client_thread_data), 1);
+ thread_data->conn = conn;
+ thread_data->data_handler = data_func;
+ thread_data->close_handler = close_func;
+ thread_data->callback_data = NULL;
+
+ /* Start a thread to read the websocket client connection
+ * This thread will automatically stop when mg_disconnect is
+ * called on the client connection */
+ if (mg_start_thread_with_id(websocket_client_thread,
+ (void *)thread_data,
+ newctx->workerthreadids) != 0) {
+ mg_free((void *)thread_data);
+ mg_free((void *)newctx->workerthreadids);
+ mg_free((void *)newctx);
+ mg_free((void *)conn);
+ conn = NULL;
+ DEBUG_TRACE("%s",
+ "Websocket client connect thread could not be started\r\n");
+ }
+#else
+ /* Appease "unused parameter" warnings */
+ (void)host;
+ (void)port;
+ (void)use_ssl;
+ (void)error_buffer;
+ (void)error_buffer_size;
+ (void)path;
+ (void)origin;
+ (void)user_data;
+ (void)data_func;
+ (void)close_func;
+#endif
+
+ return conn;
+}
+
+
+static void
+process_new_connection(struct mg_connection *conn)
+{
+ if (conn && conn->ctx) {
+ struct mg_request_info *ri = &conn->request_info;
+ int keep_alive_enabled, keep_alive, discard_len;
+ char ebuf[100];
+ const char *hostend;
+ int reqerr, uri_type;
+
+ keep_alive_enabled =
+ !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
+
+ /* Important: on new connection, reset the receiving buffer. Credit
+ * goes to crule42. */
+ conn->data_len = 0;
+ do {
+ if (!getreq(conn, ebuf, sizeof(ebuf), &reqerr)) {
+ /* The request sent by the client could not be understood by
+ * the server, or it was incomplete or a timeout. Send an
+ * error message and close the connection. */
+ if (reqerr > 0) {
+ /*assert(ebuf[0] != '\0');*/
+ send_http_error(conn, reqerr, "%s", ebuf);
+ }
+ } else if (strcmp(ri->http_version, "1.0")
+ && strcmp(ri->http_version, "1.1")) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ sizeof(ebuf),
+ "Bad HTTP version: [%s]",
+ ri->http_version);
+ send_http_error(conn, 505, "%s", ebuf);
+ }
+
+ if (ebuf[0] == '\0') {
+ uri_type = get_uri_type(conn->request_info.request_uri);
+ switch (uri_type) {
+ case 1:
+ /* Asterisk */
+ conn->request_info.local_uri = NULL;
+ break;
+ case 2:
+ /* relative uri */
+ conn->request_info.local_uri =
+ conn->request_info.request_uri;
+ break;
+ case 3:
+ case 4:
+ /* absolute uri (with/without port) */
+ hostend = get_rel_url_at_current_server(
+ conn->request_info.request_uri, conn);
+ if (hostend) {
+ conn->request_info.local_uri = hostend;
+ } else {
+ conn->request_info.local_uri = NULL;
+ }
+ break;
+ default:
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ ebuf,
+ sizeof(ebuf),
+ "Invalid URI: [%s]",
+ ri->request_uri);
+ send_http_error(conn, 400, "%s", ebuf);
+ break;
+ }
+
+ /* TODO: cleanup uri, local_uri and request_uri */
+ conn->request_info.uri = conn->request_info.local_uri;
+ }
+
+ if (ebuf[0] == '\0') {
+ if (conn->request_info.local_uri) {
+ /* handle request to local server */
+ handle_request(conn);
+ if (conn->ctx->callbacks.end_request != NULL) {
+ conn->ctx->callbacks.end_request(conn,
+ conn->status_code);
+ }
+ log_access(conn);
+ } else {
+ /* TODO: handle non-local request (PROXY) */
+ conn->must_close = 1;
+ }
+ } else {
+ conn->must_close = 1;
+ }
+
+ if (ri->remote_user != NULL) {
+ mg_free((void *)ri->remote_user);
+ /* Important! When having connections with and without auth
+ * would cause double free and then crash */
+ ri->remote_user = NULL;
+ }
+
+ /* NOTE(lsm): order is important here. should_keep_alive() call
+ * is
+ * using parsed request, which will be invalid after memmove's
+ * below.
+ * Therefore, memorize should_keep_alive() result now for later
+ * use
+ * in loop exit condition. */
+ keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled
+ && conn->content_len >= 0 && should_keep_alive(conn);
+
+ /* Discard all buffered data for this request */
+ discard_len = conn->content_len >= 0 && conn->request_len > 0
+ && conn->request_len + conn->content_len
+ < (int64_t)conn->data_len
+ ? (int)(conn->request_len + conn->content_len)
+ : conn->data_len;
+ /*assert(discard_len >= 0);*/
+ if (discard_len < 0)
+ break;
+ conn->data_len -= discard_len;
+ if (conn->data_len > 0) {
+ memmove(conn->buf,
+ conn->buf + discard_len,
+ (size_t)conn->data_len);
+ }
+
+ /* assert(conn->data_len >= 0); */
+ /* assert(conn->data_len <= conn->buf_size); */
+
+ if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
+ break;
+ }
+
+ } while (keep_alive);
+ }
+}
+
+
+/* Worker threads take accepted socket from the queue */
+static int
+consume_socket(struct mg_context *ctx, struct socket *sp)
+{
+#define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
+ if (!ctx) {
+ return 0;
+ }
+
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+ DEBUG_TRACE("%s", "going idle");
+
+ /* If the queue is empty, wait. We're idle at this point. */
+ while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
+ pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
+ }
+
+ /* If we're stopping, sq_head may be equal to sq_tail. */
+ if (ctx->sq_head > ctx->sq_tail) {
+ /* Copy socket from the queue and increment tail */
+ *sp = ctx->queue[ctx->sq_tail % QUEUE_SIZE(ctx)];
+ ctx->sq_tail++;
+
+ DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
+
+ /* Wrap pointers if needed */
+ while (ctx->sq_tail > QUEUE_SIZE(ctx)) {
+ ctx->sq_tail -= QUEUE_SIZE(ctx);
+ ctx->sq_head -= QUEUE_SIZE(ctx);
+ }
+ }
+
+ (void)pthread_cond_signal(&ctx->sq_empty);
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+ return !ctx->stop_flag;
+#undef QUEUE_SIZE
+}
+
+
+static void *
+worker_thread_run(void *thread_func_param)
+{
+ struct mg_context *ctx = (struct mg_context *)thread_func_param;
+ struct mg_connection *conn;
+ struct mg_workerTLS tls;
+#if defined(MG_LEGACY_INTERFACE)
+ uint32_t addr;
+#endif
+
+ mg_set_thread_name("worker");
+
+ tls.is_master = 0;
+ tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+ if (ctx->callbacks.init_thread) {
+ /* call init_thread for a worker thread (type 1) */
+ ctx->callbacks.init_thread(ctx, 1);
+ }
+
+ conn =
+ (struct mg_connection *)mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
+ if (conn == NULL) {
+ mg_cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
+ } else {
+ pthread_setspecific(sTlsKey, &tls);
+ conn->buf_size = MAX_REQUEST_SIZE;
+ conn->buf = (char *)(conn + 1);
+ conn->ctx = ctx;
+ conn->request_info.user_data = ctx->user_data;
+ /* Allocate a mutex for this connection to allow communication both
+ * within the request handler and from elsewhere in the application
+ */
+ (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
+
+ /* Call consume_socket() even when ctx->stop_flag > 0, to let it
+ * signal sq_empty condvar to wake up the master waiting in
+ * produce_socket() */
+ while (consume_socket(ctx, &conn->client)) {
+ conn->conn_birth_time = time(NULL);
+
+/* Fill in IP, port info early so even if SSL setup below fails,
+ * error handler would have the corresponding info.
+ * Thanks to Johannes Winkelmann for the patch.
+ */
+#if defined(USE_IPV6)
+ if (conn->client.rsa.sa.sa_family == AF_INET6) {
+ conn->request_info.remote_port =
+ ntohs(conn->client.rsa.sin6.sin6_port);
+ } else
+#endif
+ {
+ conn->request_info.remote_port =
+ ntohs(conn->client.rsa.sin.sin_port);
+ }
+
+ sockaddr_to_string(conn->request_info.remote_addr,
+ sizeof(conn->request_info.remote_addr),
+ &conn->client.rsa);
+
+#if defined(MG_LEGACY_INTERFACE)
+ /* This legacy interface only works for the IPv4 case */
+ addr = ntohl(conn->client.rsa.sin.sin_addr.s_addr);
+ memcpy(&conn->request_info.remote_ip, &addr, 4);
+#endif
+
+ conn->request_info.is_ssl = conn->client.is_ssl;
+
+ if (!conn->client.is_ssl
+#ifndef NO_SSL
+ || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
+#endif
+ ) {
+
+
+ process_new_connection(conn);
+ }
+
+ close_connection(conn);
+ }
+ }
+
+ /* Signal master that we're done with connection and exiting */
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+ ctx->running_worker_threads--;
+ (void)pthread_cond_signal(&ctx->thread_cond);
+ /* assert(ctx->running_worker_threads >= 0); */
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+ pthread_setspecific(sTlsKey, NULL);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ CloseHandle(tls.pthread_cond_helper_mutex);
+#endif
+ pthread_mutex_destroy(&conn->mutex);
+ mg_free(conn);
+
+ DEBUG_TRACE("%s", "exiting");
+ return NULL;
+}
+
+
+/* Threads have different return types on Windows and Unix. */
+#ifdef _WIN32
+static unsigned __stdcall worker_thread(void *thread_func_param)
+{
+ worker_thread_run(thread_func_param);
+ return 0;
+}
+#else
+static void *
+worker_thread(void *thread_func_param)
+{
+ worker_thread_run(thread_func_param);
+ return NULL;
+}
+#endif /* _WIN32 */
+
+
+/* Master thread adds accepted socket to a queue */
+static void
+produce_socket(struct mg_context *ctx, const struct socket *sp)
+{
+#define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
+ if (!ctx) {
+ return;
+ }
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+
+ /* If the queue is full, wait */
+ while (ctx->stop_flag == 0
+ && ctx->sq_head - ctx->sq_tail >= QUEUE_SIZE(ctx)) {
+ (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
+ }
+
+ if (ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx)) {
+ /* Copy socket to the queue and increment head */
+ ctx->queue[ctx->sq_head % QUEUE_SIZE(ctx)] = *sp;
+ ctx->sq_head++;
+ DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
+ }
+
+ (void)pthread_cond_signal(&ctx->sq_full);
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+#undef QUEUE_SIZE
+}
+
+
+static void
+accept_new_connection(const struct socket *listener, struct mg_context *ctx)
+{
+ struct socket so;
+ char src_addr[IP_ADDR_STR_LEN];
+ socklen_t len = sizeof(so.rsa);
+ int on = 1;
+ int timeout;
+
+ if (!listener) {
+ return;
+ }
+
+ if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
+ == INVALID_SOCKET) {
+ } else if (!check_acl(ctx, ntohl(*(uint32_t *)&so.rsa.sin.sin_addr))) {
+ sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
+ mg_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
+ } else {
+ /* Put so socket structure into the queue */
+ DEBUG_TRACE("Accepted socket %d", (int)so.sock);
+ set_close_on_exec(so.sock, fc(ctx));
+ so.is_ssl = listener->is_ssl;
+ so.ssl_redir = listener->ssl_redir;
+ if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
+ mg_cry(fc(ctx),
+ "%s: getsockname() failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+
+ /* Set TCP keep-alive. This is needed because if HTTP-level
+ * keep-alive
+ * is enabled, and client resets the connection, server won't get
+ * TCP FIN or RST and will keep the connection open forever. With
+ * TCP keep-alive, next keep-alive handshake will figure out that
+ * the client is down and will close the server end.
+ * Thanks to Igor Klopov who suggested the patch. */
+ if (setsockopt(so.sock,
+ SOL_SOCKET,
+ SO_KEEPALIVE,
+ (SOCK_OPT_TYPE)&on,
+ sizeof(on)) != 0) {
+ mg_cry(fc(ctx),
+ "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+
+
+ /* Disable TCP Nagle's algorithm. Normally TCP packets are
+ * coalesced
+ * to effectively fill up the underlying IP packet payload and
+ * reduce
+ * the overhead of sending lots of small buffers. However this hurts
+ * the server's throughput (ie. operations per second) when HTTP 1.1
+ * persistent connections are used and the responses are relatively
+ * small (eg. less than 1400 bytes).
+ */
+ if ((ctx != NULL) && (ctx->config[CONFIG_TCP_NODELAY] != NULL)
+ && (!strcmp(ctx->config[CONFIG_TCP_NODELAY], "1"))) {
+ if (set_tcp_nodelay(so.sock, 1) != 0) {
+ mg_cry(fc(ctx),
+ "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
+ __func__,
+ strerror(ERRNO));
+ }
+ }
+
+ if (ctx && ctx->config[REQUEST_TIMEOUT]) {
+ timeout = atoi(ctx->config[REQUEST_TIMEOUT]);
+ } else {
+ timeout = -1;
+ }
+
+ /* Set socket timeout to the given value, but not more than a
+ * a certain limit (SOCKET_TIMEOUT_QUANTUM, default 10 seconds),
+ * so the server can exit after that time if requested. */
+ if ((timeout > 0) && (timeout < SOCKET_TIMEOUT_QUANTUM)) {
+ set_sock_timeout(so.sock, timeout);
+ } else {
+ set_sock_timeout(so.sock, SOCKET_TIMEOUT_QUANTUM);
+ }
+
+ produce_socket(ctx, &so);
+ }
+}
+
+
+static void
+master_thread_run(void *thread_func_param)
+{
+ struct mg_context *ctx = (struct mg_context *)thread_func_param;
+ struct mg_workerTLS tls;
+ struct pollfd *pfd;
+ unsigned int i;
+ unsigned int workerthreadcount;
+
+ if (!ctx) {
+ return;
+ }
+
+ mg_set_thread_name("master");
+
+/* Increase priority of the master thread */
+#if defined(_WIN32)
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+#elif defined(USE_MASTER_THREAD_PRIORITY)
+ int min_prio = sched_get_priority_min(SCHED_RR);
+ int max_prio = sched_get_priority_max(SCHED_RR);
+ if ((min_prio >= 0) && (max_prio >= 0)
+ && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
+ && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
+ struct sched_param sched_param = {0};
+ sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
+ pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
+ }
+#endif
+
+/* Initialize thread local storage */
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+ tls.is_master = 1;
+ pthread_setspecific(sTlsKey, &tls);
+
+ if (ctx->callbacks.init_thread) {
+ /* Callback for the master thread (type 0) */
+ ctx->callbacks.init_thread(ctx, 0);
+ }
+
+ /* Server starts *now* */
+ ctx->start_time = time(NULL);
+
+ /* Allocate memory for the listening sockets, and start the server */
+ pfd =
+ (struct pollfd *)mg_calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
+ while (pfd != NULL && ctx->stop_flag == 0) {
+ for (i = 0; i < ctx->num_listening_sockets; i++) {
+ pfd[i].fd = ctx->listening_sockets[i].sock;
+ pfd[i].events = POLLIN;
+ }
+
+ if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
+ for (i = 0; i < ctx->num_listening_sockets; i++) {
+ /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
+ * successful poll, and POLLIN is defined as
+ * (POLLRDNORM | POLLRDBAND)
+ * Therefore, we're checking pfd[i].revents & POLLIN, not
+ * pfd[i].revents == POLLIN. */
+ if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
+ accept_new_connection(&ctx->listening_sockets[i], ctx);
+ }
+ }
+ }
+ }
+ mg_free(pfd);
+ DEBUG_TRACE("%s", "stopping workers");
+
+ /* Stop signal received: somebody called mg_stop. Quit. */
+ close_all_listening_sockets(ctx);
+
+ /* Wakeup workers that are waiting for connections to handle. */
+ pthread_cond_broadcast(&ctx->sq_full);
+
+ /* Wait until all threads finish */
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+ while (ctx->running_worker_threads > 0) {
+ (void)pthread_cond_wait(&ctx->thread_cond, &ctx->thread_mutex);
+ }
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+ /* Join all worker threads to avoid leaking threads. */
+ workerthreadcount = ctx->cfg_worker_threads;
+ for (i = 0; i < workerthreadcount; i++) {
+ if (ctx->workerthreadids[i] != 0) {
+ mg_join_thread(ctx->workerthreadids[i]);
+ }
+ }
+
+#if !defined(NO_SSL)
+ if (ctx->ssl_ctx != NULL) {
+ uninitialize_ssl(ctx);
+ }
+#endif
+ DEBUG_TRACE("%s", "exiting");
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ CloseHandle(tls.pthread_cond_helper_mutex);
+#endif
+ pthread_setspecific(sTlsKey, NULL);
+
+ /* Signal mg_stop() that we're done.
+ * WARNING: This must be the very last thing this
+ * thread does, as ctx becomes invalid after this line. */
+ ctx->stop_flag = 2;
+}
+
+
+/* Threads have different return types on Windows and Unix. */
+#ifdef _WIN32
+static unsigned __stdcall master_thread(void *thread_func_param)
+{
+ master_thread_run(thread_func_param);
+ return 0;
+}
+#else
+static void *
+master_thread(void *thread_func_param)
+{
+ master_thread_run(thread_func_param);
+ return NULL;
+}
+#endif /* _WIN32 */
+
+
+static void
+free_context(struct mg_context *ctx)
+{
+ int i;
+ struct mg_handler_info *tmp_rh;
+
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->callbacks.exit_context) {
+ ctx->callbacks.exit_context(ctx);
+ }
+
+ /* All threads exited, no sync is needed. Destroy thread mutex and
+ * condvars
+ */
+ (void)pthread_mutex_destroy(&ctx->thread_mutex);
+ (void)pthread_cond_destroy(&ctx->thread_cond);
+ (void)pthread_cond_destroy(&ctx->sq_empty);
+ (void)pthread_cond_destroy(&ctx->sq_full);
+
+ /* Destroy other context global data structures mutex */
+ (void)pthread_mutex_destroy(&ctx->nonce_mutex);
+
+#if defined(USE_TIMERS)
+ timers_exit(ctx);
+#endif
+
+ /* Deallocate config parameters */
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ if (ctx->config[i] != NULL) {
+#if defined(_MSC_VER)
+#pragma warning(suppress : 6001)
+#endif
+ mg_free(ctx->config[i]);
+ }
+ }
+
+ /* Deallocate request handlers */
+ while (ctx->handlers) {
+ tmp_rh = ctx->handlers;
+ ctx->handlers = tmp_rh->next;
+ mg_free(tmp_rh->uri);
+ mg_free(tmp_rh);
+ }
+
+#ifndef NO_SSL
+ /* Deallocate SSL context */
+ if (ctx->ssl_ctx != NULL) {
+ SSL_CTX_free(ctx->ssl_ctx);
+ }
+#endif /* !NO_SSL */
+
+ /* Deallocate worker thread ID array */
+ if (ctx->workerthreadids != NULL) {
+ mg_free(ctx->workerthreadids);
+ }
+
+ /* Deallocate the tls variable */
+ if (mg_atomic_dec(&sTlsInit) == 0) {
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ DeleteCriticalSection(&global_log_file_lock);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+#if !defined(_WIN32)
+ pthread_mutexattr_destroy(&pthread_mutex_attr);
+#endif
+
+ pthread_key_delete(sTlsKey);
+ }
+
+ /* deallocate system name string */
+ mg_free(ctx->systemName);
+
+ /* Deallocate context itself */
+ mg_free(ctx);
+}
+
+
+void
+mg_stop(struct mg_context *ctx)
+{
+ pthread_t mt;
+ if (!ctx) {
+ return;
+ }
+
+ /* We don't use a lock here. Calling mg_stop with the same ctx from
+ * two threads is not allowed. */
+ mt = ctx->masterthreadid;
+ if (mt == 0) {
+ return;
+ }
+
+ ctx->masterthreadid = 0;
+ ctx->stop_flag = 1;
+
+ /* Wait until mg_fini() stops */
+ while (ctx->stop_flag != 2) {
+ (void)mg_sleep(10);
+ }
+
+ mg_join_thread(mt);
+ free_context(ctx);
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ (void)WSACleanup();
+#endif /* _WIN32 && !__SYMBIAN32__ */
+}
+
+
+static void
+get_system_name(char **sysName)
+{
+#if defined(_WIN32)
+#if !defined(__SYMBIAN32__)
+ char name[128];
+ DWORD dwVersion = 0;
+ DWORD dwMajorVersion = 0;
+ DWORD dwMinorVersion = 0;
+ DWORD dwBuild = 0;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+// GetVersion was declared deprecated
+#pragma warning(disable : 4996)
+#endif
+ dwVersion = GetVersion();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
+ dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
+ dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
+ (void)dwBuild;
+
+ sprintf(name,
+ "Windows %u.%u",
+ (unsigned)dwMajorVersion,
+ (unsigned)dwMinorVersion);
+ *sysName = mg_strdup(name);
+#else
+ *sysName = mg_strdup("Symbian");
+#endif
+#else
+ struct utsname name;
+ memset(&name, 0, sizeof(name));
+ uname(&name);
+ *sysName = mg_strdup(name.sysname);
+#endif
+}
+
+
+struct mg_context *
+mg_start(const struct mg_callbacks *callbacks,
+ void *user_data,
+ const char **options)
+{
+ struct mg_context *ctx;
+ const char *name, *value, *default_value;
+ int idx, ok, workerthreadcount;
+ unsigned int i;
+ void (*exit_callback)(const struct mg_context *ctx) = 0;
+
+ struct mg_workerTLS tls;
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 2), &data);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+
+ /* Allocate context and initialize reasonable general case defaults. */
+ if ((ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx))) == NULL) {
+ return NULL;
+ }
+
+ /* Random number generator will initialize at the first call */
+ ctx->auth_nonce_mask =
+ (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
+
+ if (mg_atomic_inc(&sTlsInit) == 1) {
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ InitializeCriticalSection(&global_log_file_lock);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+#if !defined(_WIN32)
+ pthread_mutexattr_init(&pthread_mutex_attr);
+ pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+#endif
+
+ if (0 != pthread_key_create(&sTlsKey, tls_dtor)) {
+ /* Fatal error - abort start. However, this situation should
+ * never
+ * occur in practice. */
+ mg_atomic_dec(&sTlsInit);
+ mg_cry(fc(ctx), "Cannot initialize thread local storage");
+ mg_free(ctx);
+ return NULL;
+ }
+ } else {
+ /* TODO (low): istead of sleeping, check if sTlsKey is already
+ * initialized. */
+ mg_sleep(1);
+ }
+
+ tls.is_master = -1;
+ tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ tls.pthread_cond_helper_mutex = NULL;
+#endif
+ pthread_setspecific(sTlsKey, &tls);
+
+#if defined(USE_LUA)
+ lua_init_optional_libraries();
+#endif
+
+ ok = 0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr);
+ ok &= 0 == pthread_cond_init(&ctx->thread_cond, NULL);
+ ok &= 0 == pthread_cond_init(&ctx->sq_empty, NULL);
+ ok &= 0 == pthread_cond_init(&ctx->sq_full, NULL);
+ ok &= 0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr);
+ if (!ok) {
+ /* Fatal error - abort start. However, this situation should never
+ * occur in practice. */
+ mg_cry(fc(ctx), "Cannot initialize thread synchronization objects");
+ mg_free(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+
+ if (callbacks) {
+ ctx->callbacks = *callbacks;
+ exit_callback = callbacks->exit_context;
+ ctx->callbacks.exit_context = 0;
+ }
+ ctx->user_data = user_data;
+ ctx->handlers = NULL;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+ ctx->shared_lua_websockets = 0;
+#endif
+
+ while (options && (name = *options++) != NULL) {
+ if ((idx = get_option_index(name)) == -1) {
+ mg_cry(fc(ctx), "Invalid option: %s", name);
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ } else if ((value = *options++) == NULL) {
+ mg_cry(fc(ctx), "%s: option value cannot be NULL", name);
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+ if (ctx->config[idx] != NULL) {
+ mg_cry(fc(ctx), "warning: %s: duplicate option", name);
+ mg_free(ctx->config[idx]);
+ }
+ ctx->config[idx] = mg_strdup(value);
+ DEBUG_TRACE("[%s] -> [%s]", name, value);
+ }
+
+ /* Set default value if needed */
+ for (i = 0; config_options[i].name != NULL; i++) {
+ default_value = config_options[i].default_value;
+ if (ctx->config[i] == NULL && default_value != NULL) {
+ ctx->config[i] = mg_strdup(default_value);
+ }
+ }
+
+#if defined(NO_FILES)
+ if (ctx->config[DOCUMENT_ROOT] != NULL) {
+ mg_cry(fc(ctx), "%s", "Document root must not be set");
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+#endif
+
+ get_system_name(&ctx->systemName);
+
+ /* NOTE(lsm): order is important here. SSL certificates must
+ * be initialized before listening ports. UID must be set last. */
+ if (!set_gpass_option(ctx) ||
+#if !defined(NO_SSL)
+ !set_ssl_option(ctx) ||
+#endif
+ !set_ports_option(ctx) ||
+#if !defined(_WIN32)
+ !set_uid_option(ctx) ||
+#endif
+ !set_acl_option(ctx)) {
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+
+#if !defined(_WIN32) && !defined(__SYMBIAN32__)
+ /* Ignore SIGPIPE signal, so if browser cancels the request, it
+ * won't kill the whole process. */
+ (void)signal(SIGPIPE, SIG_IGN);
+#endif /* !_WIN32 && !__SYMBIAN32__ */
+
+ workerthreadcount = atoi(ctx->config[NUM_THREADS]);
+
+ if (workerthreadcount > MAX_WORKER_THREADS) {
+ mg_cry(fc(ctx), "Too many worker threads");
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+
+ if (workerthreadcount > 0) {
+ ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
+ ctx->workerthreadids =
+ (pthread_t *)mg_calloc(ctx->cfg_worker_threads, sizeof(pthread_t));
+ if (ctx->workerthreadids == NULL) {
+ mg_cry(fc(ctx), "Not enough memory for worker thread ID array");
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+ }
+
+#if defined(USE_TIMERS)
+ if (timers_init(ctx) != 0) {
+ mg_cry(fc(ctx), "Error creating timers");
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+#endif
+
+ /* Context has been created - init user libraries */
+ if (ctx->callbacks.init_context) {
+ ctx->callbacks.init_context(ctx);
+ }
+ ctx->callbacks.exit_context = exit_callback;
+ ctx->context_type = 1; /* server context */
+
+ /* Start master (listening) thread */
+ mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
+
+ /* Start worker threads */
+ for (i = 0; i < ctx->cfg_worker_threads; i++) {
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+ ctx->running_worker_threads++;
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+ if (mg_start_thread_with_id(worker_thread,
+ ctx,
+ &ctx->workerthreadids[i]) != 0) {
+ (void)pthread_mutex_lock(&ctx->thread_mutex);
+ ctx->running_worker_threads--;
+ (void)pthread_mutex_unlock(&ctx->thread_mutex);
+ if (i > 0) {
+ mg_cry(fc(ctx),
+ "Cannot start worker thread %i: error %ld",
+ i + 1,
+ (long)ERRNO);
+ } else {
+ mg_cry(fc(ctx),
+ "Cannot create threads: error %ld",
+ (long)ERRNO);
+ free_context(ctx);
+ pthread_setspecific(sTlsKey, NULL);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ pthread_setspecific(sTlsKey, NULL);
+ return ctx;
+}
+
+
+/* Feature check API function */
+unsigned
+mg_check_feature(unsigned feature)
+{
+ static const unsigned feature_set = 0
+/* Set bits for available features according to API documentation.
+ * This bit mask is created at compile time, according to the active
+ * preprocessor defines. It is a single const value at runtime. */
+#if !defined(NO_FILES)
+ | 0x0001u
+#endif
+#if !defined(NO_SSL)
+ | 0x0002u
+#endif
+#if !defined(NO_CGI)
+ | 0x0004u
+#endif
+#if defined(USE_IPV6)
+ | 0x0008u
+#endif
+#if defined(USE_WEBSOCKET)
+ | 0x0010u
+#endif
+#if defined(USE_LUA)
+ | 0x0020u
+#endif
+#if defined(USE_DUKTAPE)
+ | 0x0040u
+#endif
+#if !defined(NO_CACHING)
+ | 0x0080u
+#endif
+
+/* Set some extra bits not defined in the API documentation.
+ * These bits may change without further notice. */
+#if defined(MG_LEGACY_INTERFACE)
+ | 0x8000u
+#endif
+#if defined(MEMORY_DEBUGGING)
+ | 0x0100u
+#endif
+#if defined(USE_TIMERS)
+ | 0x0200u
+#endif
+#if !defined(NO_NONCE_CHECK)
+ | 0x0400u
+#endif
+#if !defined(NO_POPEN)
+ | 0x0800u
+#endif
+ ;
+ return (feature & feature_set);
+}
--- /dev/null
+/* Copyright (c) 2013-2016 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef CIVETWEB_HEADER_INCLUDED
+#define CIVETWEB_HEADER_INCLUDED
+
+#define CIVETWEB_VERSION "1.8"
+
+#ifndef CIVETWEB_API
+#if defined(_WIN32)
+#if defined(CIVETWEB_DLL_EXPORTS)
+#define CIVETWEB_API __declspec(dllexport)
+#elif defined(CIVETWEB_DLL_IMPORTS)
+#define CIVETWEB_API __declspec(dllimport)
+#else
+#define CIVETWEB_API
+#endif
+#elif __GNUC__ >= 4
+#define CIVETWEB_API __attribute__((visibility("default")))
+#else
+#define CIVETWEB_API
+#endif
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+struct mg_context; /* Handle for the HTTP service itself */
+struct mg_connection; /* Handle for the individual connection */
+
+
+/* This structure contains information about the HTTP request. */
+struct mg_request_info {
+ const char *request_method; /* "GET", "POST", etc */
+ const char *request_uri; /* URL-decoded URI (absolute or relative,
+ * as in the request) */
+ const char *local_uri; /* URL-decoded URI (relative). Can be NULL
+ * if the request_uri does not address a
+ * resource at the server host. */
+ const char *uri; /* Deprecated: use local_uri instead */
+ const char *http_version; /* E.g. "1.0", "1.1" */
+ const char *query_string; /* URL part after '?', not including '?', or
+ NULL */
+ const char *remote_user; /* Authenticated user, or NULL if no auth
+ used */
+ char remote_addr[48]; /* Client's IP address as a string. */
+
+#if defined(MG_LEGACY_INTERFACE)
+ long remote_ip; /* Client's IP address. Deprecated: use remote_addr instead
+ */
+#endif
+
+ long long content_length; /* Length (in bytes) of the request body,
+ can be -1 if no length was given. */
+ int remote_port; /* Client's port */
+ int is_ssl; /* 1 if SSL-ed, 0 if not */
+ void *user_data; /* User data pointer passed to mg_start() */
+ void *conn_data; /* Connection-specific user data */
+
+ int num_headers; /* Number of HTTP headers */
+ struct mg_header {
+ const char *name; /* HTTP header name */
+ const char *value; /* HTTP header value */
+ } http_headers[64]; /* Maximum 64 headers */
+};
+
+
+/* This structure needs to be passed to mg_start(), to let civetweb know
+ which callbacks to invoke. For a detailed description, see
+ https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */
+struct mg_callbacks {
+ /* Called when civetweb has received new HTTP request.
+ If the callback returns one, it must process the request
+ by sending valid HTTP headers and a body. Civetweb will not do
+ any further processing. Otherwise it must return zero.
+ Note that since V1.7 the "begin_request" function is called
+ before an authorization check. If an authorization check is
+ required, use a request_handler instead.
+ Return value:
+ 0: civetweb will process the request itself. In this case,
+ the callback must not send any data to the client.
+ 1-999: callback already processed the request. Civetweb will
+ not send any data after the callback returned. The
+ return code is stored as a HTTP status code for the
+ access log. */
+ int (*begin_request)(struct mg_connection *);
+
+ /* Called when civetweb has finished processing request. */
+ void (*end_request)(const struct mg_connection *, int reply_status_code);
+
+ /* Called when civetweb is about to log a message. If callback returns
+ non-zero, civetweb does not log anything. */
+ int (*log_message)(const struct mg_connection *, const char *message);
+
+ /* Called when civetweb is about to log access. If callback returns
+ non-zero, civetweb does not log anything. */
+ int (*log_access)(const struct mg_connection *, const char *message);
+
+ /* Called when civetweb initializes SSL library.
+ Parameters:
+ user_data: parameter user_data passed when starting the server.
+ Return value:
+ 0: civetweb will set up the SSL certificate.
+ 1: civetweb assumes the callback already set up the certificate.
+ -1: initializing ssl fails. */
+ int (*init_ssl)(void *ssl_context, void *user_data);
+
+#if defined(MG_LEGACY_INTERFACE)
+ /* Called when websocket request is received, before websocket handshake.
+ Return value:
+ 0: civetweb proceeds with websocket handshake.
+ 1: connection is closed immediately.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ int (*websocket_connect)(const struct mg_connection *);
+
+ /* Called when websocket handshake is successfully completed, and
+ connection is ready for data exchange.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ void (*websocket_ready)(struct mg_connection *);
+
+ /* Called when data frame has been received from the client.
+ Parameters:
+ bits: first byte of the websocket frame, see websocket RFC at
+ http://tools.ietf.org/html/rfc6455, section 5.2
+ data, data_len: payload, with mask (if any) already applied.
+ Return value:
+ 1: keep this websocket connection open.
+ 0: close this websocket connection.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ int (*websocket_data)(struct mg_connection *,
+ int bits,
+ char *data,
+ size_t data_len);
+#endif /* MG_LEGACY_INTERFACE */
+
+ /* Called when civetweb is closing a connection. The per-context mutex is
+ locked when this is invoked. This is primarily useful for noting when
+ a websocket is closing and removing it from any application-maintained
+ list of clients.
+ Using this callback for websocket connections is deprecated: Use
+ mg_set_websocket_handler instead. */
+ void (*connection_close)(const struct mg_connection *);
+
+ /* Called when civetweb tries to open a file. Used to intercept file open
+ calls, and serve file data from memory instead.
+ Parameters:
+ path: Full path to the file to open.
+ data_len: Placeholder for the file size, if file is served from
+ memory.
+ Return value:
+ NULL: do not serve file from memory, proceed with normal file open.
+ non-NULL: pointer to the file contents in memory. data_len must be
+ initilized with the size of the memory block. */
+ const char *(*open_file)(const struct mg_connection *,
+ const char *path,
+ size_t *data_len);
+
+ /* Called when civetweb is about to serve Lua server page, if
+ Lua support is enabled.
+ Parameters:
+ lua_context: "lua_State *" pointer. */
+ void (*init_lua)(const struct mg_connection *, void *lua_context);
+
+#if defined(MG_LEGACY_INTERFACE)
+ /* Called when civetweb has uploaded a file to a temporary directory as a
+ result of mg_upload() call.
+ Note that mg_upload is deprecated. Use mg_handle_form_request instead.
+ Parameters:
+ file_name: full path name to the uploaded file. */
+ void (*upload)(struct mg_connection *, const char *file_name);
+#endif
+
+ /* Called when civetweb is about to send HTTP error to the client.
+ Implementing this callback allows to create custom error pages.
+ Parameters:
+ status: HTTP error status code.
+ Return value:
+ 1: run civetweb error handler.
+ 0: callback already handled the error. */
+ int (*http_error)(struct mg_connection *, int status);
+
+ /* Called after civetweb context has been created, before requests
+ are processed.
+ Parameters:
+ ctx: context handle */
+ void (*init_context)(const struct mg_context *ctx);
+
+ /* Called when a new worker thread is initialized.
+ Parameters:
+ ctx: context handle
+ thread_type:
+ 0 indicates the master thread
+ 1 indicates a worker thread handling client connections
+ 2 indicates an internal helper thread (timer thread)
+ */
+ void (*init_thread)(const struct mg_context *ctx, int thread_type);
+
+ /* Called when civetweb context is deleted.
+ Parameters:
+ ctx: context handle */
+ void (*exit_context)(const struct mg_context *ctx);
+};
+
+
+/* Start web server.
+
+ Parameters:
+ callbacks: mg_callbacks structure with user-defined callbacks.
+ options: NULL terminated list of option_name, option_value pairs that
+ specify Civetweb configuration parameters.
+
+ Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
+ processing is required for these, signal handlers must be set up
+ after calling mg_start().
+
+
+ Example:
+ const char *options[] = {
+ "document_root", "/var/www",
+ "listening_ports", "80,443s",
+ NULL
+ };
+ struct mg_context *ctx = mg_start(&my_func, NULL, options);
+
+ Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
+ for the list of valid option and their possible values.
+
+ Return:
+ web server context, or NULL on error. */
+CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
+ void *user_data,
+ const char **configuration_options);
+
+
+/* Stop the web server.
+
+ Must be called last, when an application wants to stop the web server and
+ release all associated resources. This function blocks until all Civetweb
+ threads are stopped. Context pointer becomes invalid. */
+CIVETWEB_API void mg_stop(struct mg_context *);
+
+
+/* mg_request_handler
+
+ Called when a new request comes in. This callback is URI based
+ and configured with mg_set_request_handler().
+
+ Parameters:
+ conn: current connection information.
+ cbdata: the callback data configured with mg_set_request_handler().
+ Returns:
+ 0: the handler could not handle the request, so fall through.
+ 1 - 999: the handler processed the request. The return code is
+ stored as a HTTP status code for the access log. */
+typedef int (*mg_request_handler)(struct mg_connection *conn, void *cbdata);
+
+
+/* mg_set_request_handler
+
+ Sets or removes a URI mapping for a request handler.
+ This function uses mg_lock_context internally.
+
+ URI's are ordered and prefixed URI's are supported. For example,
+ consider two URIs: /a/b and /a
+ /a matches /a
+ /a/b matches /a/b
+ /a/c matches /a
+
+ Parameters:
+ ctx: server context
+ uri: the URI (exact or pattern) for the handler
+ handler: the callback handler to use when the URI is requested.
+ If NULL, an already registered handler for this URI will be
+ removed.
+ The URI used to remove a handler must match exactly the one used
+ to
+ register it (not only a pattern match).
+ cbdata: the callback data to give to the handler when it is called. */
+CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_request_handler handler,
+ void *cbdata);
+
+
+/* Callback types for websocket handlers in C/C++.
+
+ mg_websocket_connect_handler
+ Is called when the client intends to establish a websocket connection,
+ before websocket handshake.
+ Return value:
+ 0: civetweb proceeds with websocket handshake.
+ 1: connection is closed immediately.
+
+ mg_websocket_ready_handler
+ Is called when websocket handshake is successfully completed, and
+ connection is ready for data exchange.
+
+ mg_websocket_data_handler
+ Is called when a data frame has been received from the client.
+ Parameters:
+ bits: first byte of the websocket frame, see websocket RFC at
+ http://tools.ietf.org/html/rfc6455, section 5.2
+ data, data_len: payload, with mask (if any) already applied.
+ Return value:
+ 1: keep this websocket connection open.
+ 0: close this websocket connection.
+
+ mg_connection_close_handler
+ Is called, when the connection is closed.*/
+typedef int (*mg_websocket_connect_handler)(const struct mg_connection *,
+ void *);
+typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
+typedef int (*mg_websocket_data_handler)(struct mg_connection *,
+ int,
+ char *,
+ size_t,
+ void *);
+typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
+ void *);
+
+
+/* mg_set_websocket_handler
+
+ Set or remove handler functions for websocket connections.
+ This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void
+mg_set_websocket_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_websocket_connect_handler connect_handler,
+ mg_websocket_ready_handler ready_handler,
+ mg_websocket_data_handler data_handler,
+ mg_websocket_close_handler close_handler,
+ void *cbdata);
+
+
+/* mg_authorization_handler
+
+ Some description here
+
+ Parameters:
+ conn: current connection information.
+ cbdata: the callback data configured with mg_set_request_handler().
+ Returns:
+ 0: access denied
+ 1: access granted
+ */
+typedef int (*mg_authorization_handler)(struct mg_connection *conn,
+ void *cbdata);
+
+
+/* mg_set_auth_handler
+
+ Sets or removes a URI mapping for an authorization handler.
+ This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_authorization_handler handler,
+ void *cbdata);
+
+
+/* Get the value of particular configuration parameter.
+ The value returned is read-only. Civetweb does not allow changing
+ configuration at run time.
+ If given parameter name is not valid, NULL is returned. For valid
+ names, return value is guaranteed to be non-NULL. If parameter is not
+ set, zero-length string is returned. */
+CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx,
+ const char *name);
+
+
+/* Get context from connection. */
+CIVETWEB_API struct mg_context *
+mg_get_context(const struct mg_connection *conn);
+
+
+/* Get user data passed to mg_start from context. */
+CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
+
+
+/* Set user data for the current connection. */
+CIVETWEB_API void mg_set_user_connection_data(struct mg_connection *conn,
+ void *data);
+
+
+/* Get user data set for the current connection. */
+CIVETWEB_API void *
+mg_get_user_connection_data(const struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* Return array of strings that represent valid configuration options.
+ For each option, option name and default value is returned, i.e. the
+ number of entries in the array equals to number_of_options x 2.
+ Array is NULL terminated. */
+/* Deprecated: Use mg_get_valid_options instead. */
+CIVETWEB_API const char **mg_get_valid_option_names(void);
+#endif
+
+
+struct mg_option {
+ const char *name;
+ int type;
+ const char *default_value;
+};
+
+
+enum {
+ CONFIG_TYPE_UNKNOWN = 0x0,
+ CONFIG_TYPE_NUMBER = 0x1,
+ CONFIG_TYPE_STRING = 0x2,
+ CONFIG_TYPE_FILE = 0x3,
+ CONFIG_TYPE_DIRECTORY = 0x4,
+ CONFIG_TYPE_BOOLEAN = 0x5,
+ CONFIG_TYPE_EXT_PATTERN = 0x6
+};
+
+
+/* Return array of struct mg_option, representing all valid configuration
+ options of civetweb.c.
+ The array is terminated by a NULL name option. */
+CIVETWEB_API const struct mg_option *mg_get_valid_options(void);
+
+
+struct mg_server_ports {
+ int protocol; /* 1 = IPv4, 2 = IPv6, 3 = both */
+ int port; /* port number */
+ int is_ssl; /* https port: 0 = no, 1 = yes */
+ int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
+ int _reserved1;
+ int _reserved2;
+ int _reserved3;
+ int _reserved4;
+};
+
+
+/* Get the list of ports that civetweb is listening on.
+ The parameter size is the size of the ports array in elements.
+ The caller is responsibility to allocate the required memory.
+ This function returns the number of struct mg_server_ports elements
+ filled in, or <0 in case of an error. */
+CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx,
+ int size,
+ struct mg_server_ports *ports);
+
+
+/* Deprecated: Use mg_get_server_ports instead. */
+CIVETWEB_API size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl);
+
+
+/* Add, edit or delete the entry in the passwords file.
+
+ This function allows an application to manipulate .htpasswd files on the
+ fly by adding, deleting and changing user records. This is one of the
+ several ways of implementing authentication on the server side. For another,
+ cookie-based way please refer to the examples/chat in the source tree.
+
+ If password is not NULL, entry is added (or modified if already exists).
+ If password is NULL, entry is deleted.
+
+ Return:
+ 1 on success, 0 on error. */
+CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
+ const char *domain,
+ const char *user,
+ const char *password);
+
+
+/* Return information associated with the request. */
+CIVETWEB_API const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *);
+
+
+/* Send data to the client.
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len);
+
+
+/* Send data to a websocket client wrapped in a websocket frame. Uses
+ mg_lock_connection to ensure that the transmission is not interrupted,
+ i.e., when the application is proactively communicating and responding to
+ a request simultaneously.
+
+ Send data to a websocket client wrapped in a websocket frame.
+ This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_websocket_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t data_len);
+
+
+/* Send data to a websocket server wrapped in a masked websocket frame. Uses
+ mg_lock_connection to ensure that the transmission is not interrupted,
+ i.e., when the application is proactively communicating and responding to
+ a request simultaneously.
+
+ Send data to a websocket server wrapped in a masked websocket frame.
+ This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t data_len);
+
+
+/* Blocks until unique access is obtained to this connection. Intended for use
+ with websockets only.
+ Invoke this before mg_write or mg_printf when communicating with a
+ websocket if your code has server-initiated communication as well as
+ communication in direct response to a message. */
+CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
+CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+#define mg_lock mg_lock_connection
+#define mg_unlock mg_unlock_connection
+#endif
+
+
+/* Lock server context. This lock may be used to protect resources
+ that are shared between different connection/worker threads. */
+CIVETWEB_API void mg_lock_context(struct mg_context *ctx);
+CIVETWEB_API void mg_unlock_context(struct mg_context *ctx);
+
+
+/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
+enum {
+ WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+ WEBSOCKET_OPCODE_TEXT = 0x1,
+ WEBSOCKET_OPCODE_BINARY = 0x2,
+ WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+ WEBSOCKET_OPCODE_PING = 0x9,
+ WEBSOCKET_OPCODE_PONG = 0xa
+};
+
+
+/* Macros for enabling compiler-specific checks for printf-like arguments. */
+#undef PRINTF_FORMAT_STRING
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <sal.h>
+#if defined(_MSC_VER) && _MSC_VER > 1400
+#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
+#else
+#define PRINTF_FORMAT_STRING(s) __format_string s
+#endif
+#else
+#define PRINTF_FORMAT_STRING(s) s
+#endif
+
+#ifdef __GNUC__
+#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
+#else
+#define PRINTF_ARGS(x, y)
+#endif
+
+
+/* Send data to the client using printf() semantics.
+ Works exactly like mg_write(), but allows to do message formatting. */
+CIVETWEB_API int mg_printf(struct mg_connection *,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(2, 3);
+
+
+/* Send contents of the entire file together with HTTP headers. */
+CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
+
+/* Send contents of the entire file together with HTTP headers.
+ Parameters:
+ conn: Current connection information.
+ path: Full path to the file to send.
+ mime_type: Content-Type for file. NULL will cause the type to be
+ looked up by the file extension.
+*/
+CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn,
+ const char *path,
+ const char *mime_type);
+
+/* Store body data into a file. */
+CIVETWEB_API long long mg_store_body(struct mg_connection *conn,
+ const char *path);
+/* Read entire request body and stor it in a file "path".
+ Return:
+ < 0 Error
+ >= 0 Number of bytes stored in file "path".
+*/
+
+
+/* Read data from the remote end, return number of bytes read.
+ Return:
+ 0 connection has been closed by peer. No more data could be read.
+ < 0 read error. No more data could be read from the connection.
+ > 0 number of bytes read into the buffer. */
+CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
+
+
+/* Get the value of particular HTTP header.
+
+ This is a helper function. It traverses request_info->http_headers array,
+ and if the header is present in the array, returns its value. If it is
+ not present, NULL is returned. */
+CIVETWEB_API const char *mg_get_header(const struct mg_connection *,
+ const char *name);
+
+
+/* Get a value of particular form variable.
+
+ Parameters:
+ data: pointer to form-uri-encoded buffer. This could be either POST data,
+ or request_info.query_string.
+ data_len: length of the encoded data.
+ var_name: variable name to decode from the buffer
+ dst: destination buffer for the decoded variable
+ dst_len: length of the destination buffer
+
+ Return:
+ On success, length of the decoded variable.
+ On error:
+ -1 (variable not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ decoded variable).
+
+ Destination buffer is guaranteed to be '\0' - terminated if it is not
+ NULL or zero length. */
+CIVETWEB_API int mg_get_var(const char *data,
+ size_t data_len,
+ const char *var_name,
+ char *dst,
+ size_t dst_len);
+
+
+/* Get a value of particular form variable.
+
+ Parameters:
+ data: pointer to form-uri-encoded buffer. This could be either POST data,
+ or request_info.query_string.
+ data_len: length of the encoded data.
+ var_name: variable name to decode from the buffer
+ dst: destination buffer for the decoded variable
+ dst_len: length of the destination buffer
+ occurrence: which occurrence of the variable, 0 is the first, 1 the
+ second...
+ this makes it possible to parse a query like
+ b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1
+
+ Return:
+ On success, length of the decoded variable.
+ On error:
+ -1 (variable not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ decoded variable).
+
+ Destination buffer is guaranteed to be '\0' - terminated if it is not
+ NULL or zero length. */
+CIVETWEB_API int mg_get_var2(const char *data,
+ size_t data_len,
+ const char *var_name,
+ char *dst,
+ size_t dst_len,
+ size_t occurrence);
+
+
+/* Fetch value of certain cookie variable into the destination buffer.
+
+ Destination buffer is guaranteed to be '\0' - terminated. In case of
+ failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
+ parameter. This function returns only first occurrence.
+
+ Return:
+ On success, value length.
+ On error:
+ -1 (either "Cookie:" header is not present at all or the requested
+ parameter is not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ value). */
+CIVETWEB_API int mg_get_cookie(const char *cookie,
+ const char *var_name,
+ char *buf,
+ size_t buf_len);
+
+
+/* Download data from the remote web server.
+ host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
+ port: port number, e.g. 80.
+ use_ssl: wether to use SSL connection.
+ error_buffer, error_buffer_size: error message placeholder.
+ request_fmt,...: HTTP request.
+ Return:
+ On success, valid pointer to the new connection, suitable for mg_read().
+ On error, NULL. error_buffer contains error message.
+ Example:
+ char ebuf[100];
+ struct mg_connection *conn;
+ conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
+ "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+ */
+CIVETWEB_API struct mg_connection *
+mg_download(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ PRINTF_FORMAT_STRING(const char *request_fmt),
+ ...) PRINTF_ARGS(6, 7);
+
+
+/* Close the connection opened by mg_download(). */
+CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* File upload functionality. Each uploaded file gets saved into a temporary
+ file and MG_UPLOAD event is sent.
+ Return number of uploaded files.
+ Deprecated: Use mg_handle_form_request instead. */
+CIVETWEB_API int mg_upload(struct mg_connection *conn,
+ const char *destination_dir);
+#endif
+
+
+/* This structure contains callback functions for handling form fields.
+ It is used as an argument to mg_handle_form_request. */
+struct mg_form_data_handler {
+ /* This callback function is called, if a new field has been found.
+ * The return value of this callback is used to define how the field
+ * should be processed.
+ *
+ * Parameters:
+ * key: Name of the field ("name" property of the HTML input field).
+ * filename: Name of a file to upload, at the client computer.
+ * Only set for input fields of type "file", otherwise NULL.
+ * path: Output parameter: File name (incl. path) to store the file
+ * at the server computer. Only used if FORM_FIELD_STORAGE_STORE
+ * is returned by this callback. Existing files will be
+ * overwritten.
+ * pathlen: Length of the buffer for path.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * The callback must return the intended storage for this field
+ * (See FORM_FIELD_STORAGE_*).
+ */
+ int (*field_found)(const char *key,
+ const char *filename,
+ char *path,
+ size_t pathlen,
+ void *user_data);
+
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
+ * this callback will receive the field data.
+ *
+ * Parameters:
+ * key: Name of the field ("name" property of the HTML input field).
+ * value: Value of the input field.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * TODO: Needs to be defined.
+ */
+ int (*field_get)(const char *key,
+ const char *value,
+ size_t valuelen,
+ void *user_data);
+
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
+ * the data will be stored into a file. If the file has been written
+ * successfully, this callback will be called. This callback will
+ * not be called for only partially uploaded files. The
+ * mg_handle_form_request function will either store the file completely
+ * and call this callback, or it will remove any partial content and
+ * not call this callback function.
+ *
+ * Parameters:
+ * path: Path of the file stored at the server.
+ * file_size: Size of the stored file in bytes.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * TODO: Needs to be defined.
+ */
+ int (*field_store)(const char *path, long long file_size, void *user_data);
+
+ /* User supplied argument, passed to all callback functions. */
+ void *user_data;
+};
+
+
+/* Return values definition for the "field_found" callback in
+ * mg_form_data_handler. */
+enum {
+ /* Skip this field (neither get nor store it). Continue with the
+ * next field. */
+ FORM_FIELD_STORAGE_SKIP = 0x0,
+ /* Get the field value. */
+ FORM_FIELD_STORAGE_GET = 0x1,
+ /* Store the field value into a file. */
+ FORM_FIELD_STORAGE_STORE = 0x2,
+ /* Stop parsing this request. Skip the remaining fields. */
+ FORM_FIELD_STORAGE_ABORT = 0x10
+};
+
+
+/* Process form data.
+ * Returns the number of fields handled, or < 0 in case of an error.
+ * Note: It is possible that several fields are already handled successfully
+ * (e.g., stored into files), before the request handling is stopped with an
+ * error. In this case a number < 0 is returned as well.
+ * In any case, it is the duty of the caller to remove files once they are
+ * no longer required. */
+CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn,
+ struct mg_form_data_handler *fdh);
+
+
+/* Convenience function -- create detached thread.
+ Return: 0 on success, non-0 on error. */
+typedef void *(*mg_thread_func_t)(void *);
+CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p);
+
+
+/* Return builtin mime type for the given file name.
+ For unrecognized extensions, "text/plain" is returned. */
+CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
+
+
+/* Get text representation of HTTP status code. */
+CIVETWEB_API const char *mg_get_response_code_text(struct mg_connection *conn,
+ int response_code);
+
+
+/* Return CivetWeb version. */
+CIVETWEB_API const char *mg_version(void);
+
+
+/* URL-decode input buffer into destination buffer.
+ 0-terminate the destination buffer.
+ form-url-encoded data differs from URI encoding in a way that it
+ uses '+' as character for space, see RFC 1866 section 8.2.1
+ http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
+ Return: length of the decoded data, or -1 if dst buffer is too small. */
+CIVETWEB_API int mg_url_decode(const char *src,
+ int src_len,
+ char *dst,
+ int dst_len,
+ int is_form_url_encoded);
+
+
+/* URL-encode input buffer into destination buffer.
+ returns the length of the resulting buffer or -1
+ is the buffer is too small. */
+CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
+
+
+/* MD5 hash given strings.
+ Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
+ ASCIIz strings. When function returns, buf will contain human-readable
+ MD5 hash. Example:
+ char buf[33];
+ mg_md5(buf, "aa", "bb", NULL); */
+CIVETWEB_API char *mg_md5(char buf[33], ...);
+
+
+/* Print error message to the opened error log stream.
+ This utilizes the provided logging configuration.
+ conn: connection
+ fmt: format string without the line return
+ ...: variable argument list
+ Example:
+ mg_cry(conn,"i like %s", "logging"); */
+CIVETWEB_API void mg_cry(const struct mg_connection *conn,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(2, 3);
+
+
+/* utility methods to compare two buffers, case incensitive. */
+CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2);
+CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
+
+
+/* Connect to a websocket as a client
+ Parameters:
+ host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or
+ "localhost"
+ port: server port
+ use_ssl: make a secure connection to server
+ error_buffer, error_buffer_size: buffer for an error message
+ path: server path you are trying to connect to, i.e. if connection to
+ localhost/app, path should be "/app"
+ origin: value of the Origin HTTP header
+ data_func: callback that should be used when data is received from the
+ server
+ user_data: user supplied argument
+
+ Return:
+ On success, valid mg_connection object.
+ On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *
+mg_connect_websocket_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data);
+
+
+/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
+ Parameters:
+ host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or
+ "localhost"
+ port: server port
+ use_ssl: make a secure connection to server
+ error_buffer, error_buffer_size: buffer for an error message
+
+ Return:
+ On success, valid mg_connection object.
+ On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size);
+
+
+struct mg_client_options {
+ const char *host;
+ int port;
+ const char *client_cert;
+ const char *server_cert;
+ /* TODO: add more data */
+};
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+ char *error_buffer,
+ size_t error_buffer_size);
+
+
+enum { TIMEOUT_INFINITE = -1 };
+
+
+/* Wait for a response from the server
+ Parameters:
+ conn: connection
+ ebuf, ebuf_len: error message placeholder.
+ timeout: time to wait for a response in milliseconds (if < 0 then wait
+ forever)
+
+ Return:
+ On success, >= 0
+ On error/timeout, < 0
+*/
+CIVETWEB_API int mg_get_response(struct mg_connection *conn,
+ char *ebuf,
+ size_t ebuf_len,
+ int timeout);
+
+
+/* Check which features where set when civetweb has been compiled.
+ Parameters:
+ feature: specifies which feature should be checked
+ 1 serve files (NO_FILES not set)
+ 2 support HTTPS (NO_SSL not set)
+ 4 support CGI (NO_CGI not set)
+ 8 support IPv6 (USE_IPV6 set)
+ 16 support WebSocket (USE_WEBSOCKET set)
+ 32 support Lua scripts and Lua server pages (USE_LUA is set)
+ 64 support server side JavaScript (USE_DUKTAPE is set)
+ 128 support caching (NO_CACHING not set)
+ The result is undefined for all other feature values.
+
+ Return:
+ If feature is available > 0
+ If feature is not available = 0
+*/
+CIVETWEB_API unsigned mg_check_feature(unsigned feature);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CIVETWEB_HEADER_INCLUDED */
--- /dev/null
+/* Copyright (c) 2016 the Civetweb developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+static int
+url_encoded_field_found(const struct mg_connection *conn,
+ const char *key,
+ size_t key_len,
+ const char *filename,
+ size_t filename_len,
+ char *path,
+ size_t path_len,
+ struct mg_form_data_handler *fdh)
+{
+ char key_dec[1024];
+ char filename_dec[1024];
+ int key_dec_len;
+ int filename_dec_len;
+ int ret;
+
+ key_dec_len =
+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+ if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
+ return FORM_FIELD_STORAGE_SKIP;
+ }
+
+ if (filename) {
+ filename_dec_len = mg_url_decode(filename,
+ (int)filename_len,
+ filename_dec,
+ (int)sizeof(filename_dec),
+ 1);
+
+ if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
+ || (filename_dec_len < 0)) {
+ /* Log error message and skip this field. */
+ mg_cry(conn, "%s: Cannot decode filename", __func__);
+ return FORM_FIELD_STORAGE_SKIP;
+ }
+ } else {
+ filename_dec[0] = 0;
+ }
+
+ ret =
+ fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
+
+ if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
+ if (fdh->field_get == NULL) {
+ mg_cry(conn, "%s: Function \"Get\" not available", __func__);
+ return FORM_FIELD_STORAGE_SKIP;
+ }
+ }
+ if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
+ if (fdh->field_store == NULL) {
+ mg_cry(conn, "%s: Function \"Store\" not available", __func__);
+ return FORM_FIELD_STORAGE_SKIP;
+ }
+ }
+
+ return ret;
+}
+
+
+static int
+url_encoded_field_get(const struct mg_connection *conn,
+ const char *key,
+ size_t key_len,
+ const char *value,
+ size_t value_len,
+ struct mg_form_data_handler *fdh)
+{
+ char key_dec[1024];
+
+ char *value_dec = mg_malloc(value_len + 1);
+ int value_dec_len;
+
+ if (!value_dec) {
+ /* Log error message and stop parsing the form data. */
+ mg_cry(conn,
+ "%s: Not enough memory (required: %lu)",
+ __func__,
+ (unsigned long)(value_len + 1));
+ return FORM_FIELD_STORAGE_ABORT;
+ }
+
+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+ value_dec_len =
+ mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
+
+ return fdh->field_get(key_dec,
+ value_dec,
+ (size_t)value_dec_len,
+ fdh->user_data);
+}
+
+
+static int
+field_stored(const struct mg_connection *conn,
+ const char *path,
+ long long file_size,
+ struct mg_form_data_handler *fdh)
+{
+ /* Equivalent to "upload" callback of "mg_upload". */
+
+ (void)conn; /* we do not need mg_cry here, so conn is currently unused */
+
+ return fdh->field_store(path, file_size, fdh->user_data);
+}
+
+
+static const char *
+search_boundary(const char *buf,
+ size_t buf_len,
+ const char *boundary,
+ size_t boundary_len)
+{
+ /* We must do a binary search here, not a string search, since the buffer
+ * may contain '\x00' bytes, if binary data is transferred. */
+ int clen = (int)buf_len - (int)boundary_len - 4;
+ int i;
+
+ for (i = 0; i <= clen; i++) {
+ if (!memcmp(buf + i, "\r\n--", 4)) {
+ if (!memcmp(buf + i + 4, boundary, boundary_len)) {
+ return buf + i;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+int
+mg_handle_form_request(struct mg_connection *conn,
+ struct mg_form_data_handler *fdh)
+{
+ const char *content_type;
+ char path[512];
+ char buf[1024];
+ int field_storage;
+ int buf_fill = 0;
+ int r;
+ int field_count = 0;
+ struct file fstore = STRUCT_FILE_INITIALIZER;
+ int64_t file_size = 0; /* init here, to a avoid a false positive
+ "uninitialized variable used" warning */
+
+ int has_body_data =
+ (conn->request_info.content_length > 0) || (conn->is_chunked);
+
+ /* There are three ways to encode data from a HTML form:
+ * 1) method: GET (default)
+ * The form data is in the HTTP query string.
+ * 2) method: POST, enctype: "application/x-www-form-urlencoded"
+ * The form data is in the request body.
+ * The body is url encoded (the default encoding for POST).
+ * 3) method: POST, enctype: "multipart/form-data".
+ * The form data is in the request body of a multipart message.
+ * This is the typical way to handle file upload from a form.
+ */
+
+ if (!has_body_data) {
+ const char *data;
+
+ if (strcmp(conn->request_info.request_method, "GET")) {
+ /* No body data, but not a GET request.
+ * This is not a valid form request. */
+ return -1;
+ }
+
+ /* GET request: form data is in the query string. */
+ /* The entire data has already been loaded, so there is no nead to
+ * call mg_read. We just need to split the query string into key-value
+ * pairs. */
+ data = conn->request_info.query_string;
+ if (!data) {
+ /* No query string. */
+ return -1;
+ }
+
+ /* Split data in a=1&b=xy&c=3&c=4 ... */
+ while (*data) {
+ const char *val = strchr(data, '=');
+ const char *next;
+ ptrdiff_t keylen, vallen;
+
+ if (!val) {
+ break;
+ }
+ keylen = val - data;
+
+ /* In every "field_found" callback we ask what to do with the
+ * data ("field_storage"). This could be:
+ * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
+ * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
+ * callback function
+ * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
+ * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
+ * (for parsing long data on the fly)
+ * (currently not implemented)
+ * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
+ */
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ data,
+ (size_t)keylen,
+ NULL,
+ 0,
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ val++;
+ next = strchr(val, '&');
+ if (next) {
+ vallen = next - val;
+ next++;
+ } else {
+ vallen = (ptrdiff_t)strlen(val);
+ next = val + vallen;
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
+ /* Call callback */
+ url_encoded_field_get(
+ conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
+ }
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+ /* Store the content to a file */
+ if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+ fstore.fp = NULL;
+ }
+ file_size = 0;
+ if (fstore.fp != NULL) {
+ size_t n =
+ (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
+ if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
+ mg_cry(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ fclose(fstore.fp);
+ fstore.fp = NULL;
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+
+ if (fstore.fp) {
+ r = fclose(fstore.fp);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry(conn,
+ "%s: Error saving file %s",
+ __func__,
+ path);
+ remove_bad_file(conn, path);
+ }
+ fstore.fp = NULL;
+ }
+
+ } else {
+ mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+ }
+ }
+
+ /* if (field_storage == FORM_FIELD_STORAGE_READ) { */
+ /* The idea of "field_storage=read" is to let the API user read
+ * data chunk by chunk and to some data processing on the fly.
+ * This should avoid the need to store data in the server:
+ * It should neither be stored in memory, like
+ * "field_storage=get" does, nor in a file like
+ * "field_storage=store".
+ * However, for a "GET" request this does not make any much
+ * sense, since the data is already stored in memory, as it is
+ * part of the query string.
+ */
+ /* } */
+
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+ == FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ /* Proceed to next entry */
+ data = next;
+ }
+
+ return field_count;
+ }
+
+ content_type = mg_get_header(conn, "Content-Type");
+
+ if (!content_type
+ || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
+ || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
+ /* The form data is in the request body data, encoded in key/value
+ * pairs. */
+ int all_data_read = 0;
+
+ /* Read body data and split it in keys and values.
+ * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
+ * Here we use "POST", and read the data from the request body.
+ * The data read on the fly, so it is not required to buffer the
+ * entire request in memory before processing it. */
+ for (;;) {
+ const char *val;
+ const char *next;
+ ptrdiff_t keylen, vallen;
+ ptrdiff_t used;
+ int end_of_key_value_pair_found = 0;
+ int get_block;
+
+ if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+ size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+ r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+ if (r < 0) {
+ /* read error */
+ return -1;
+ }
+ if (r != (int)to_read) {
+ /* TODO: Create a function to get "all_data_read" from
+ * the conn object. All data is read if the Content-Length
+ * has been reached, or if chunked encoding is used and
+ * the end marker has been read, or if the connection has
+ * been closed. */
+ all_data_read = 1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ break;
+ }
+ }
+
+ val = strchr(buf, '=');
+
+ if (!val) {
+ break;
+ }
+ keylen = val - buf;
+ val++;
+
+ /* Call callback */
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ buf,
+ (size_t)keylen,
+ NULL,
+ 0,
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+ == FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+ if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+ fstore.fp = NULL;
+ }
+ file_size = 0;
+ if (!fstore.fp) {
+ mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+ }
+ }
+
+ get_block = 0;
+ /* Loop to read values larger than sizeof(buf)-keylen-2 */
+ do {
+ next = strchr(val, '&');
+ if (next) {
+ vallen = next - val;
+ next++;
+ end_of_key_value_pair_found = 1;
+ } else {
+ vallen = (ptrdiff_t)strlen(val);
+ next = val + vallen;
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
+#if 0
+ if (!end_of_key_value_pair_found && !all_data_read) {
+ /* This callback will deliver partial contents */
+ }
+#else
+ (void)all_data_read; /* avoid warning */
+#endif
+
+ /* Call callback */
+ url_encoded_field_get(conn,
+ ((get_block > 0) ? NULL : buf),
+ ((get_block > 0) ? 0
+ : (size_t)keylen),
+ val,
+ (size_t)vallen,
+ fdh);
+ get_block++;
+ }
+ if (fstore.fp) {
+ size_t n =
+ (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
+ if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
+ mg_cry(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ fclose(fstore.fp);
+ fstore.fp = NULL;
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+ }
+
+ if (!end_of_key_value_pair_found) {
+ used = next - buf;
+ memmove(buf,
+ buf + (size_t)used,
+ sizeof(buf) - (size_t)used);
+ buf_fill -= (int)used;
+ if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+ size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+ r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+ if (r < 0) {
+ /* read error */
+ return -1;
+ }
+ if (r != (int)to_read) {
+ /* TODO: Create a function to get "all_data_read"
+ * from the conn object. All data is read if the
+ * Content-Length has been reached, or if chunked
+ * encoding is used and the end marker has been
+ * read, or if the connection has been closed. */
+ all_data_read = 1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ break;
+ }
+ val = buf;
+ }
+ }
+
+ } while (!end_of_key_value_pair_found);
+
+ if (fstore.fp) {
+ r = fclose(fstore.fp);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry(conn, "%s: Error saving file %s", __func__, path);
+ remove_bad_file(conn, path);
+ }
+ fstore.fp = NULL;
+ }
+
+ /* Proceed to next entry */
+ used = next - buf;
+ memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+ buf_fill -= (int)used;
+ }
+
+ return field_count;
+ }
+
+ if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
+ /* The form data is in the request body data, encoded as multipart
+ * content (see https://www.ietf.org/rfc/rfc1867.txt,
+ * https://www.ietf.org/rfc/rfc2388.txt). */
+ const char *boundary;
+ size_t bl;
+ ptrdiff_t used;
+ struct mg_request_info part_header;
+ char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
+ const char *content_disp;
+ const char *next;
+
+ memset(&part_header, 0, sizeof(part_header));
+
+ /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
+ bl = 20;
+ while (content_type[bl] == ' ') {
+ bl++;
+ }
+
+ /* There has to be a BOUNDARY definition in the Content-Type header */
+ if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
+ /* Malformed request */
+ return -1;
+ }
+
+ boundary = content_type + bl + 9;
+ bl = strlen(boundary);
+
+ if (bl + 800 > sizeof(buf)) {
+ /* Sanity check: The algorithm can not work if bl >= sizeof(buf),
+ * and it will not work effectively, if the buf is only a few byte
+ * larger than bl, or it buf can not hold the multipart header
+ * plus the boundary.
+ * Check some reasonable number here, that should be fulfilled by
+ * any reasonable request from every browser. If it is not
+ * fulfilled, it might be a hand-made request, intended to
+ * interfere with the algorithm. */
+ return -1;
+ }
+
+ for (;;) {
+ size_t towrite, n;
+ int get_block;
+
+ r = mg_read(conn,
+ buf + (size_t)buf_fill,
+ sizeof(buf) - 1 - (size_t)buf_fill);
+ if (r < 0) {
+ /* read error */
+ return -1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ /* No data */
+ return -1;
+ }
+
+ if (buf[0] != '-' || buf[1] != '-') {
+ /* Malformed request */
+ return -1;
+ }
+ if (strncmp(buf + 2, boundary, bl)) {
+ /* Malformed request */
+ return -1;
+ }
+ if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
+ /* Every part must end with \r\n, if there is another part.
+ * The end of the request has an extra -- */
+ if (((size_t)buf_fill != (size_t)(bl + 6))
+ || (strncmp(buf + bl + 2, "--\r\n", 4))) {
+ /* Malformed request */
+ return -1;
+ }
+ /* End of the request */
+ break;
+ }
+
+ /* Next, we need to get the part header: Read until \r\n\r\n */
+ hbuf = buf + bl + 4;
+ hend = strstr(hbuf, "\r\n\r\n");
+ if (!hend) {
+ /* Malformed request */
+ return -1;
+ }
+
+ parse_http_headers(&hbuf, &part_header);
+ if ((hend + 2) != hbuf) {
+ /* Malformed request */
+ return -1;
+ }
+
+ /* Skip \r\n\r\n */
+ hend += 4;
+
+ /* According to the RFC, every part has to have a header field like:
+ * Content-Disposition: form-data; name="..." */
+ content_disp = get_header(&part_header, "Content-Disposition");
+ if (!content_disp) {
+ /* Malformed request */
+ return -1;
+ }
+
+ /* Get the mandatory name="..." part of the Content-Disposition
+ * header. */
+ nbeg = strstr(content_disp, "name=\"");
+ if (!nbeg) {
+ /* Malformed request */
+ return -1;
+ }
+ nbeg += 6;
+ nend = strchr(nbeg, '\"');
+ if (!nend) {
+ /* Malformed request */
+ return -1;
+ }
+
+ /* Get the optional filename="..." part of the Content-Disposition
+ * header. */
+ fbeg = strstr(content_disp, "filename=\"");
+ if (fbeg) {
+ fbeg += 10;
+ fend = strchr(fbeg, '\"');
+ if (!fend) {
+ /* Malformed request (the filename field is optional, but if
+ * it exists, it needs to be terminated correctly). */
+ return -1;
+ }
+
+ /* TODO: check Content-Type */
+ /* Content-Type: application/octet-stream */
+
+ } else {
+ fend = fbeg;
+ }
+
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ nbeg,
+ (size_t)(nend - nbeg),
+ fbeg,
+ (size_t)(fend - fbeg),
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ /* If the boundary is already in the buffer, get the address,
+ * otherwise next will be NULL. */
+ next = search_boundary(hbuf,
+ (size_t)((buf - hbuf) + buf_fill),
+ boundary,
+ bl);
+
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+ /* Store the content to a file */
+ if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+ fstore.fp = NULL;
+ }
+ file_size = 0;
+
+ if (!fstore.fp) {
+ mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+ }
+ }
+
+ get_block = 0;
+ while (!next) {
+ /* Set "towrite" to the number of bytes available
+ * in the buffer */
+ towrite = (size_t)(buf - hend + buf_fill);
+ /* Subtract the boundary length, to deal with
+ * cases the boundary is only partially stored
+ * in the buffer. */
+ towrite -= bl + 4;
+
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
+ url_encoded_field_get(conn,
+ ((get_block > 0) ? NULL : nbeg),
+ ((get_block > 0)
+ ? 0
+ : (size_t)(nend - nbeg)),
+ hend,
+ towrite,
+ fdh);
+ get_block++;
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+ if (fstore.fp) {
+
+ /* Store the content of the buffer. */
+ n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
+ if ((n != towrite) || (ferror(fstore.fp))) {
+ mg_cry(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ fclose(fstore.fp);
+ fstore.fp = NULL;
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+ }
+ }
+
+ memmove(buf, hend + towrite, bl + 4);
+ buf_fill = (int)(bl + 4);
+ hend = buf;
+
+ /* Read new data */
+ r = mg_read(conn,
+ buf + (size_t)buf_fill,
+ sizeof(buf) - 1 - (size_t)buf_fill);
+ if (r < 0) {
+ /* read error */
+ return -1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ /* No data */
+ return -1;
+ }
+
+ /* Find boundary */
+ next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
+ }
+
+ towrite = (size_t)(next - hend);
+
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
+ /* Call callback */
+ url_encoded_field_get(conn,
+ ((get_block > 0) ? NULL : nbeg),
+ ((get_block > 0) ? 0
+ : (size_t)(nend - nbeg)),
+ hend,
+ towrite,
+ fdh);
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+
+ if (fstore.fp) {
+ n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
+ if ((n != towrite) || (ferror(fstore.fp))) {
+ mg_cry(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ fclose(fstore.fp);
+ fstore.fp = NULL;
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+ }
+ }
+
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
+
+ if (fstore.fp) {
+ r = fclose(fstore.fp);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry(conn,
+ "%s: Error saving file %s",
+ __func__,
+ path);
+ remove_bad_file(conn, path);
+ }
+ fstore.fp = NULL;
+ }
+ }
+
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+ == FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ /* Remove from the buffer */
+ used = next - buf + 2;
+ memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+ buf_fill -= (int)used;
+ }
+
+ /* All parts handled */
+ return field_count;
+ }
+
+ /* Unknown Content-Type */
+ return -1;
+}
--- /dev/null
+/*
+ * This an amalgamation of md5.c and md5.h into a single file
+ * with all static declaration to reduce linker conflicts
+ * in Civetweb.
+ *
+ * The MD5_STATIC declaration was added to facilitate static
+ * inclusion.
+ * No Face Press, LLC
+ */
+
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initialize the algorithm. */
+MD5_STATIC void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
+
+/* Finish the message and return the digest. */
+MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef MD5_STATIC
+#include <string.h>
+#endif
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#define BYTE_ORDER (0)
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 (0x242070db)
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 (0x4787c62a)
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 (0x698098d8)
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 (0x6b901122)
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 (0x49b40821)
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 (0x265e5a51)
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 (0x02441453)
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 (0x21e1cde6)
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 (0x455a14ed)
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 (0x676f02d9)
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 (0x6d9d6122)
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 (0x4bdecfa9)
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 (0x289b7ec6)
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 (0x04881d05)
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 (0x1fa27cf8)
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 (0x432aff97)
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 (0x655b59c3)
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 (0x6fa87e4f)
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 (0x4e0811a1)
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 (0x2ad7d2bb)
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
+ d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned, a direct assignment is possible */
+ /* cast through a (void *) should avoid a compiler warning,
+ see
+ https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
+ */
+ X = (const md5_word_t *)(const void *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+#if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+#else
+#define xbuf X /* (static only) */
+#endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
+ + (md5_word_t)(xp[2] << 16)
+ + (md5_word_t)(xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* Round 1. */
+/* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + F(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+/* Round 2. */
+/* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + G(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+/* Round 3. */
+/* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + H(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+/* Round 4. */
+/* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + I(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+MD5_STATIC void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
+{
+ const md5_byte_t *p = data;
+ size_t left = nbytes;
+ size_t offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += (md5_word_t)(nbytes >> 29);
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+MD5_STATIC void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
--- /dev/null
+/* This file is part of the CivetWeb web server.
+ * See https://github.com/civetweb/civetweb/
+ * (C) 2015 by the CivetWeb authors, MIT license.
+ */
+
+#include "duktape.h"
+
+/* TODO: the mg context should be added to duktape as well */
+/* Alternative: redefine a new, clean API from scratch (instead of using mg),
+ * or at least do not add problematic functions. */
+/* For evaluation purposes, currently only "send" is supported.
+ * All other ~50 functions will be added later. */
+
+/* Note: This is only experimental support, so the API may still change. */
+
+static const char *civetweb_conn_id = "\xFF"
+ "civetweb_conn";
+static const char *civetweb_ctx_id = "\xFF"
+ "civetweb_ctx";
+
+
+static void *
+mg_duk_mem_alloc(void *udata, duk_size_t size)
+{
+ return mg_malloc(size);
+}
+
+
+static void *
+mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
+{
+ return mg_realloc(ptr, newsize);
+}
+
+
+static void
+mg_duk_mem_free(void *udata, void *ptr)
+{
+ mg_free(ptr);
+}
+
+
+static void
+mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
+{
+ /* Script is called "protected" (duk_peval_file), so script errors should
+ * never yield in a call to this function. Maybe calls prior to executing
+ * the script could raise a fatal error. */
+ struct mg_connection *conn;
+
+ duk_push_global_stash(ctx);
+ duk_get_prop_string(ctx, -1, civetweb_conn_id);
+ conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+ mg_cry(conn, "%s", msg);
+}
+
+
+static duk_ret_t
+duk_itf_write(duk_context *ctx)
+{
+ struct mg_connection *conn;
+ duk_double_t ret;
+ duk_size_t len = 0;
+ const char *val = duk_require_lstring(ctx, -1, &len);
+
+ /*
+ duk_push_global_stash(ctx);
+ duk_get_prop_string(ctx, -1, civetweb_conn_id);
+ conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+ */
+ duk_push_current_function(ctx);
+ duk_get_prop_string(ctx, -1, civetweb_conn_id);
+ conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+ if (!conn) {
+ duk_error(ctx,
+ DUK_ERR_INTERNAL_ERROR,
+ "function not available without connection object");
+ /* probably never reached, but satisfies static code analysis */
+ return DUK_RET_INTERNAL_ERROR;
+ }
+
+ ret = mg_write(conn, val, len);
+
+ duk_push_number(ctx, ret);
+ return 1;
+}
+
+
+static duk_ret_t
+duk_itf_read(duk_context *ctx)
+{
+ struct mg_connection *conn;
+ char buf[1024];
+ int len;
+
+ duk_push_global_stash(ctx);
+ duk_get_prop_string(ctx, -1, civetweb_conn_id);
+ conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+ if (!conn) {
+ duk_error(ctx,
+ DUK_ERR_INTERNAL_ERROR,
+ "function not available without connection object");
+ /* probably never reached, but satisfies static code analysis */
+ return DUK_RET_INTERNAL_ERROR;
+ }
+
+ len = mg_read(conn, buf, sizeof(buf));
+
+ duk_push_lstring(ctx, buf, len);
+ return 1;
+}
+
+
+static duk_ret_t
+duk_itf_getoption(duk_context *ctx)
+{
+ struct mg_context *cv_ctx;
+ const char *ret;
+ duk_size_t len = 0;
+ const char *val = duk_require_lstring(ctx, -1, &len);
+
+ duk_push_current_function(ctx);
+ duk_get_prop_string(ctx, -1, civetweb_ctx_id);
+ cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
+
+ if (!cv_ctx) {
+ duk_error(ctx,
+ DUK_ERR_INTERNAL_ERROR,
+ "function not available without connection object");
+ /* probably never reached, but satisfies static code analysis */
+ return DUK_RET_INTERNAL_ERROR;
+ }
+
+ ret = mg_get_option(cv_ctx, val);
+ if (ret) {
+ duk_push_string(ctx, ret);
+ } else {
+ duk_push_null(ctx);
+ }
+
+ return 1;
+}
+
+
+static void
+mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
+{
+ int i;
+ duk_context *ctx = NULL;
+
+ conn->must_close = 1;
+
+ /* Create Duktape interpreter state */
+ ctx = duk_create_heap(mg_duk_mem_alloc,
+ mg_duk_mem_realloc,
+ mg_duk_mem_free,
+ NULL,
+ mg_duk_fatal_handler);
+ if (!ctx) {
+ mg_cry(conn, "Failed to create a Duktape heap.");
+ goto exec_duktape_finished;
+ }
+
+ /* Add "conn" object */
+ duk_push_global_object(ctx);
+ duk_push_object(ctx); /* create a new table/object ("conn") */
+
+ duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
+ duk_push_pointer(ctx, (void *)conn);
+ duk_put_prop_string(ctx, -2, civetweb_conn_id);
+ duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
+
+ duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
+ duk_push_pointer(ctx, (void *)conn);
+ duk_put_prop_string(ctx, -2, civetweb_conn_id);
+ duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
+
+ duk_push_string(ctx, conn->request_info.request_method);
+ duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
+
+ duk_push_string(ctx, conn->request_info.request_uri);
+ duk_put_prop_string(ctx, -2, "request_uri");
+
+ duk_push_string(ctx, conn->request_info.local_uri);
+ duk_put_prop_string(ctx, -2, "uri");
+
+ duk_push_string(ctx, conn->request_info.http_version);
+ duk_put_prop_string(ctx, -2, "http_version");
+
+ duk_push_string(ctx, conn->request_info.query_string);
+ duk_put_prop_string(ctx, -2, "query_string");
+
+ duk_push_string(ctx, conn->request_info.remote_addr);
+ duk_put_prop_string(ctx, -2, "remote_addr");
+
+ duk_push_int(ctx, conn->request_info.remote_port);
+ duk_put_prop_string(ctx, -2, "remote_port");
+
+ duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
+ duk_put_prop_string(ctx, -2, "server_port");
+
+ duk_push_object(ctx); /* subfolder "conn.http_headers" */
+ for (i = 0; i < conn->request_info.num_headers; i++) {
+ duk_push_string(ctx, conn->request_info.http_headers[i].value);
+ duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
+ }
+ duk_put_prop_string(ctx, -2, "http_headers");
+
+ duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
+
+ /* Add "civetweb" object */
+ duk_push_global_object(ctx);
+ duk_push_object(ctx); /* create a new table/object ("conn") */
+
+ duk_push_string(ctx, CIVETWEB_VERSION);
+ duk_put_prop_string(ctx, -2, "version");
+
+ duk_push_string(ctx, script_name);
+ duk_put_prop_string(ctx, -2, "script_name");
+
+ if (conn->ctx != NULL) {
+ duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
+ duk_push_pointer(ctx, (void *)(conn->ctx));
+ duk_put_prop_string(ctx, -2, civetweb_ctx_id);
+ duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
+
+ if (conn->ctx->systemName != NULL) {
+ duk_push_string(ctx, conn->ctx->systemName);
+ duk_put_prop_string(ctx, -2, "system");
+ }
+ }
+
+ duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
+
+ duk_push_global_stash(ctx);
+ duk_push_pointer(ctx, (void *)conn);
+ duk_put_prop_string(ctx, -2, civetweb_conn_id);
+
+ if (duk_peval_file(ctx, script_name) != 0) {
+ mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
+ goto exec_duktape_finished;
+ }
+ duk_pop(ctx); /* ignore result */
+
+exec_duktape_finished:
+ duk_destroy_heap(ctx);
+}
--- /dev/null
+#include "civetweb_lua.h"
+#include "civetweb_private_lua.h"
+
+#ifdef _WIN32
+static void *
+mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
+{
+ /* TODO (low): This is an incomplete implementation of mmap for windows.
+ * Currently it is sufficient, but there are a lot of unused parameters.
+ * Better use a function "mg_map" which only has the required parameters,
+ * and implement it using mmap in Linux and CreateFileMapping in Windows.
+ * Noone should expect a full mmap for Windows here.
+ */
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+ HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
+ void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t)len);
+ CloseHandle(mh);
+
+ /* unused parameters */
+ (void)addr;
+ (void)prot;
+ (void)flags;
+ (void)offset;
+
+ return p;
+}
+
+static void
+munmap(void *addr, int64_t length)
+{
+ /* unused parameters */
+ (void)length;
+
+ UnmapViewOfFile(addr);
+}
+
+#define MAP_FAILED (NULL)
+#define MAP_PRIVATE (0)
+#define PROT_READ (0)
+#else
+#include <sys/mman.h>
+#endif
+
+static const char *LUASOCKET = "luasocket";
+static const char lua_regkey_ctx = 1;
+static const char lua_regkey_connlist = 2;
+
+/* Forward declarations */
+static void handle_request(struct mg_connection *);
+static int handle_lsp_request(struct mg_connection *,
+ const char *,
+ struct file *,
+ struct lua_State *);
+
+static void
+reg_string(struct lua_State *L, const char *name, const char *val)
+{
+ if (name != NULL && val != NULL) {
+ lua_pushstring(L, name);
+ lua_pushstring(L, val);
+ lua_rawset(L, -3);
+ }
+}
+
+static void
+reg_int(struct lua_State *L, const char *name, int val)
+{
+ if (name != NULL) {
+ lua_pushstring(L, name);
+ lua_pushinteger(L, val);
+ lua_rawset(L, -3);
+ }
+}
+
+static void
+reg_boolean(struct lua_State *L, const char *name, int val)
+{
+ if (name != NULL) {
+ lua_pushstring(L, name);
+ lua_pushboolean(L, val != 0);
+ lua_rawset(L, -3);
+ }
+}
+
+static void
+reg_conn_function(struct lua_State *L,
+ const char *name,
+ lua_CFunction func,
+ struct mg_connection *conn)
+{
+ if (name != NULL && func != NULL && conn != NULL) {
+ lua_pushstring(L, name);
+ lua_pushlightuserdata(L, conn);
+ lua_pushcclosure(L, func, 1);
+ lua_rawset(L, -3);
+ }
+}
+
+static void
+reg_function(struct lua_State *L, const char *name, lua_CFunction func)
+{
+ if (name != NULL && func != NULL) {
+ lua_pushstring(L, name);
+ lua_pushcclosure(L, func, 0);
+ lua_rawset(L, -3);
+ }
+}
+
+static void
+lua_cry(struct mg_connection *conn,
+ int err,
+ lua_State *L,
+ const char *lua_title,
+ const char *lua_operation)
+{
+ switch (err) {
+ case LUA_OK:
+ case LUA_YIELD:
+ break;
+ case LUA_ERRRUN:
+ mg_cry(conn,
+ "%s: %s failed: runtime error: %s",
+ lua_title,
+ lua_operation,
+ lua_tostring(L, -1));
+ break;
+ case LUA_ERRSYNTAX:
+ mg_cry(conn,
+ "%s: %s failed: syntax error: %s",
+ lua_title,
+ lua_operation,
+ lua_tostring(L, -1));
+ break;
+ case LUA_ERRMEM:
+ mg_cry(conn, "%s: %s failed: out of memory", lua_title, lua_operation);
+ break;
+ case LUA_ERRGCMM:
+ mg_cry(conn,
+ "%s: %s failed: error during garbage collection",
+ lua_title,
+ lua_operation);
+ break;
+ case LUA_ERRERR:
+ mg_cry(conn,
+ "%s: %s failed: error in error handling: %s",
+ lua_title,
+ lua_operation,
+ lua_tostring(L, -1));
+ break;
+ default:
+ mg_cry(conn, "%s: %s failed: error %i", lua_title, lua_operation, err);
+ break;
+ }
+}
+
+static int
+lsp_sock_close(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ if ((num_args == 1) && lua_istable(L, -1)) {
+ lua_getfield(L, -1, "sock");
+ closesocket((SOCKET)lua_tonumber(L, -1));
+ } else {
+ return luaL_error(L, "invalid :close() call");
+ }
+ return 1;
+}
+
+static int
+lsp_sock_recv(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ char buf[2000];
+ int n;
+
+ if ((num_args == 1) && lua_istable(L, -1)) {
+ lua_getfield(L, -1, "sock");
+ n = recv((SOCKET)lua_tonumber(L, -1), buf, sizeof(buf), 0);
+ if (n <= 0) {
+ lua_pushnil(L);
+ } else {
+ lua_pushlstring(L, buf, n);
+ }
+ } else {
+ return luaL_error(L, "invalid :close() call");
+ }
+ return 1;
+}
+
+static int
+lsp_sock_send(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *buf;
+ size_t len, sent = 0;
+ int n = 0, sock;
+
+ if ((num_args == 2) && lua_istable(L, -2) && lua_isstring(L, -1)) {
+ buf = lua_tolstring(L, -1, &len);
+ lua_getfield(L, -2, "sock");
+ sock = (int)lua_tonumber(L, -1);
+ while (sent < len) {
+ if ((n = send(sock, buf + sent, (int)(len - sent), 0)) <= 0) {
+ break;
+ }
+ sent += n;
+ }
+ lua_pushnumber(L, n);
+ } else {
+ return luaL_error(L, "invalid :close() call");
+ }
+ return 1;
+}
+
+static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
+ {"send", lsp_sock_send},
+ {"recv", lsp_sock_recv},
+ {NULL, NULL}};
+
+static int
+lsp_connect(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ char ebuf[100];
+ SOCKET sock;
+ union usa sa;
+ int ok;
+
+ if ((num_args == 3) && lua_isstring(L, -3) && lua_isnumber(L, -2)
+ && lua_isnumber(L, -1)) {
+ ok = connect_socket(NULL,
+ lua_tostring(L, -3),
+ (int)lua_tonumber(L, -2),
+ (int)lua_tonumber(L, -1),
+ ebuf,
+ sizeof(ebuf),
+ &sock,
+ &sa);
+ if (!ok) {
+ return luaL_error(L, ebuf);
+ } else {
+ lua_newtable(L);
+ reg_int(L, "sock", (int)sock);
+ reg_string(L, "host", lua_tostring(L, -4));
+ luaL_getmetatable(L, LUASOCKET);
+ lua_setmetatable(L, -2);
+ /* TODO (high): The metatable misses a _gc method to free the
+ * sock object -> currently lsp_connect is a resource leak. */
+ }
+ } else {
+ return luaL_error(
+ L, "connect(host,port,is_ssl): invalid parameter given.");
+ }
+ return 1;
+}
+
+static int
+lsp_error(lua_State *L)
+{
+ lua_getglobal(L, "mg");
+ lua_getfield(L, -1, "onerror");
+ lua_pushvalue(L, -3);
+ lua_pcall(L, 1, 0, 0);
+ return 0;
+}
+
+/* Silently stop processing chunks. */
+static void
+lsp_abort(lua_State *L)
+{
+ int top = lua_gettop(L);
+ lua_getglobal(L, "mg");
+ lua_pushnil(L);
+ lua_setfield(L, -2, "onerror");
+ lua_settop(L, top);
+ lua_pushstring(L, "aborting");
+ lua_error(L);
+}
+
+struct lsp_var_reader_data {
+ const char *begin;
+ unsigned len;
+ unsigned state;
+};
+
+
+static const char *
+lsp_var_reader(lua_State *L, void *ud, size_t *sz)
+{
+ struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud;
+ const char *ret;
+ (void)(L); /* unused */
+
+ switch (reader->state) {
+ case 0:
+ ret = "mg.write(";
+ *sz = strlen(ret);
+ break;
+ case 1:
+ ret = reader->begin;
+ *sz = reader->len;
+ break;
+ case 2:
+ ret = ")";
+ *sz = strlen(ret);
+ break;
+ default:
+ ret = 0;
+ *sz = 0;
+ }
+
+ reader->state++;
+ return ret;
+}
+
+
+static int
+lsp(struct mg_connection *conn,
+ const char *path,
+ const char *p,
+ int64_t len,
+ lua_State *L)
+{
+ int i, j, pos = 0, lines = 1, lualines = 0, is_var, lua_ok;
+ char chunkname[MG_BUF_LEN];
+ struct lsp_var_reader_data data;
+
+ for (i = 0; i < len; i++) {
+ if (p[i] == '\n')
+ lines++;
+ if ((i + 1) < len && p[i] == '<' && p[i + 1] == '?') {
+
+ /* <?= ?> means a variable is enclosed and its value should be
+ * printed */
+ is_var = ((i + 2) < len && p[i + 2] == '=');
+
+ if (is_var)
+ j = i + 2;
+ else
+ j = i + 1;
+
+ while (j < len) {
+ if (p[j] == '\n')
+ lualines++;
+ if ((j + 1) < len && p[j] == '?' && p[j + 1] == '>') {
+ mg_write(conn, p + pos, i - pos);
+
+ mg_snprintf(conn,
+ NULL, /* name only used for debugging */
+ chunkname,
+ sizeof(chunkname),
+ "@%s+%i",
+ path,
+ lines);
+ lua_pushlightuserdata(L, conn);
+ lua_pushcclosure(L, lsp_error, 1);
+
+ if (is_var) {
+ data.begin = p + (i + 3);
+ data.len = j - (i + 3);
+ data.state = 0;
+ lua_ok = mg_lua_load(
+ L, lsp_var_reader, &data, chunkname, NULL);
+ } else {
+ lua_ok = luaL_loadbuffer(L,
+ p + (i + 2),
+ j - (i + 2),
+ chunkname);
+ }
+
+ if (lua_ok) {
+ /* Syntax error or OOM. Error message is pushed on
+ * stack. */
+ lua_pcall(L, 1, 0, 0);
+ } else {
+ /* Success loading chunk. Call it. */
+ lua_pcall(L, 0, 0, 1);
+ }
+
+ pos = j + 2;
+ i = pos - 1;
+ break;
+ }
+ j++;
+ }
+
+ if (lualines > 0) {
+ lines += lualines;
+ lualines = 0;
+ }
+ }
+ }
+
+ if (i > pos) {
+ mg_write(conn, p + pos, i - pos);
+ }
+
+ return 0;
+}
+
+
+/* mg.write: Send data to the client */
+static int
+lsp_write(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+ const char *str;
+ size_t size;
+ int i;
+
+ for (i = 1; i <= num_args; i++) {
+ if (lua_isstring(L, i)) {
+ str = lua_tolstring(L, i, &size);
+ mg_write(conn, str, size);
+ }
+ }
+
+ return 0;
+}
+
+
+/* mg.read: Read data from the client (e.g., from a POST request) */
+static int
+lsp_read(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ char buf[1024];
+ int len = mg_read(conn, buf, sizeof(buf));
+
+ if (len <= 0)
+ return 0;
+ lua_pushlstring(L, buf, len);
+
+ return 1;
+}
+
+
+/* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
+static int
+lsp_keep_alive(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+
+ /* This function may be called with one parameter (boolean) to set the
+ keep_alive state.
+ Or without a parameter to just query the current keep_alive state. */
+ if ((num_args == 1) && lua_isboolean(L, 1)) {
+ conn->must_close = !lua_toboolean(L, 1);
+ } else if (num_args != 0) {
+ /* Syntax error */
+ return luaL_error(L, "invalid keep_alive() call");
+ }
+
+ /* Return the current "keep_alive" state. This may be false, even it
+ * keep_alive(true) has been called. */
+ lua_pushboolean(L, should_keep_alive(conn));
+ return 1;
+}
+
+
+/* mg.include: Include another .lp file */
+static int
+lsp_include(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+ struct file file = STRUCT_FILE_INITIALIZER;
+ const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+ if (filename) {
+ if (handle_lsp_request(conn, filename, &file, L)) {
+ /* handle_lsp_request returned an error code, meaning an error
+ occured in
+ the included page and mg.onerror returned non-zero. Stop processing.
+ */
+ lsp_abort(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid include() call");
+ }
+ return 0;
+}
+
+
+/* mg.cry: Log an error. Default value for mg.onerror. */
+static int
+lsp_cry(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+ const char *text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+ if (text) {
+ mg_cry(conn, "%s", lua_tostring(L, -1));
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid cry() call");
+ }
+ return 0;
+}
+
+
+/* mg.redirect: Redirect the request (internally). */
+static int
+lsp_redirect(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+ const char *target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+ if (target) {
+ conn->request_info.local_uri = target;
+ handle_request(conn);
+ lsp_abort(L);
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid redirect() call");
+ }
+ return 0;
+}
+
+
+/* mg.send_file */
+static int
+lsp_send_file(lua_State *L)
+{
+ struct mg_connection *conn =
+ (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+ int num_args = lua_gettop(L);
+ const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+ if (filename) {
+ mg_send_file(conn, filename);
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid send_file() call");
+ }
+ return 0;
+}
+
+
+/* mg.get_time */
+static int
+lsp_get_time(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0;
+ struct timespec ts;
+ double d;
+
+ clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts);
+ d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9);
+ lua_pushnumber(L, d);
+ return 1;
+}
+
+
+/* mg.get_var */
+static int
+lsp_get_var(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *data, *var_name;
+ size_t data_len, occurrence;
+ int ret;
+ char dst[512];
+
+ if (num_args >= 2 && num_args <= 3) {
+ data = lua_tolstring(L, 1, &data_len);
+ var_name = lua_tostring(L, 2);
+ occurrence = (num_args > 2) ? (long)lua_tonumber(L, 3) : 0;
+
+ ret =
+ mg_get_var2(data, data_len, var_name, dst, sizeof(dst), occurrence);
+ if (ret >= 0) {
+ /* Variable found: return value to Lua */
+ lua_pushstring(L, dst);
+ } else {
+ /* Variable not found (TODO (mid): may be string too long) */
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid get_var() call");
+ }
+ return 1;
+}
+
+
+/* mg.get_mime_type */
+static int
+lsp_get_mime_type(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ struct vec mime_type = {0, 0};
+ struct mg_context *ctx;
+ const char *text;
+
+ lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ ctx = (struct mg_context *)lua_touserdata(L, -1);
+
+ if (num_args == 1) {
+ text = lua_tostring(L, 1);
+ if (text) {
+ if (ctx) {
+ get_mime_type(ctx, text, &mime_type);
+ lua_pushlstring(L, mime_type.ptr, mime_type.len);
+ } else {
+ text = mg_get_builtin_mime_type(text);
+ lua_pushstring(L, text);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid argument for get_mime_type() call");
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid get_mime_type() call");
+ }
+ return 1;
+}
+
+
+/* mg.get_cookie */
+static int
+lsp_get_cookie(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *cookie;
+ const char *var_name;
+ int ret;
+ char dst[512];
+
+ if (num_args == 2) {
+ cookie = lua_tostring(L, 1);
+ var_name = lua_tostring(L, 2);
+ if (cookie != NULL && var_name != NULL) {
+ ret = mg_get_cookie(cookie, var_name, dst, sizeof(dst));
+ } else {
+ ret = -1;
+ }
+
+ if (ret >= 0) {
+ lua_pushlstring(L, dst, ret);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid get_cookie() call");
+ }
+ return 1;
+}
+
+
+/* mg.md5 */
+static int
+lsp_md5(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *text;
+ md5_byte_t hash[16];
+ md5_state_t ctx;
+ size_t text_len;
+ char buf[40];
+
+ if (num_args == 1) {
+ text = lua_tolstring(L, 1, &text_len);
+ if (text) {
+ md5_init(&ctx);
+ md5_append(&ctx, (const md5_byte_t *)text, text_len);
+ md5_finish(&ctx, hash);
+ bin2str(buf, hash, sizeof(hash));
+ lua_pushstring(L, buf);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid md5() call");
+ }
+ return 1;
+}
+
+
+/* mg.url_encode */
+static int
+lsp_url_encode(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *text;
+ size_t text_len;
+ char dst[512];
+
+ if (num_args == 1) {
+ text = lua_tolstring(L, 1, &text_len);
+ if (text) {
+ mg_url_encode(text, dst, sizeof(dst));
+ lua_pushstring(L, dst);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid url_encode() call");
+ }
+ return 1;
+}
+
+
+/* mg.url_decode */
+static int
+lsp_url_decode(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *text;
+ size_t text_len;
+ int is_form;
+ char dst[512];
+
+ if (num_args == 1 || (num_args == 2 && lua_isboolean(L, 2))) {
+ text = lua_tolstring(L, 1, &text_len);
+ is_form = (num_args == 2) ? lua_isboolean(L, 2) : 0;
+ if (text) {
+ mg_url_decode(text, text_len, dst, (int)sizeof(dst), is_form);
+ lua_pushstring(L, dst);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid url_decode() call");
+ }
+ return 1;
+}
+
+
+/* mg.base64_encode */
+static int
+lsp_base64_encode(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *text;
+ size_t text_len;
+ char *dst;
+
+ if (num_args == 1) {
+ text = lua_tolstring(L, 1, &text_len);
+ if (text) {
+ dst = (char *)mg_malloc(text_len * 8 / 6 + 4);
+ if (dst) {
+ base64_encode((const unsigned char *)text, (int)text_len, dst);
+ lua_pushstring(L, dst);
+ mg_free(dst);
+ } else {
+ return luaL_error(L, "out of memory in base64_encode() call");
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid base64_encode() call");
+ }
+ return 1;
+}
+
+
+/* mg.base64_encode */
+static int
+lsp_base64_decode(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ const char *text;
+ size_t text_len, dst_len;
+ int ret;
+ char *dst;
+
+ if (num_args == 1) {
+ text = lua_tolstring(L, 1, &text_len);
+ if (text) {
+ dst = (char *)mg_malloc(text_len);
+ if (dst) {
+ ret = base64_decode((const unsigned char *)text,
+ (int)text_len,
+ dst,
+ &dst_len);
+ if (ret != -1) {
+ mg_free(dst);
+ return luaL_error(
+ L, "illegal character in lsp_base64_decode() call");
+ } else {
+ lua_pushlstring(L, dst, dst_len);
+ mg_free(dst);
+ }
+ } else {
+ return luaL_error(L,
+ "out of memory in lsp_base64_decode() call");
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ /* Syntax error */
+ return luaL_error(L, "invalid lsp_base64_decode() call");
+ }
+ return 1;
+}
+
+
+/* mg.get_response_code_text */
+static int
+lsp_get_response_code_text(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ int type1;
+ double code;
+ const char *text;
+
+ if (num_args == 1) {
+ type1 = lua_type(L, 1);
+ if (type1 == LUA_TNUMBER) {
+ /* If the first argument is a number,
+ convert it to the corresponding text. */
+ code = lua_tonumber(L, 1);
+ text = mg_get_response_code_text(NULL, (int)code);
+ if (text)
+ lua_pushstring(L, text);
+ return text ? 1 : 0;
+ }
+ }
+
+ /* Syntax error */
+ return luaL_error(L, "invalid get_response_code_text() call");
+}
+
+
+/* mg.random - might be better than math.random on some systems */
+static int
+lsp_random(lua_State *L)
+{
+ int num_args = lua_gettop(L);
+ if (num_args == 0) {
+ /* The civetweb internal random number generator will generate
+ * a 64 bit random number. */
+ uint64_t r = get_random();
+ /* Lua "number" is a IEEE 754 double precission float:
+ * https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+ * Thus, mask with 2^53-1 to get an integer with the maximum
+ * precission available. */
+ r &= ((((uint64_t)1) << 53) - 1);
+ lua_pushnumber(L, (double)r);
+ return 1;
+ }
+
+ /* Syntax error */
+ return luaL_error(L, "invalid random() call");
+}
+
+
+union {
+ void *p;
+ void (*f)(unsigned char uuid[16]);
+} pf_uuid_generate;
+
+
+/* mg.uuid */
+static int
+lsp_uuid(lua_State *L)
+{
+ union {
+ unsigned char uuid_array[16];
+ struct uuid_struct_type {
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+ } uuid_struct;
+ } uuid;
+
+ char uuid_str[40];
+ int num_args = lua_gettop(L);
+
+ memset(&uuid, 0, sizeof(uuid));
+ memset(uuid_str, 0, sizeof(uuid_str));
+
+ if (num_args == 0) {
+
+ pf_uuid_generate.f(uuid.uuid_array);
+
+ sprintf(uuid_str,
+ "{%08lX-%04X-%04X-%02X%02X-"
+ "%02X%02X%02X%02X%02X%02X}",
+ (unsigned long)uuid.uuid_struct.data1,
+ (unsigned)uuid.uuid_struct.data2,
+ (unsigned)uuid.uuid_struct.data3,
+ (unsigned)uuid.uuid_struct.data4[0],
+ (unsigned)uuid.uuid_struct.data4[1],
+ (unsigned)uuid.uuid_struct.data4[2],
+ (unsigned)uuid.uuid_struct.data4[3],
+ (unsigned)uuid.uuid_struct.data4[4],
+ (unsigned)uuid.uuid_struct.data4[5],
+ (unsigned)uuid.uuid_struct.data4[6],
+ (unsigned)uuid.uuid_struct.data4[7]);
+
+ lua_pushstring(L, uuid_str);
+ return 1;
+ }
+
+ /* Syntax error */
+ return luaL_error(L, "invalid random() call");
+}
+
+
+#ifdef USE_WEBSOCKET
+struct lua_websock_data {
+ lua_State *state;
+ char *script;
+ unsigned references;
+ struct mg_connection *conn[MAX_WORKER_THREADS];
+ pthread_mutex_t ws_mutex;
+};
+#endif
+
+
+/* mg.write for websockets */
+static int
+lwebsock_write(lua_State *L)
+{
+#ifdef USE_WEBSOCKET
+ int num_args = lua_gettop(L);
+ struct lua_websock_data *ws;
+ const char *str;
+ size_t size;
+ int opcode = -1;
+ unsigned i;
+ struct mg_connection *client = NULL;
+
+ lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ ws = (struct lua_websock_data *)lua_touserdata(L, -1);
+
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+ if (num_args == 1) {
+ /* just one text: send it to all client */
+ if (lua_isstring(L, 1)) {
+ opcode = WEBSOCKET_OPCODE_TEXT;
+ }
+ } else if (num_args == 2) {
+ if (lua_isnumber(L, 1)) {
+ /* opcode number and message text */
+ opcode = (int)lua_tointeger(L, 1);
+ } else if (lua_isstring(L, 1)) {
+ /* opcode string and message text */
+ str = lua_tostring(L, 1);
+ if (!mg_strncasecmp(str, "text", 4))
+ opcode = WEBSOCKET_OPCODE_TEXT;
+ else if (!mg_strncasecmp(str, "bin", 3))
+ opcode = WEBSOCKET_OPCODE_BINARY;
+ else if (!mg_strncasecmp(str, "close", 5))
+ opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
+ else if (!mg_strncasecmp(str, "ping", 4))
+ opcode = WEBSOCKET_OPCODE_PING;
+ else if (!mg_strncasecmp(str, "pong", 4))
+ opcode = WEBSOCKET_OPCODE_PONG;
+ else if (!mg_strncasecmp(str, "cont", 4))
+ opcode = WEBSOCKET_OPCODE_CONTINUATION;
+ } else if (lua_isuserdata(L, 1)) {
+ /* client id and message text */
+ client = (struct mg_connection *)lua_touserdata(L, 1);
+ opcode = WEBSOCKET_OPCODE_TEXT;
+ }
+ } else if (num_args == 3) {
+ if (lua_isuserdata(L, 1)) {
+ client = (struct mg_connection *)lua_touserdata(L, 1);
+ if (lua_isnumber(L, 2)) {
+ /* client id, opcode number and message text */
+ opcode = (int)lua_tointeger(L, 2);
+ } else if (lua_isstring(L, 2)) {
+ /* client id, opcode string and message text */
+ str = lua_tostring(L, 2);
+ if (!mg_strncasecmp(str, "text", 4))
+ opcode = WEBSOCKET_OPCODE_TEXT;
+ else if (!mg_strncasecmp(str, "bin", 3))
+ opcode = WEBSOCKET_OPCODE_BINARY;
+ else if (!mg_strncasecmp(str, "close", 5))
+ opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
+ else if (!mg_strncasecmp(str, "ping", 4))
+ opcode = WEBSOCKET_OPCODE_PING;
+ else if (!mg_strncasecmp(str, "pong", 4))
+ opcode = WEBSOCKET_OPCODE_PONG;
+ else if (!mg_strncasecmp(str, "cont", 4))
+ opcode = WEBSOCKET_OPCODE_CONTINUATION;
+ }
+ }
+ }
+
+ if (opcode >= 0 && opcode < 16 && lua_isstring(L, num_args)) {
+ str = lua_tolstring(L, num_args, &size);
+ if (client) {
+ for (i = 0; i < ws->references; i++) {
+ if (client == ws->conn[i]) {
+ mg_websocket_write(ws->conn[i], opcode, str, size);
+ }
+ }
+ } else {
+ for (i = 0; i < ws->references; i++) {
+ mg_websocket_write(ws->conn[i], opcode, str, size);
+ }
+ }
+ } else {
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+ return luaL_error(L, "invalid websocket write() call");
+ }
+
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+#else
+ (void)(L); /* unused */
+#endif
+ return 0;
+}
+
+
+struct laction_arg {
+ lua_State *state;
+ const char *script;
+ pthread_mutex_t *pmutex;
+ char txt[1];
+};
+
+
+static int
+lua_action(struct laction_arg *arg)
+{
+ int err, ok;
+ struct mg_context *ctx;
+
+ (void)pthread_mutex_lock(arg->pmutex);
+
+ lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx);
+ lua_gettable(arg->state, LUA_REGISTRYINDEX);
+ ctx = (struct mg_context *)lua_touserdata(arg->state, -1);
+
+ err = luaL_loadstring(arg->state, arg->txt);
+ if (err != 0) {
+ lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
+ (void)pthread_mutex_unlock(arg->pmutex);
+ mg_free(arg);
+ return 0;
+ }
+ err = lua_pcall(arg->state, 0, 1, 0);
+ if (err != 0) {
+ lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
+ (void)pthread_mutex_unlock(arg->pmutex);
+ mg_free(arg);
+ return 0;
+ }
+
+ ok = lua_type(arg->state, -1);
+ if (lua_isboolean(arg->state, -1)) {
+ ok = lua_toboolean(arg->state, -1);
+ } else {
+ ok = 0;
+ }
+ lua_pop(arg->state, 1);
+
+ (void)pthread_mutex_unlock(arg->pmutex);
+
+ if (!ok) {
+ mg_free(arg);
+ }
+ return ok;
+}
+
+
+static int
+lua_action_free(struct laction_arg *arg)
+{
+ if (lua_action(arg)) {
+ mg_free(arg);
+ }
+ return 0;
+}
+
+
+static int
+lwebsocket_set_timer(lua_State *L, int is_periodic)
+{
+#if defined(USE_TIMERS) && defined(USE_WEBSOCKET)
+ int num_args = lua_gettop(L);
+ struct lua_websock_data *ws;
+ int type1, type2, ok = 0;
+ double timediff;
+ struct mg_context *ctx;
+ struct laction_arg *arg;
+ const char *txt;
+ size_t txt_len;
+
+ lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ ctx = (struct mg_context *)lua_touserdata(L, -1);
+
+ lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ ws = (struct lua_websock_data *)lua_touserdata(L, -1);
+
+ if (num_args < 2) {
+ return luaL_error(L,
+ "not enough arguments for set_timer/interval() call");
+ }
+
+ type1 = lua_type(L, 1);
+ type2 = lua_type(L, 2);
+
+ if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) {
+ timediff = (double)lua_tonumber(L, 2);
+ txt = lua_tostring(L, 1);
+ txt_len = strlen(txt);
+ arg = (struct laction_arg *)mg_malloc(sizeof(struct laction_arg)
+ + txt_len + 10);
+ arg->state = L;
+ arg->script = ws->script;
+ arg->pmutex = &(ws->ws_mutex);
+ memcpy(arg->txt, "return(", 7);
+ memcpy(arg->txt + 7, txt, txt_len);
+ arg->txt[txt_len + 7] = ')';
+ arg->txt[txt_len + 8] = 0;
+ ok =
+ (0
+ == timer_add(ctx,
+ timediff,
+ is_periodic,
+ 1,
+ (taction)(is_periodic ? lua_action : lua_action_free),
+ (void *)arg));
+ } else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) {
+ /* TODO (mid): not implemented yet */
+ return luaL_error(L, "invalid arguments for set_timer/interval() call");
+ } else {
+ return luaL_error(L, "invalid arguments for set_timer/interval() call");
+ }
+
+ lua_pushboolean(L, ok);
+ return 1;
+
+#else
+ (void)(L); /* unused */
+ (void)(is_periodic); /* unused */
+ return 0;
+#endif
+}
+
+
+/* mg.set_timeout for websockets */
+static int
+lwebsocket_set_timeout(lua_State *L)
+{
+ return lwebsocket_set_timer(L, 0);
+}
+
+
+/* mg.set_interval for websockets */
+static int
+lwebsocket_set_interval(lua_State *L)
+{
+ return lwebsocket_set_timer(L, 1);
+}
+
+enum {
+ LUA_ENV_TYPE_LUA_SERVER_PAGE = 0,
+ LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,
+ LUA_ENV_TYPE_LUA_WEBSOCKET = 2,
+};
+
+
+static void
+prepare_lua_request_info(struct mg_connection *conn, lua_State *L)
+{
+ const char *s;
+ int i;
+
+ /* Export mg.request_info */
+ lua_pushstring(L, "request_info");
+ lua_newtable(L);
+ reg_string(L, "request_method", conn->request_info.request_method);
+ reg_string(L, "request_uri", conn->request_info.request_uri);
+ reg_string(L, "uri", conn->request_info.local_uri);
+ reg_string(L, "http_version", conn->request_info.http_version);
+ reg_string(L, "query_string", conn->request_info.query_string);
+#if defined(MG_LEGACY_INTERFACE)
+ reg_int(L, "remote_ip", conn->request_info.remote_ip); /* remote_ip is
+ deprecated, use
+ remote_addr
+ instead */
+#endif
+ reg_string(L, "remote_addr", conn->request_info.remote_addr);
+ /* TODO (high): ip version */
+ reg_int(L, "remote_port", conn->request_info.remote_port);
+ reg_int(L, "num_headers", conn->request_info.num_headers);
+ reg_int(L, "server_port", ntohs(conn->client.lsa.sin.sin_port));
+
+ if (conn->request_info.content_length >= 0) {
+ /* reg_int64: content_length */
+ lua_pushstring(L, "content_length");
+ lua_pushnumber(
+ L,
+ (lua_Number)conn->request_info
+ .content_length); /* lua_Number may be used as 52 bit integer */
+ lua_rawset(L, -3);
+ }
+ if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
+ reg_string(L, "content_type", s);
+ }
+
+ if (conn->request_info.remote_user != NULL) {
+ reg_string(L, "remote_user", conn->request_info.remote_user);
+ reg_string(L, "auth_type", "Digest");
+ }
+
+ reg_boolean(L, "https", conn->ssl != NULL);
+
+ if (conn->status_code > 0) {
+ /* Lua error handler should show the status code */
+ reg_int(L, "status", conn->status_code);
+ }
+
+ lua_pushstring(L, "http_headers");
+ lua_newtable(L);
+ for (i = 0; i < conn->request_info.num_headers; i++) {
+ reg_string(L,
+ conn->request_info.http_headers[i].name,
+ conn->request_info.http_headers[i].value);
+ }
+ lua_rawset(L, -3);
+
+ lua_rawset(L, -3);
+}
+
+
+void
+civetweb_open_lua_libs(lua_State *L)
+{
+ {
+ extern void luaL_openlibs(lua_State *);
+ luaL_openlibs(L);
+ }
+
+#ifdef USE_LUA_SQLITE3
+ {
+ extern int luaopen_lsqlite3(lua_State *);
+ luaopen_lsqlite3(L);
+ }
+#endif
+#ifdef USE_LUA_LUAXML
+ {
+ extern int luaopen_LuaXML_lib(lua_State *);
+ luaopen_LuaXML_lib(L);
+ }
+#endif
+#ifdef USE_LUA_FILE_SYSTEM
+ {
+ extern int luaopen_lfs(lua_State *);
+ luaopen_lfs(L);
+ }
+#endif
+#ifdef USE_LUA_BINARY
+ {
+ /* TODO (low): Test if this could be used as a replacement for bit32.
+ * Check again with Lua 5.3 later. */
+ extern int luaopen_binary(lua_State *);
+
+ luaL_requiref(L, "binary", luaopen_binary, 1);
+ lua_pop(L, 1);
+ }
+#endif
+}
+
+
+static void
+prepare_lua_environment(struct mg_context *ctx,
+ struct mg_connection *conn,
+ struct lua_websock_data *ws_conn_list,
+ lua_State *L,
+ const char *script_name,
+ int lua_env_type)
+{
+ civetweb_open_lua_libs(L);
+
+#if LUA_VERSION_NUM == 502
+ /* Keep the "connect" method for compatibility,
+ * but do not backport it to Lua 5.1.
+ * TODO: Redesign the interface.
+ */
+ luaL_newmetatable(L, LUASOCKET);
+ lua_pushliteral(L, "__index");
+ luaL_newlib(L, luasocket_methods);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ lua_register(L, "connect", lsp_connect);
+#endif
+
+ /* Store context in the registry */
+ if (ctx != NULL) {
+ lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+ lua_pushlightuserdata(L, (void *)ctx);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+ if (ws_conn_list != NULL) {
+ lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+ lua_pushlightuserdata(L, (void *)ws_conn_list);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+
+ /* Register mg module */
+ lua_newtable(L);
+
+ switch (lua_env_type) {
+ case LUA_ENV_TYPE_LUA_SERVER_PAGE:
+ reg_string(L, "lua_type", "page");
+ break;
+ case LUA_ENV_TYPE_PLAIN_LUA_PAGE:
+ reg_string(L, "lua_type", "script");
+ break;
+ case LUA_ENV_TYPE_LUA_WEBSOCKET:
+ reg_string(L, "lua_type", "websocket");
+ break;
+ }
+
+ if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE
+ || lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) {
+ reg_conn_function(L, "cry", lsp_cry, conn);
+ reg_conn_function(L, "read", lsp_read, conn);
+ reg_conn_function(L, "write", lsp_write, conn);
+ reg_conn_function(L, "keep_alive", lsp_keep_alive, conn);
+ reg_conn_function(L, "send_file", lsp_send_file, conn);
+ }
+
+ if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) {
+ reg_conn_function(L, "include", lsp_include, conn);
+ reg_conn_function(L, "redirect", lsp_redirect, conn);
+ }
+
+ if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
+ reg_function(L, "write", lwebsock_write);
+#ifdef USE_TIMERS
+ reg_function(L, "set_timeout", lwebsocket_set_timeout);
+ reg_function(L, "set_interval", lwebsocket_set_interval);
+#endif
+ /* reg_conn_function(L, "send_file", lsp_send_file, conn); */
+ }
+
+ reg_function(L, "time", lsp_get_time);
+ reg_function(L, "get_var", lsp_get_var);
+ reg_function(L, "get_mime_type", lsp_get_mime_type);
+ reg_function(L, "get_cookie", lsp_get_cookie);
+ reg_function(L, "md5", lsp_md5);
+ reg_function(L, "url_encode", lsp_url_encode);
+ reg_function(L, "url_decode", lsp_url_decode);
+ reg_function(L, "base64_encode", lsp_base64_encode);
+ reg_function(L, "base64_decode", lsp_base64_decode);
+ reg_function(L, "get_response_code_text", lsp_get_response_code_text);
+ reg_function(L, "random", lsp_random);
+ if (pf_uuid_generate.f) {
+ reg_function(L, "uuid", lsp_uuid);
+ }
+
+ reg_string(L, "version", CIVETWEB_VERSION);
+
+ reg_string(L, "script_name", script_name);
+
+ if (ctx != NULL) {
+ reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
+ reg_string(L, "auth_domain", ctx->config[AUTHENTICATION_DOMAIN]);
+#if defined(USE_WEBSOCKET)
+ reg_string(L, "websocket_root", ctx->config[WEBSOCKET_ROOT]);
+#endif
+
+ if (ctx->systemName != NULL) {
+ reg_string(L, "system", ctx->systemName);
+ }
+ }
+
+ /* Export connection specific info */
+ if (conn != NULL) {
+ prepare_lua_request_info(conn, L);
+ }
+
+ lua_setglobal(L, "mg");
+
+ /* Register default mg.onerror function */
+ IGNORE_UNUSED_RESULT(
+ luaL_dostring(L,
+ "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
+ "debug.traceback(e, 1)) end"));
+
+ if (ctx != NULL) {
+ /* Preload */
+ if (ctx->config[LUA_PRELOAD_FILE] != NULL) {
+ IGNORE_UNUSED_RESULT(luaL_dofile(L, ctx->config[LUA_PRELOAD_FILE]));
+ }
+
+ if (ctx->callbacks.init_lua != NULL) {
+ ctx->callbacks.init_lua(conn, L);
+ }
+ }
+}
+
+
+static int
+lua_error_handler(lua_State *L)
+{
+ const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
+
+ lua_getglobal(L, "mg");
+ if (!lua_isnil(L, -1)) {
+ lua_getfield(L, -1, "write"); /* call mg.write() */
+ lua_pushstring(L, error_msg);
+ lua_pushliteral(L, "\n");
+ lua_call(L, 2, 0);
+ IGNORE_UNUSED_RESULT(
+ luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"));
+ } else {
+ printf("Lua error: [%s]\n", error_msg);
+ IGNORE_UNUSED_RESULT(
+ luaL_dostring(L, "print(debug.traceback(), '\\n')"));
+ }
+ /* TODO(lsm, low): leave the stack balanced */
+
+ return 0;
+}
+
+
+static void *
+lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
+{
+
+ (void)ud;
+ (void)osize; /* not used */
+
+ if (nsize == 0) {
+ mg_free(ptr);
+ return NULL;
+ }
+ return mg_realloc(ptr, nsize);
+}
+
+
+static void
+mg_exec_lua_script(struct mg_connection *conn,
+ const char *path,
+ const void **exports)
+{
+ int i;
+ lua_State *L;
+
+ /* Assume the script does not support keep_alive. The script may change this
+ * by calling mg.keep_alive(true). */
+ conn->must_close = 1;
+
+ /* Execute a plain Lua script. */
+ if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) {
+ prepare_lua_environment(
+ conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
+ lua_pushcclosure(L, &lua_error_handler, 0);
+
+ if (exports != NULL) {
+#if LUA_VERSION_NUM > 501
+ lua_pushglobaltable(L);
+ for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
+ lua_CFunction func;
+ lua_pushstring(L, (const char *)(exports[i]));
+ *(const void **)(&func) = exports[i + 1];
+ lua_pushcclosure(L, func, 0);
+ lua_rawset(L, -3);
+ }
+#else
+ for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
+ lua_CFunction func;
+ const char *name = (const char *)(exports[i]);
+ *(const void **)(&func) = exports[i + 1];
+ lua_register(L, name, func);
+ }
+#endif
+ }
+
+ if (luaL_loadfile(L, path) != 0) {
+ lua_error_handler(L);
+ }
+ lua_pcall(L, 0, 0, -2);
+ lua_close(L);
+ }
+}
+
+
+static int
+handle_lsp_request(struct mg_connection *conn,
+ const char *path,
+ struct file *filep,
+ struct lua_State *ls)
+{
+ void *p = NULL;
+ lua_State *L = NULL;
+ int error = 1;
+ struct file filesize = STRUCT_FILE_INITIALIZER;
+
+ /* Assume the script does not support keep_alive. The script may change this
+ * by calling mg.keep_alive(true). */
+ conn->must_close = 1;
+
+ /* We need both mg_stat to get file size, and mg_fopen to get fd */
+ if (!mg_stat(conn, path, &filesize)) {
+
+ /* File not found */
+ if (ls == NULL) {
+ send_http_error(conn, 500, "Error: File %s not found", path);
+ } else {
+ luaL_error(ls, "File [%s] not found", path);
+ }
+
+ goto cleanup_handle_lsp_request;
+ }
+
+ if (!mg_fopen(conn, path, "r", filep)) {
+
+ /* File not found or not accessible */
+ if (ls == NULL) {
+ send_http_error(conn,
+ 500,
+ "Error: Cannot open script file %s",
+ path);
+ } else {
+ luaL_error(ls, "Cannot [%s] not found", path);
+ }
+
+ goto cleanup_handle_lsp_request;
+ }
+
+ /* TODO: Operations mg_fopen and mg_stat should do what their names
+ * indicate. They should not fill in different members of the same
+ * struct file.
+ * See Github issue #225 */
+ filep->size = filesize.size;
+
+ if (filep->membuf == NULL
+ && (p = mmap(NULL,
+ (size_t)filep->size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fileno(filep->fp),
+ 0)) == MAP_FAILED) {
+
+ /* mmap failed */
+ if (ls == NULL) {
+ send_http_error(
+ conn,
+ 500,
+ "Error: Cannot open script\nFile %s can not be mapped",
+ path);
+ } else {
+ luaL_error(ls,
+ "mmap(%s, %zu, %d): %s",
+ path,
+ (size_t)filep->size,
+ fileno(filep->fp),
+ strerror(errno));
+ }
+
+ goto cleanup_handle_lsp_request;
+ }
+
+ if (ls != NULL) {
+ L = ls;
+ } else {
+ L = lua_newstate(lua_allocator, NULL);
+ if (L == NULL) {
+ send_http_error(
+ conn,
+ 500,
+ "%s",
+ "Error: Cannot execute script\nlua_newstate failed");
+
+ goto cleanup_handle_lsp_request;
+ }
+ prepare_lua_environment(
+ conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
+ }
+
+ /* Lua state is ready to use */
+ /* We're not sending HTTP headers here, Lua page must do it. */
+ error = lsp(conn,
+ path,
+ (filep->membuf == NULL) ? (const char *)p
+ : (const char *)filep->membuf,
+ filep->size,
+ L);
+
+
+cleanup_handle_lsp_request:
+
+ if (L != NULL && ls == NULL)
+ lua_close(L);
+ if (p != NULL)
+ munmap(p, filep->size);
+ mg_fclose(filep);
+
+ return error;
+}
+
+
+#ifdef USE_WEBSOCKET
+struct mg_shared_lua_websocket_list {
+ struct lua_websock_data ws;
+ struct mg_shared_lua_websocket_list *next;
+};
+
+
+static void *
+lua_websocket_new(const char *script, struct mg_connection *conn)
+{
+ struct mg_shared_lua_websocket_list **shared_websock_list =
+ &(conn->ctx->shared_lua_websockets);
+ struct lua_websock_data *ws;
+ int err, ok = 0;
+
+ assert(conn->lua_websocket_state == NULL);
+
+ /* lock list (mg_context global) */
+ mg_lock_context(conn->ctx);
+ while (*shared_websock_list) {
+ /* check if ws already in list */
+ if (0 == strcmp(script, (*shared_websock_list)->ws.script)) {
+ break;
+ }
+ shared_websock_list = &((*shared_websock_list)->next);
+ }
+
+ if (*shared_websock_list == NULL) {
+ /* add ws to list */
+ *shared_websock_list = (struct mg_shared_lua_websocket_list *)
+ mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
+ if (*shared_websock_list == NULL) {
+ mg_unlock_context(conn->ctx);
+ mg_cry(conn, "Cannot create shared websocket struct, OOM");
+ return NULL;
+ }
+ /* init ws list element */
+ ws = &(*shared_websock_list)->ws;
+ ws->script = mg_strdup(script); /* TODO (low): handle OOM */
+ pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr);
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+ ws->state = lua_newstate(lua_allocator, NULL);
+ ws->conn[0] = conn;
+ ws->references = 1;
+ prepare_lua_environment(
+ conn->ctx, NULL, ws, ws->state, script, LUA_ENV_TYPE_LUA_WEBSOCKET);
+ err = luaL_loadfile(ws->state, script);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, script, "load");
+ }
+ err = lua_pcall(ws->state, 0, 0, 0);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, script, "init");
+ }
+ } else {
+ /* inc ref count */
+ ws = &(*shared_websock_list)->ws;
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+ (*shared_websock_list)->ws.conn[(ws->references)++] = conn;
+ }
+ mg_unlock_context(conn->ctx);
+
+ /* call add */
+ lua_getglobal(ws->state, "open");
+ lua_newtable(ws->state);
+ prepare_lua_request_info(conn, ws->state);
+ lua_pushstring(ws->state, "client");
+ lua_pushlightuserdata(ws->state, (void *)conn);
+ lua_rawset(ws->state, -3);
+
+ err = lua_pcall(ws->state, 1, 1, 0);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, script, "open handler");
+ } else {
+ if (lua_isboolean(ws->state, -1)) {
+ ok = lua_toboolean(ws->state, -1);
+ }
+ lua_pop(ws->state, 1);
+ }
+ if (!ok) {
+ /* Remove from ws connection list. */
+ /* TODO (mid): Check if list entry and Lua state needs to be deleted
+ * (see websocket_close). */
+ (*shared_websock_list)->ws.conn[--(ws->references)] = 0;
+ }
+
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+ return ok ? (void *)ws : NULL;
+}
+
+
+static int
+lua_websocket_data(struct mg_connection *conn,
+ int bits,
+ char *data,
+ size_t data_len,
+ void *ws_arg)
+{
+ struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+ int err, ok = 0;
+
+ assert(ws != NULL);
+ assert(ws->state != NULL);
+
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+ lua_getglobal(ws->state, "data");
+ lua_newtable(ws->state);
+ lua_pushstring(ws->state, "client");
+ lua_pushlightuserdata(ws->state, (void *)conn);
+ lua_rawset(ws->state, -3);
+ lua_pushstring(ws->state, "bits"); /* TODO: dont use "bits" but fields with
+ a meaning according to
+ http://tools.ietf.org/html/rfc6455,
+ section 5.2 */
+ lua_pushnumber(ws->state, bits);
+ lua_rawset(ws->state, -3);
+ lua_pushstring(ws->state, "data");
+ lua_pushlstring(ws->state, data, data_len);
+ lua_rawset(ws->state, -3);
+
+ err = lua_pcall(ws->state, 1, 1, 0);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, ws->script, "data handler");
+ } else {
+ if (lua_isboolean(ws->state, -1)) {
+ ok = lua_toboolean(ws->state, -1);
+ }
+ lua_pop(ws->state, 1);
+ }
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+ return ok;
+}
+
+
+static int
+lua_websocket_ready(struct mg_connection *conn, void *ws_arg)
+{
+ struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+ int err, ok = 0;
+
+ assert(ws != NULL);
+ assert(ws->state != NULL);
+
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+ lua_getglobal(ws->state, "ready");
+ lua_newtable(ws->state);
+ lua_pushstring(ws->state, "client");
+ lua_pushlightuserdata(ws->state, (void *)conn);
+ lua_rawset(ws->state, -3);
+ err = lua_pcall(ws->state, 1, 1, 0);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, ws->script, "ready handler");
+ } else {
+ if (lua_isboolean(ws->state, -1)) {
+ ok = lua_toboolean(ws->state, -1);
+ }
+ lua_pop(ws->state, 1);
+ }
+
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+ return ok;
+}
+
+
+static void
+lua_websocket_close(struct mg_connection *conn, void *ws_arg)
+{
+ struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+ struct mg_shared_lua_websocket_list **shared_websock_list =
+ &(conn->ctx->shared_lua_websockets);
+ int err = 0;
+ unsigned i;
+
+ assert(ws != NULL);
+ assert(ws->state != NULL);
+
+ (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+ lua_getglobal(ws->state, "close");
+ lua_newtable(ws->state);
+ lua_pushstring(ws->state, "client");
+ lua_pushlightuserdata(ws->state, (void *)conn);
+ lua_rawset(ws->state, -3);
+
+ err = lua_pcall(ws->state, 1, 0, 0);
+ if (err != 0) {
+ lua_cry(conn, err, ws->state, ws->script, "close handler");
+ }
+ for (i = 0; i < ws->references; i++) {
+ if (ws->conn[i] == conn) {
+ ws->references--;
+ ws->conn[i] = ws->conn[ws->references];
+ }
+ }
+ /* TODO: Delete lua_websock_data and remove it from the websocket list.
+ This must only be done, when all connections are closed, and all
+ asynchronous operations and timers are completed/expired. */
+ (void)shared_websock_list; /* shared_websock_list unused (see open TODO) */
+
+ (void)pthread_mutex_unlock(&(ws->ws_mutex));
+}
+#endif
+
+
+static void
+lua_init_optional_libraries(void)
+{
+#if !defined(_WIN32)
+ void *dll_handle = dlopen("libuuid.so", RTLD_LAZY);
+ pf_uuid_generate.p = dlsym(dll_handle, "uuid_generate");
+#else
+ pf_uuid_generate.p = 0;
+#endif
+}
--- /dev/null
+
+#if !defined(MAX_TIMERS)
+#define MAX_TIMERS MAX_WORKER_THREADS
+#endif
+
+typedef int (*taction)(void *arg);
+
+struct ttimer {
+ double time;
+ double period;
+ taction action;
+ void *arg;
+};
+
+struct ttimers {
+ pthread_t threadid; /* Timer thread ID */
+ pthread_mutex_t mutex; /* Protects timer lists */
+ struct ttimer timers[MAX_TIMERS]; /* List of timers */
+ unsigned timer_count; /* Current size of timer list */
+};
+
+static int
+timer_add(struct mg_context *ctx,
+ double next_time,
+ double period,
+ int is_relative,
+ taction action,
+ void *arg)
+{
+ unsigned u, v;
+ int error = 0;
+ struct timespec now;
+
+ if (ctx->stop_flag) {
+ return 0;
+ }
+
+ if (is_relative) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ next_time += now.tv_sec;
+ next_time += now.tv_nsec * 1.0E-9;
+ }
+
+ pthread_mutex_lock(&ctx->timers->mutex);
+ if (ctx->timers->timer_count == MAX_TIMERS) {
+ error = 1;
+ } else {
+ for (u = 0; u < ctx->timers->timer_count; u++) {
+ if (ctx->timers->timers[u].time < next_time) {
+ for (v = ctx->timers->timer_count; v > u; v--) {
+ ctx->timers->timers[v] = ctx->timers->timers[v - 1];
+ }
+ break;
+ }
+ }
+ ctx->timers->timers[u].time = next_time;
+ ctx->timers->timers[u].period = period;
+ ctx->timers->timers[u].action = action;
+ ctx->timers->timers[u].arg = arg;
+ ctx->timers->timer_count++;
+ }
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ return error;
+}
+
+static void
+timer_thread_run(void *thread_func_param)
+{
+ struct mg_context *ctx = (struct mg_context *)thread_func_param;
+ struct timespec now;
+ double d;
+ unsigned u;
+ int re_schedule;
+ struct ttimer t;
+
+ mg_set_thread_name("timer");
+
+ if (ctx->callbacks.init_thread) {
+ /* Timer thread */
+ ctx->callbacks.init_thread(ctx, 2);
+ }
+
+#if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
+ /* TODO */
+ while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &request, &request)
+ == EINTR) { /*nop*/
+ ;
+ }
+#else
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
+ while (ctx->stop_flag == 0) {
+ pthread_mutex_lock(&ctx->timers->mutex);
+ if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
+ t = ctx->timers->timers[0];
+ for (u = 1; u < ctx->timers->timer_count; u++) {
+ ctx->timers->timers[u - 1] = ctx->timers->timers[u];
+ }
+ ctx->timers->timer_count--;
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ re_schedule = t.action(t.arg);
+ if (re_schedule && (t.period > 0)) {
+ timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
+ }
+ continue;
+ } else {
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ }
+ mg_sleep(1);
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
+ }
+#endif
+}
+
+#ifdef _WIN32
+static unsigned __stdcall timer_thread(void *thread_func_param)
+{
+ timer_thread_run(thread_func_param);
+ return 0;
+}
+#else
+static void *
+timer_thread(void *thread_func_param)
+{
+ timer_thread_run(thread_func_param);
+ return NULL;
+}
+#endif /* _WIN32 */
+
+static int
+timers_init(struct mg_context *ctx)
+{
+ ctx->timers = (struct ttimers *)mg_calloc(sizeof(struct ttimers), 1);
+ (void)pthread_mutex_init(&ctx->timers->mutex, NULL);
+
+ /* Start timer thread */
+ mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
+
+ return 0;
+}
+
+static void
+timers_exit(struct mg_context *ctx)
+{
+ if (ctx->timers) {
+ (void)pthread_mutex_destroy(&ctx->timers->mutex);
+ mg_free(ctx->timers);
+ }
+}
--- /dev/null
+
+Licensing of XZ Embedded
+========================
+
+ All the files in this package have been written by Lasse Collin
+ and/or Igor Pavlov. All these files have been put into the
+ public domain. You can do whatever you want with these files.
+
+ As usual, this software is provided "as is", without any warranty.
+
--- /dev/null
+
+XZ Embedded
+===========
+
+ XZ Embedded is a relatively small, limited implementation of the .xz
+ file format. Currently only decoding is implemented.
+
+ XZ Embedded was written for use in the Linux kernel, but the code can
+ be easily used in other environments too, including regular userspace
+ applications. See userspace/xzminidec.c for an example program.
+
+ This README contains information that is useful only when the copy
+ of XZ Embedded isn't part of the Linux kernel tree. You should also
+ read linux/Documentation/xz.txt even if you aren't using XZ Embedded
+ as part of Linux; information in that file is not repeated in this
+ README.
+
+Compiling the Linux kernel module
+
+ The xz_dec module depends on crc32 module, so make sure that you have
+ it enabled (CONFIG_CRC32).
+
+ Building the xz_dec and xz_dec_test modules without support for BCJ
+ filters:
+
+ cd linux/lib/xz
+ make -C /path/to/kernel/source \
+ KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+ CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
+
+ Building the xz_dec and xz_dec_test modules with support for BCJ
+ filters:
+
+ cd linux/lib/xz
+ make -C /path/to/kernel/source \
+ KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+ CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
+ CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
+ CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
+ CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
+
+ If you want only one or a few of the BCJ filters, omit the appropriate
+ variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
+ code shared between all BCJ filters.
+
+ Most people don't need the xz_dec_test module. You can skip building
+ it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
+
+Compiler requirements
+
+ XZ Embedded should compile as either GNU-C89 (used in the Linux
+ kernel) or with any C99 compiler. Getting the code to compile with
+ non-GNU C89 compiler or a C++ compiler should be quite easy as
+ long as there is a data type for unsigned 64-bit integer (or the
+ code is modified not to support large files, which needs some more
+ care than just using 32-bit integer instead of 64-bit).
+
+ If you use GCC, try to use a recent version. For example, on x86-32,
+ xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
+ compiled with GCC 4.3.3.
+
+Embedding into userspace applications
+
+ To embed the XZ decoder, copy the following files into a single
+ directory in your source code tree:
+
+ linux/include/linux/xz.h
+ linux/lib/xz/xz_crc32.c
+ linux/lib/xz/xz_dec_lzma2.c
+ linux/lib/xz/xz_dec_stream.c
+ linux/lib/xz/xz_lzma2.h
+ linux/lib/xz/xz_private.h
+ linux/lib/xz/xz_stream.h
+ userspace/xz_config.h
+
+ Alternatively, xz.h may be placed into a different directory but then
+ that directory must be in the compiler include path when compiling
+ the .c files.
+
+ Your code should use only the functions declared in xz.h. The rest of
+ the .h files are meant only for internal use in XZ Embedded.
+
+ You may want to modify xz_config.h to be more suitable for your build
+ environment. Probably you should at least skim through it even if the
+ default file works as is.
+
+Integrity check support
+
+ XZ Embedded always supports the integrity check types None and
+ CRC32. Support for CRC64 is optional. SHA-256 is currently not
+ supported in XZ Embedded although the .xz format does support it.
+ The xz tool from XZ Utils uses CRC64 by default, but CRC32 is usually
+ enough in embedded systems to keep the code size smaller.
+
+ If you want support for CRC64, you need to copy linux/lib/xz/xz_crc64.c
+ into your application, and #define XZ_USE_CRC64 in xz_config.h or in
+ compiler flags.
+
+ When using the internal CRC32 or CRC64, their lookup tables need to be
+ initialized with xz_crc32_init() and xz_crc64_init(), respectively.
+ See xz.h for details.
+
+ To use external CRC32 or CRC64 code instead of the code from
+ xz_crc32.c or xz_crc64.c, the following #defines may be used
+ in xz_config.h or in compiler flags:
+
+ #define XZ_INTERNAL_CRC32 0
+ #define XZ_INTERNAL_CRC64 0
+
+ Then it is up to you to provide compatible xz_crc32() or xz_crc64()
+ functions.
+
+ If the .xz file being decompressed uses an integrity check type that
+ isn't supported by XZ Embedded, it is treated as an error and the
+ file cannot be decompressed. For multi-call mode, this can be modified
+ by #defining XZ_DEC_ANY_CHECK. Then xz_dec_run() will return
+ XZ_UNSUPPORTED_CHECK when unsupported check type is detected. After
+ that decompression can be continued normally except that the
+ integrity check won't be verified. In single-call mode there's
+ no way to continue decoding, so XZ_DEC_ANY_CHECK is almost useless
+ in single-call mode.
+
+BCJ filter support
+
+ If you want support for one or more BCJ filters, you need to copy also
+ linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
+ #defines in xz_config.h or in compiler flags. You don't need these
+ #defines in the code that just uses XZ Embedded via xz.h, but having
+ them always #defined doesn't hurt either.
+
+ #define Instruction set BCJ filter endianness
+ XZ_DEC_X86 x86-32 or x86-64 Little endian only
+ XZ_DEC_POWERPC PowerPC Big endian only
+ XZ_DEC_IA64 Itanium (IA-64) Big or little endian
+ XZ_DEC_ARM ARM Little endian only
+ XZ_DEC_ARMTHUMB ARM-Thumb Little endian only
+ XZ_DEC_SPARC SPARC Big or little endian
+
+ While some architectures are (partially) bi-endian, the endianness
+ setting doesn't change the endianness of the instructions on all
+ architectures. That's why Itanium and SPARC filters work for both big
+ and little endian executables (Itanium has little endian instructions
+ and SPARC has big endian instructions).
+
+ There currently is no filter for little endian PowerPC or big endian
+ ARM or ARM-Thumb. Implementing filters for them can be considered if
+ there is a need for such filters in real-world applications.
+
+Notes about shared libraries
+
+ If you are including XZ Embedded into a shared library, you very
+ probably should rename the xz_* functions to prevent symbol
+ conflicts in case your library is linked against some other library
+ or application that also has XZ Embedded in it (which may even be
+ a different version of XZ Embedded). TODO: Provide an easy way
+ to do this.
+
+ Please don't create a shared library of XZ Embedded itself unless
+ it is fine to rebuild everything depending on that shared library
+ everytime you upgrade to a newer version of XZ Embedded. There are
+ no API or ABI stability guarantees between different versions of
+ XZ Embedded.
+
--- /dev/null
+
+XZ data compression in Linux
+============================
+
+Introduction
+
+ XZ is a general purpose data compression format with high compression
+ ratio and relatively fast decompression. The primary compression
+ algorithm (filter) is LZMA2. Additional filters can be used to improve
+ compression ratio even further. E.g. Branch/Call/Jump (BCJ) filters
+ improve compression ratio of executable data.
+
+ The XZ decompressor in Linux is called XZ Embedded. It supports
+ the LZMA2 filter and optionally also BCJ filters. CRC32 is supported
+ for integrity checking. The home page of XZ Embedded is at
+ <http://tukaani.org/xz/embedded.html>, where you can find the
+ latest version and also information about using the code outside
+ the Linux kernel.
+
+ For userspace, XZ Utils provide a zlib-like compression library
+ and a gzip-like command line tool. XZ Utils can be downloaded from
+ <http://tukaani.org/xz/>.
+
+XZ related components in the kernel
+
+ The xz_dec module provides XZ decompressor with single-call (buffer
+ to buffer) and multi-call (stateful) APIs. The usage of the xz_dec
+ module is documented in include/linux/xz.h.
+
+ The xz_dec_test module is for testing xz_dec. xz_dec_test is not
+ useful unless you are hacking the XZ decompressor. xz_dec_test
+ allocates a char device major dynamically to which one can write
+ .xz files from userspace. The decompressed output is thrown away.
+ Keep an eye on dmesg to see diagnostics printed by xz_dec_test.
+ See the xz_dec_test source code for the details.
+
+ For decompressing the kernel image, initramfs, and initrd, there
+ is a wrapper function in lib/decompress_unxz.c. Its API is the
+ same as in other decompress_*.c files, which is defined in
+ include/linux/decompress/generic.h.
+
+ scripts/xz_wrap.sh is a wrapper for the xz command line tool found
+ from XZ Utils. The wrapper sets compression options to values suitable
+ for compressing the kernel image.
+
+ For kernel makefiles, two commands are provided for use with
+ $(call if_needed). The kernel image should be compressed with
+ $(call if_needed,xzkern) which will use a BCJ filter and a big LZMA2
+ dictionary. It will also append a four-byte trailer containing the
+ uncompressed size of the file, which is needed by the boot code.
+ Other things should be compressed with $(call if_needed,xzmisc)
+ which will use no BCJ filter and 1 MiB LZMA2 dictionary.
+
+Notes on compression options
+
+ Since the XZ Embedded supports only streams with no integrity check or
+ CRC32, make sure that you don't use some other integrity check type
+ when encoding files that are supposed to be decoded by the kernel. With
+ liblzma, you need to use either LZMA_CHECK_NONE or LZMA_CHECK_CRC32
+ when encoding. With the xz command line tool, use --check=none or
+ --check=crc32.
+
+ Using CRC32 is strongly recommended unless there is some other layer
+ which will verify the integrity of the uncompressed data anyway.
+ Double checking the integrity would probably be waste of CPU cycles.
+ Note that the headers will always have a CRC32 which will be validated
+ by the decoder; you can only change the integrity check type (or
+ disable it) for the actual uncompressed data.
+
+ In userspace, LZMA2 is typically used with dictionary sizes of several
+ megabytes. The decoder needs to have the dictionary in RAM, thus big
+ dictionaries cannot be used for files that are intended to be decoded
+ by the kernel. 1 MiB is probably the maximum reasonable dictionary
+ size for in-kernel use (maybe more is OK for initramfs). The presets
+ in XZ Utils may not be optimal when creating files for the kernel,
+ so don't hesitate to use custom settings. Example:
+
+ xz --check=crc32 --lzma2=dict=512KiB inputfile
+
+ An exception to above dictionary size limitation is when the decoder
+ is used in single-call mode. Decompressing the kernel itself is an
+ example of this situation. In single-call mode, the memory usage
+ doesn't depend on the dictionary size, and it is perfectly fine to
+ use a big dictionary: for maximum compression, the dictionary should
+ be at least as big as the uncompressed data itself.
+
+Future plans
+
+ Creating a limited XZ encoder may be considered if people think it is
+ useful. LZMA2 is slower to compress than e.g. Deflate or LZO even at
+ the fastest settings, so it isn't clear if LZMA2 encoder is wanted
+ into the kernel.
+
+ Support for limited random-access reading is planned for the
+ decompression code. I don't know if it could have any use in the
+ kernel, but I know that it would be useful in some embedded projects
+ outside the Linux kernel.
+
+Conformance to the .xz file format specification
+
+ There are a couple of corner cases where things have been simplified
+ at expense of detecting errors as early as possible. These should not
+ matter in practice all, since they don't cause security issues. But
+ it is good to know this if testing the code e.g. with the test files
+ from XZ Utils.
+
+Reporting bugs
+
+ Before reporting a bug, please check that it's not fixed already
+ at upstream. See <http://tukaani.org/xz/embedded.html> to get the
+ latest code.
+
+ Report bugs to <lasse.collin@tukaani.org> or visit #tukaani on
+ Freenode and talk to Larhzu. I don't actively read LKML or other
+ kernel-related mailing lists, so if there's something I should know,
+ you should email to me personally or use IRC.
+
+ Don't bother Igor Pavlov with questions about the XZ implementation
+ in the kernel or about XZ Utils. While these two implementations
+ include essential code that is directly based on Igor Pavlov's code,
+ these implementations aren't maintained nor supported by him.
+
--- /dev/null
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef DECOMPRESS_UNXZ_H
+#define DECOMPRESS_UNXZ_H
+
+int unxz(unsigned char *in, int in_size,
+ int (*fill)(void *dest, unsigned int size),
+ int (*flush)(void *src, unsigned int size),
+ unsigned char *out, int *in_used,
+ void (*error)(char *x));
+
+#endif
--- /dev/null
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+# include <linux/stddef.h>
+# include <linux/types.h>
+#else
+# include <stddef.h>
+# include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+# define XZ_EXTERN extern
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE: Single-call mode. This uses less RAM than
+ * than multi-call modes, because the LZMA2
+ * dictionary doesn't need to be allocated as
+ * part of the decoder state. All required data
+ * structures are allocated at initialization,
+ * so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2
+ * dictionary buffer. All data structures are
+ * allocated at initialization, so xz_dec_run()
+ * cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is
+ * allocated once the required size has been
+ * parsed from the stream headers. If the
+ * allocation fails, xz_dec_run() will return
+ * XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode {
+ XZ_SINGLE,
+ XZ_PREALLOC,
+ XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK: Everything is OK so far. More input or more
+ * output space is required to continue. This
+ * return code is possible only in multi-call mode
+ * (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END: Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
+ * is still possible in multi-call mode by simply
+ * calling xz_dec_run() again.
+ * Note that this return value is used only if
+ * XZ_DEC_ANY_CHECK was defined at build time,
+ * which is not used in the kernel. Unsupported
+ * check types return XZ_OPTIONS_ERROR if
+ * XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR: Allocating memory failed. This return code is
+ * possible only if the decoder was initialized
+ * with XZ_DYNALLOC. The amount of memory that was
+ * tried to be allocated was no more than the
+ * dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than
+ * allowed by the dict_max argument given to
+ * xz_dec_init(). This return value is possible
+ * only in multi-call mode (XZ_PREALLOC or
+ * XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ * ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic
+ * bytes).
+ * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested
+ * compression options. In the decoder this means
+ * that the header CRC32 matches, but the header
+ * itself specifies something that we don't support.
+ * @XZ_DATA_ERROR: Compressed data is corrupt.
+ * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly
+ * different between multi-call and single-call
+ * mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret {
+ XZ_OK,
+ XZ_STREAM_END,
+ XZ_UNSUPPORTED_CHECK,
+ XZ_MEM_ERROR,
+ XZ_MEMLIMIT_ERROR,
+ XZ_FORMAT_ERROR,
+ XZ_OPTIONS_ERROR,
+ XZ_DATA_ERROR,
+ XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in: Beginning of the input buffer. This may be NULL if and only
+ * if in_pos is equal to in_size.
+ * @in_pos: Current position in the input buffer. This must not exceed
+ * in_size.
+ * @in_size: Size of the input buffer
+ * @out: Beginning of the output buffer. This may be NULL if and only
+ * if out_pos is equal to out_size.
+ * @out_pos: Current position in the output buffer. This must not exceed
+ * out_size.
+ * @out_size: Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_size;
+
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode: Operation mode
+ * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
+ * multi-call decoding. This is ignored in single-call mode
+ * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ * or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ * in practice), so other values for dict_max don't make sense.
+ * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ * 512 KiB, and 1 MiB are probably the only reasonable values,
+ * except for kernel and initramfs images where a bigger
+ * dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s: Decoder state allocated using xz_dec_init()
+ * @b: Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s: Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s: Decoder state allocated using xz_dec_init(). If s is NULL,
+ * this function does nothing.
+ */
+XZ_EXTERN void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+# ifdef __KERNEL__
+# define XZ_INTERNAL_CRC32 0
+# else
+# define XZ_INTERNAL_CRC32 1
+# endif
+#endif
+
+/*
+ * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
+ * implementation is needed too.
+ */
+#ifndef XZ_USE_CRC64
+# undef XZ_INTERNAL_CRC64
+# define XZ_INTERNAL_CRC64 0
+#endif
+#ifndef XZ_INTERNAL_CRC64
+# ifdef __KERNEL__
+# error Using CRC64 in the kernel has not been implemented.
+# else
+# define XZ_INTERNAL_CRC64 1
+# endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+
+#if XZ_INTERNAL_CRC64
+/*
+ * This must be called before any other xz_* function (except xz_crc32_init())
+ * to initialize the CRC64 lookup table.
+ */
+XZ_EXTERN void xz_crc64_init(void);
+
+/*
+ * Update CRC64 value using the polynomial from ECMA-182. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * Important notes about in-place decompression
+ *
+ * At least on x86, the kernel is decompressed in place: the compressed data
+ * is placed to the end of the output buffer, and the decompressor overwrites
+ * most of the compressed data. There must be enough safety margin to
+ * guarantee that the write position is always behind the read position.
+ *
+ * The safety margin for XZ with LZMA2 or BCJ+LZMA2 is calculated below.
+ * Note that the margin with XZ is bigger than with Deflate (gzip)!
+ *
+ * The worst case for in-place decompression is that the beginning of
+ * the file is compressed extremely well, and the rest of the file is
+ * uncompressible. Thus, we must look for worst-case expansion when the
+ * compressor is encoding uncompressible data.
+ *
+ * The structure of the .xz file in case of a compresed kernel is as follows.
+ * Sizes (as bytes) of the fields are in parenthesis.
+ *
+ * Stream Header (12)
+ * Block Header:
+ * Block Header (8-12)
+ * Compressed Data (N)
+ * Block Padding (0-3)
+ * CRC32 (4)
+ * Index (8-20)
+ * Stream Footer (12)
+ *
+ * Normally there is exactly one Block, but let's assume that there are
+ * 2-4 Blocks just in case. Because Stream Header and also Block Header
+ * of the first Block don't make the decompressor produce any uncompressed
+ * data, we can ignore them from our calculations. Block Headers of possible
+ * additional Blocks have to be taken into account still. With these
+ * assumptions, it is safe to assume that the total header overhead is
+ * less than 128 bytes.
+ *
+ * Compressed Data contains LZMA2 or BCJ+LZMA2 encoded data. Since BCJ
+ * doesn't change the size of the data, it is enough to calculate the
+ * safety margin for LZMA2.
+ *
+ * LZMA2 stores the data in chunks. Each chunk has a header whose size is
+ * a maximum of 6 bytes, but to get round 2^n numbers, let's assume that
+ * the maximum chunk header size is 8 bytes. After the chunk header, there
+ * may be up to 64 KiB of actual payload in the chunk. Often the payload is
+ * quite a bit smaller though; to be safe, let's assume that an average
+ * chunk has only 32 KiB of payload.
+ *
+ * The maximum uncompressed size of the payload is 2 MiB. The minimum
+ * uncompressed size of the payload is in practice never less than the
+ * payload size itself. The LZMA2 format would allow uncompressed size
+ * to be less than the payload size, but no sane compressor creates such
+ * files. LZMA2 supports storing uncompressible data in uncompressed form,
+ * so there's never a need to create payloads whose uncompressed size is
+ * smaller than the compressed size.
+ *
+ * The assumption, that the uncompressed size of the payload is never
+ * smaller than the payload itself, is valid only when talking about
+ * the payload as a whole. It is possible that the payload has parts where
+ * the decompressor consumes more input than it produces output. Calculating
+ * the worst case for this would be tricky. Instead of trying to do that,
+ * let's simply make sure that the decompressor never overwrites any bytes
+ * of the payload which it is currently reading.
+ *
+ * Now we have enough information to calculate the safety margin. We need
+ * - 128 bytes for the .xz file format headers;
+ * - 8 bytes per every 32 KiB of uncompressed size (one LZMA2 chunk header
+ * per chunk, each chunk having average payload size of 32 KiB); and
+ * - 64 KiB (biggest possible LZMA2 chunk payload size) to make sure that
+ * the decompressor never overwrites anything from the LZMA2 chunk
+ * payload it is currently reading.
+ *
+ * We get the following formula:
+ *
+ * safety_margin = 128 + uncompressed_size * 8 / 32768 + 65536
+ * = 128 + (uncompressed_size >> 12) + 65536
+ *
+ * For comparison, according to arch/x86/boot/compressed/misc.c, the
+ * equivalent formula for Deflate is this:
+ *
+ * safety_margin = 18 + (uncompressed_size >> 12) + 32768
+ *
+ * Thus, when updating Deflate-only in-place kernel decompressor to
+ * support XZ, the fixed overhead has to be increased from 18+32768 bytes
+ * to 128+65536 bytes.
+ */
+
+/*
+ * STATIC is defined to "static" if we are being built for kernel
+ * decompression (pre-boot code). <linux/decompress/mm.h> will define
+ * STATIC to empty if it wasn't already defined. Since we will need to
+ * know later if we are being used for kernel decompression, we define
+ * XZ_PREBOOT here.
+ */
+#ifdef STATIC
+# define XZ_PREBOOT
+#endif
+#ifdef __KERNEL__
+# include <linux/decompress/mm.h>
+#endif
+#define XZ_EXTERN STATIC
+
+#ifndef XZ_PREBOOT
+# include <linux/slab.h>
+# include <linux/xz.h>
+#else
+/*
+ * Use the internal CRC32 code instead of kernel's CRC32 module, which
+ * is not available in early phase of booting.
+ */
+#define XZ_INTERNAL_CRC32 1
+
+/*
+ * For boot time use, we enable only the BCJ filter of the current
+ * architecture or none if no BCJ filter is available for the architecture.
+ */
+#ifdef CONFIG_X86
+# define XZ_DEC_X86
+#endif
+#ifdef CONFIG_PPC
+# define XZ_DEC_POWERPC
+#endif
+#ifdef CONFIG_ARM
+# define XZ_DEC_ARM
+#endif
+#ifdef CONFIG_IA64
+# define XZ_DEC_IA64
+#endif
+#ifdef CONFIG_SPARC
+# define XZ_DEC_SPARC
+#endif
+
+/*
+ * This will get the basic headers so that memeq() and others
+ * can be defined.
+ */
+#include "xz/xz_private.h"
+
+/*
+ * Replace the normal allocation functions with the versions from
+ * <linux/decompress/mm.h>. vfree() needs to support vfree(NULL)
+ * when XZ_DYNALLOC is used, but the pre-boot free() doesn't support it.
+ * Workaround it here because the other decompressors don't need it.
+ */
+#undef kmalloc
+#undef kfree
+#undef vmalloc
+#undef vfree
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) do { if (ptr != NULL) free(ptr); } while (0)
+
+/*
+ * FIXME: Not all basic memory functions are provided in architecture-specific
+ * files (yet). We define our own versions here for now, but this should be
+ * only a temporary solution.
+ *
+ * memeq and memzero are not used much and any remotely sane implementation
+ * is fast enough. memcpy/memmove speed matters in multi-call mode, but
+ * the kernel image is decompressed in single-call mode, in which only
+ * memcpy speed can matter and only if there is a lot of uncompressible data
+ * (LZMA2 stores uncompressible chunks in uncompressed form). Thus, the
+ * functions below should just be kept small; it's probably not worth
+ * optimizing for speed.
+ */
+
+#ifndef memeq
+static bool memeq(const void *a, const void *b, size_t size)
+{
+ const uint8_t *x = a;
+ const uint8_t *y = b;
+ size_t i;
+
+ for (i = 0; i < size; ++i)
+ if (x[i] != y[i])
+ return false;
+
+ return true;
+}
+#endif
+
+#ifndef memzero
+static void memzero(void *buf, size_t size)
+{
+ uint8_t *b = buf;
+ uint8_t *e = b + size;
+
+ while (b != e)
+ *b++ = '\0';
+}
+#endif
+
+#if 0
+/* Not static to avoid a conflict with the prototype in the Linux headers. */
+void *memmove(void *dest, const void *src, size_t size)
+{
+ uint8_t *d = dest;
+ const uint8_t *s = src;
+ size_t i;
+
+ if (d < s) {
+ for (i = 0; i < size; ++i)
+ d[i] = s[i];
+ } else if (d > s) {
+ i = size;
+ while (i-- > 0)
+ d[i] = s[i];
+ }
+
+ return dest;
+}
+#endif
+
+/*
+ * Since we need memmove anyway, would use it as memcpy too.
+ * Commented out for now to avoid breaking things.
+ */
+/*
+#ifndef memcpy
+# define memcpy memmove
+#endif
+*/
+
+#include "xz/xz_crc32.c"
+#include "xz/xz_dec_stream.c"
+#include "xz/xz_dec_lzma2.c"
+#include "xz/xz_dec_bcj.c"
+
+#endif /* XZ_PREBOOT */
+
+/* Size of the input and output buffers in multi-call mode */
+#define XZ_IOBUF_SIZE 4096
+
+/*
+ * This function implements the API defined in <linux/decompress/generic.h>.
+ *
+ * This wrapper will automatically choose single-call or multi-call mode
+ * of the native XZ decoder API. The single-call mode can be used only when
+ * both input and output buffers are available as a single chunk, i.e. when
+ * fill() and flush() won't be used.
+ */
+int INIT unxz(unsigned char *in, int in_size,
+ int (*fill)(void *dest, unsigned int size),
+ int (*flush)(void *src, unsigned int size),
+ unsigned char *out, int *in_used,
+ void (*error)(char *x))
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ bool must_free_in = false;
+
+#if XZ_INTERNAL_CRC32
+ xz_crc32_init();
+#endif
+
+ if (in_used != NULL)
+ *in_used = 0;
+
+ if (fill == NULL && flush == NULL)
+ s = xz_dec_init(XZ_SINGLE, 0);
+ else
+ s = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+
+ if (s == NULL)
+ goto error_alloc_state;
+
+ if (flush == NULL) {
+ b.out = out;
+ b.out_size = (size_t)-1;
+ } else {
+ b.out_size = XZ_IOBUF_SIZE;
+ b.out = malloc(XZ_IOBUF_SIZE);
+ if (b.out == NULL)
+ goto error_alloc_out;
+ }
+
+ if (in == NULL) {
+ must_free_in = true;
+ in = malloc(XZ_IOBUF_SIZE);
+ if (in == NULL)
+ goto error_alloc_in;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = in_size;
+ b.out_pos = 0;
+
+ if (fill == NULL && flush == NULL) {
+ ret = xz_dec_run(s, &b);
+ } else {
+ do {
+ if (b.in_pos == b.in_size && fill != NULL) {
+ if (in_used != NULL)
+ *in_used += b.in_pos;
+
+ b.in_pos = 0;
+
+ in_size = fill(in, XZ_IOBUF_SIZE);
+ if (in_size < 0) {
+ /*
+ * This isn't an optimal error code
+ * but it probably isn't worth making
+ * a new one either.
+ */
+ ret = XZ_BUF_ERROR;
+ break;
+ }
+
+ b.in_size = in_size;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (flush != NULL && (b.out_pos == b.out_size
+ || (ret != XZ_OK && b.out_pos > 0))) {
+ /*
+ * Setting ret here may hide an error
+ * returned by xz_dec_run(), but probably
+ * it's not too bad.
+ */
+ if (flush(b.out, b.out_pos) != (int)b.out_pos)
+ ret = XZ_BUF_ERROR;
+
+ b.out_pos = 0;
+ }
+ } while (ret == XZ_OK);
+
+ if (must_free_in)
+ free(in);
+
+ if (flush != NULL)
+ free(b.out);
+ }
+
+ if (in_used != NULL)
+ *in_used += b.in_pos;
+
+ xz_dec_end(s);
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ return 0;
+
+ case XZ_MEM_ERROR:
+ /* This can occur only in multi-call mode. */
+ error("XZ decompressor ran out of memory");
+ break;
+
+ case XZ_FORMAT_ERROR:
+ error("Input is not in the XZ format (wrong magic bytes)");
+ break;
+
+ case XZ_OPTIONS_ERROR:
+ error("Input was encoded with settings that are not "
+ "supported by this XZ decoder");
+ break;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ error("XZ-compressed data is corrupt");
+ break;
+
+ default:
+ error("Bug in the XZ decompressor");
+ break;
+ }
+
+ return -1;
+
+error_alloc_in:
+ if (flush != NULL)
+ free(b.out);
+
+error_alloc_out:
+ xz_dec_end(s);
+
+error_alloc_state:
+ error("XZ decompressor ran out of memory");
+ return -1;
+}
+
+/*
+ * This macro is used by architecture-specific files to decompress
+ * the kernel image.
+ */
+#define decompress unxz
--- /dev/null
+config XZ_DEC
+ tristate "XZ decompression support"
+ select CRC32
+ help
+ LZMA2 compression algorithm and BCJ filters are supported using
+ the .xz file format as the container. For integrity checking,
+ CRC32 is supported. See Documentation/xz.txt for more information.
+
+if XZ_DEC
+
+config XZ_DEC_X86
+ bool "x86 BCJ filter decoder"
+ default y if X86
+ select XZ_DEC_BCJ
+
+config XZ_DEC_POWERPC
+ bool "PowerPC BCJ filter decoder"
+ default y if PPC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_IA64
+ bool "IA-64 BCJ filter decoder"
+ default y if IA64
+ select XZ_DEC_BCJ
+
+config XZ_DEC_ARM
+ bool "ARM BCJ filter decoder"
+ default y if ARM
+ select XZ_DEC_BCJ
+
+config XZ_DEC_ARMTHUMB
+ bool "ARM-Thumb BCJ filter decoder"
+ default y if (ARM && ARM_THUMB)
+ select XZ_DEC_BCJ
+
+config XZ_DEC_SPARC
+ bool "SPARC BCJ filter decoder"
+ default y if SPARC
+ select XZ_DEC_BCJ
+
+endif
+
+config XZ_DEC_BCJ
+ bool
+ default n
+
+config XZ_DEC_TEST
+ tristate "XZ decompressor tester"
+ default n
+ depends on XZ_DEC
+ help
+ This allows passing .xz files to the in-kernel XZ decoder via
+ a character special file. It calculates CRC32 of the decompressed
+ data and writes diagnostics to the system log.
+
+ Unless you are developing the XZ decoder, you don't need this
+ and should say N.
--- /dev/null
+obj-$(CONFIG_XZ_DEC) += xz_dec.o
+xz_dec-y := xz_dec_syms.o xz_dec_stream.o xz_dec_lzma2.o
+xz_dec-$(CONFIG_XZ_DEC_BCJ) += xz_dec_bcj.o
+
+obj-$(CONFIG_XZ_DEC_TEST) += xz_dec_test.o
--- /dev/null
+/*
+ * CRC32 using the polynomial from IEEE-802.3
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is not the fastest implementation, but it is pretty compact.
+ * The fastest versions of xz_crc32() on modern CPUs without hardware
+ * accelerated CRC instruction are 3-5 times as fast as this version,
+ * but they are bigger and use more memory for the lookup table.
+ */
+
+#include "xz_private.h"
+
+/*
+ * STATIC_RW_DATA is used in the pre-boot environment on some architectures.
+ * See <linux/decompress/mm.h> for details.
+ */
+#ifndef STATIC_RW_DATA
+# define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint32_t xz_crc32_table[256];
+
+XZ_EXTERN void xz_crc32_init(void)
+{
+ const uint32_t poly = 0xEDB88320;
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t r;
+
+ for (i = 0; i < 256; ++i) {
+ r = i;
+ for (j = 0; j < 8; ++j)
+ r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+ xz_crc32_table[i] = r;
+ }
+
+ return;
+}
+
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+ crc = ~crc;
+
+ while (size != 0) {
+ crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+ --size;
+ }
+
+ return ~crc;
+}
--- /dev/null
+/*
+ * CRC64 using the polynomial from ECMA-182
+ *
+ * This file is similar to xz_crc32.c. See the comments there.
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+#ifndef STATIC_RW_DATA
+# define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint64_t xz_crc64_table[256];
+
+XZ_EXTERN void xz_crc64_init(void)
+{
+ const uint64_t poly = 0xC96C5795D7870F42;
+
+ uint32_t i;
+ uint32_t j;
+ uint64_t r;
+
+ for (i = 0; i < 256; ++i) {
+ r = i;
+ for (j = 0; j < 8; ++j)
+ r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+ xz_crc64_table[i] = r;
+ }
+
+ return;
+}
+
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc)
+{
+ crc = ~crc;
+
+ while (size != 0) {
+ crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+ --size;
+ }
+
+ return ~crc;
+}
--- /dev/null
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+ /* Type of the BCJ filter being used */
+ enum {
+ BCJ_X86 = 4, /* x86 or x86-64 */
+ BCJ_POWERPC = 5, /* Big endian only */
+ BCJ_IA64 = 6, /* Big or little endian */
+ BCJ_ARM = 7, /* Little endian only */
+ BCJ_ARMTHUMB = 8, /* Little endian only */
+ BCJ_SPARC = 9 /* Big or little endian */
+ } type;
+
+ /*
+ * Return value of the next filter in the chain. We need to preserve
+ * this information across calls, because we must not call the next
+ * filter anymore once it has returned XZ_STREAM_END.
+ */
+ enum xz_ret ret;
+
+ /* True if we are operating in single-call mode. */
+ bool single_call;
+
+ /*
+ * Absolute position relative to the beginning of the uncompressed
+ * data (in a single .xz Block). We care only about the lowest 32
+ * bits so this doesn't need to be uint64_t even with big files.
+ */
+ uint32_t pos;
+
+ /* x86 filter state */
+ uint32_t x86_prev_mask;
+
+ /* Temporary space to hold the variables from struct xz_buf */
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+
+ struct {
+ /* Amount of already filtered data in the beginning of buf */
+ size_t filtered;
+
+ /* Total amount of data currently stored in buf */
+ size_t size;
+
+ /*
+ * Buffer to hold a mix of filtered and unfiltered data. This
+ * needs to be big enough to hold Alignment + 2 * Look-ahead:
+ *
+ * Type Alignment Look-ahead
+ * x86 1 4
+ * PowerPC 4 0
+ * IA-64 16 0
+ * ARM 4 0
+ * ARM-Thumb 2 2
+ * SPARC 4 0
+ */
+ uint8_t buf[16];
+ } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+ return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const bool mask_to_allowed_status[8]
+ = { true, true, true, false, true, false, false, false };
+
+ static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+ size_t i;
+ size_t prev_pos = (size_t)-1;
+ uint32_t prev_mask = s->x86_prev_mask;
+ uint32_t src;
+ uint32_t dest;
+ uint32_t j;
+ uint8_t b;
+
+ if (size <= 4)
+ return 0;
+
+ size -= 4;
+ for (i = 0; i < size; ++i) {
+ if ((buf[i] & 0xFE) != 0xE8)
+ continue;
+
+ prev_pos = i - prev_pos;
+ if (prev_pos > 3) {
+ prev_mask = 0;
+ } else {
+ prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+ if (prev_mask != 0) {
+ b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+ if (!mask_to_allowed_status[prev_mask]
+ || bcj_x86_test_msbyte(b)) {
+ prev_pos = i;
+ prev_mask = (prev_mask << 1) | 1;
+ continue;
+ }
+ }
+ }
+
+ prev_pos = i;
+
+ if (bcj_x86_test_msbyte(buf[i + 4])) {
+ src = get_unaligned_le32(buf + i + 1);
+ while (true) {
+ dest = src - (s->pos + (uint32_t)i + 5);
+ if (prev_mask == 0)
+ break;
+
+ j = mask_to_bit_num[prev_mask] * 8;
+ b = (uint8_t)(dest >> (24 - j));
+ if (!bcj_x86_test_msbyte(b))
+ break;
+
+ src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+ }
+
+ dest &= 0x01FFFFFF;
+ dest |= (uint32_t)0 - (dest & 0x01000000);
+ put_unaligned_le32(dest, buf + i + 1);
+ i += 4;
+ } else {
+ prev_mask = (prev_mask << 1) | 1;
+ }
+ }
+
+ prev_pos = i - prev_pos;
+ s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr & 0xFC000003) == 0x48000001) {
+ instr &= 0x03FFFFFC;
+ instr -= s->pos + (uint32_t)i;
+ instr &= 0x03FFFFFC;
+ instr |= 0x48000001;
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const uint8_t branch_table[32] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 6, 6, 0, 0, 7, 7,
+ 4, 4, 0, 0, 4, 4, 0, 0
+ };
+
+ /*
+ * The local variables take a little bit stack space, but it's less
+ * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+ * stack usage here without doing that for the LZMA2 decoder too.
+ */
+
+ /* Loop counters */
+ size_t i;
+ size_t j;
+
+ /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+ uint32_t slot;
+
+ /* Bitwise offset of the instruction indicated by slot */
+ uint32_t bit_pos;
+
+ /* bit_pos split into byte and bit parts */
+ uint32_t byte_pos;
+ uint32_t bit_res;
+
+ /* Address part of an instruction */
+ uint32_t addr;
+
+ /* Mask used to detect which instructions to convert */
+ uint32_t mask;
+
+ /* 41-bit instruction stored somewhere in the lowest 48 bits */
+ uint64_t instr;
+
+ /* Instruction normalized with bit_res for easier manipulation */
+ uint64_t norm;
+
+ for (i = 0; i + 16 <= size; i += 16) {
+ mask = branch_table[buf[i] & 0x1F];
+ for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+ if (((mask >> slot) & 1) == 0)
+ continue;
+
+ byte_pos = bit_pos >> 3;
+ bit_res = bit_pos & 7;
+ instr = 0;
+ for (j = 0; j < 6; ++j)
+ instr |= (uint64_t)(buf[i + j + byte_pos])
+ << (8 * j);
+
+ norm = instr >> bit_res;
+
+ if (((norm >> 37) & 0x0F) == 0x05
+ && ((norm >> 9) & 0x07) == 0) {
+ addr = (norm >> 13) & 0x0FFFFF;
+ addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+ addr <<= 4;
+ addr -= s->pos + (uint32_t)i;
+ addr >>= 4;
+
+ norm &= ~((uint64_t)0x8FFFFF << 13);
+ norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+ norm |= (uint64_t)(addr & 0x100000)
+ << (36 - 20);
+
+ instr &= (1 << bit_res) - 1;
+ instr |= norm << bit_res;
+
+ for (j = 0; j < 6; j++)
+ buf[i + j + byte_pos]
+ = (uint8_t)(instr >> (8 * j));
+ }
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xEB) {
+ addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+ | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= s->pos + (uint32_t)i + 8;
+ addr >>= 2;
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 2) {
+ if ((buf[i + 1] & 0xF8) == 0xF0
+ && (buf[i + 3] & 0xF8) == 0xF8) {
+ addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+ | ((uint32_t)buf[i] << 11)
+ | (((uint32_t)buf[i + 3] & 0x07) << 8)
+ | (uint32_t)buf[i + 2];
+ addr <<= 1;
+ addr -= s->pos + (uint32_t)i + 4;
+ addr >>= 1;
+ buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+ buf[i] = (uint8_t)(addr >> 11);
+ buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+ buf[i + 2] = (uint8_t)addr;
+ i += 2;
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+ instr <<= 2;
+ instr -= s->pos + (uint32_t)i;
+ instr >>= 2;
+ instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+ | 0x40000000 | (instr & 0x3FFFFF);
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s,
+ uint8_t *buf, size_t *pos, size_t size)
+{
+ size_t filtered;
+
+ buf += *pos;
+ size -= *pos;
+
+ switch (s->type) {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+ filtered = bcj_x86(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+ filtered = bcj_powerpc(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+ filtered = bcj_ia64(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+ filtered = bcj_arm(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+ filtered = bcj_armthumb(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+ filtered = bcj_sparc(s, buf, size);
+ break;
+#endif
+ default:
+ /* Never reached but silence compiler warnings. */
+ filtered = 0;
+ break;
+ }
+
+ *pos += filtered;
+ s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+ size_t copy_size;
+
+ copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+ memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+ b->out_pos += copy_size;
+
+ s->temp.filtered -= copy_size;
+ s->temp.size -= copy_size;
+ memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+ struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b)
+{
+ size_t out_start;
+
+ /*
+ * Flush pending already filtered data to the output buffer. Return
+ * immediatelly if we couldn't flush everything, or if the next
+ * filter in the chain had already returned XZ_STREAM_END.
+ */
+ if (s->temp.filtered > 0) {
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+ }
+
+ /*
+ * If we have more output space than what is currently pending in
+ * temp, copy the unfiltered data from temp to the output buffer
+ * and try to fill the output buffer by decoding more data from the
+ * next filter in the chain. Apply the BCJ filter on the new data
+ * in the output buffer. If everything cannot be filtered, copy it
+ * to temp and rewind the output buffer position accordingly.
+ *
+ * This needs to be always run when temp.size == 0 to handle a special
+ * case where the output buffer is full and the next filter has no
+ * more output coming but hasn't returned XZ_STREAM_END yet.
+ */
+ if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
+ out_start = b->out_pos;
+ memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+ b->out_pos += s->temp.size;
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+ if (s->ret != XZ_STREAM_END
+ && (s->ret != XZ_OK || s->single_call))
+ return s->ret;
+
+ bcj_apply(s, b->out, &out_start, b->out_pos);
+
+ /*
+ * As an exception, if the next filter returned XZ_STREAM_END,
+ * we can do that too, since the last few bytes that remain
+ * unfiltered are meant to remain unfiltered.
+ */
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+
+ s->temp.size = b->out_pos - out_start;
+ b->out_pos -= s->temp.size;
+ memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+ /*
+ * If there wasn't enough input to the next filter to fill
+ * the output buffer with unfiltered data, there's no point
+ * to try decoding more data to temp.
+ */
+ if (b->out_pos + s->temp.size < b->out_size)
+ return XZ_OK;
+ }
+
+ /*
+ * We have unfiltered data in temp. If the output buffer isn't full
+ * yet, try to fill the temp buffer by decoding more data from the
+ * next filter. Apply the BCJ filter on temp. Then we hopefully can
+ * fill the actual output buffer by copying filtered data from temp.
+ * A mix of filtered and unfiltered data may be left in temp; it will
+ * be taken care on the next call to this function.
+ */
+ if (b->out_pos < b->out_size) {
+ /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+ s->out = b->out;
+ s->out_pos = b->out_pos;
+ s->out_size = b->out_size;
+ b->out = s->temp.buf;
+ b->out_pos = s->temp.size;
+ b->out_size = sizeof(s->temp.buf);
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+
+ s->temp.size = b->out_pos;
+ b->out = s->out;
+ b->out_pos = s->out_pos;
+ b->out_size = s->out_size;
+
+ if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+ return s->ret;
+
+ bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+ /*
+ * If the next filter returned XZ_STREAM_END, we mark that
+ * everything is filtered, since the last unfiltered bytes
+ * of the stream are meant to be left as is.
+ */
+ if (s->ret == XZ_STREAM_END)
+ s->temp.filtered = s->temp.size;
+
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+ }
+
+ return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+{
+ struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s != NULL)
+ s->single_call = single_call;
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+ switch (id) {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+#endif
+ break;
+
+ default:
+ /* Unsupported Filter ID */
+ return XZ_OPTIONS_ERROR;
+ }
+
+ s->type = id;
+ s->ret = XZ_OK;
+ s->pos = 0;
+ s->x86_prev_mask = 0;
+ s->temp.filtered = 0;
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+#endif
--- /dev/null
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ * start <= pos <= full <= end
+ * pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ * end == size
+ * size <= size_max
+ * allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+ /* Beginning of the history buffer */
+ uint8_t *buf;
+
+ /* Old position in buf (before decoding more data) */
+ size_t start;
+
+ /* Position in buf */
+ size_t pos;
+
+ /*
+ * How full dictionary is. This is used to detect corrupt input that
+ * would read beyond the beginning of the uncompressed stream.
+ */
+ size_t full;
+
+ /* Write limit; we don't write to buf[limit] or later bytes. */
+ size_t limit;
+
+ /*
+ * End of the dictionary buffer. In multi-call mode, this is
+ * the same as the dictionary size. In single-call mode, this
+ * indicates the size of the output buffer.
+ */
+ size_t end;
+
+ /*
+ * Size of the dictionary as specified in Block Header. This is used
+ * together with "full" to detect corrupt input that would make us
+ * read beyond the beginning of the uncompressed stream.
+ */
+ uint32_t size;
+
+ /*
+ * Maximum allowed dictionary size in multi-call mode.
+ * This is ignored in single-call mode.
+ */
+ uint32_t size_max;
+
+ /*
+ * Amount of memory currently allocated for the dictionary.
+ * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+ * size_max is always the same as the allocated size.)
+ */
+ uint32_t allocated;
+
+ /* Operation mode */
+ enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+ uint32_t range;
+ uint32_t code;
+
+ /*
+ * Number of initializing bytes remaining to be read
+ * by rc_read_init().
+ */
+ uint32_t init_bytes_left;
+
+ /*
+ * Buffer from which we read our input. It can be either
+ * temp.buf or the caller-provided input buffer.
+ */
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+ /* Probability of match length being at least 10 */
+ uint16_t choice;
+
+ /* Probability of match length being at least 18 */
+ uint16_t choice2;
+
+ /* Probabilities for match lengths 2-9 */
+ uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+ /* Probabilities for match lengths 10-17 */
+ uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+ /* Probabilities for match lengths 18-273 */
+ uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+ /* Distances of latest four matches */
+ uint32_t rep0;
+ uint32_t rep1;
+ uint32_t rep2;
+ uint32_t rep3;
+
+ /* Types of the most recently seen LZMA symbols */
+ enum lzma_state state;
+
+ /*
+ * Length of a match. This is updated so that dict_repeat can
+ * be called again to finish repeating the whole match.
+ */
+ uint32_t len;
+
+ /*
+ * LZMA properties or related bit masks (number of literal
+ * context bits, a mask dervied from the number of literal
+ * position bits, and a mask dervied from the number
+ * position bits)
+ */
+ uint32_t lc;
+ uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+ uint32_t pos_mask; /* (1 << pb) - 1 */
+
+ /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+ uint16_t is_match[STATES][POS_STATES_MAX];
+
+ /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+ uint16_t is_rep[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep0.
+ * Otherwise check is_rep1.
+ */
+ uint16_t is_rep0[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep1.
+ * Otherwise check is_rep2.
+ */
+ uint16_t is_rep1[STATES];
+
+ /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+ uint16_t is_rep2[STATES];
+
+ /*
+ * If 1, the repeated match has length of one byte. Otherwise
+ * the length is decoded from rep_len_decoder.
+ */
+ uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+ /*
+ * Probability tree for the highest two bits of the match
+ * distance. There is a separate probability tree for match
+ * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+ */
+ uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+ /*
+ * Probility trees for additional bits for match distance
+ * when the distance is in the range [4, 127].
+ */
+ uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+ /*
+ * Probability tree for the lowest four bits of a match
+ * distance that is equal to or greater than 128.
+ */
+ uint16_t dist_align[ALIGN_SIZE];
+
+ /* Length of a normal match */
+ struct lzma_len_dec match_len_dec;
+
+ /* Length of a repeated match */
+ struct lzma_len_dec rep_len_dec;
+
+ /* Probabilities of literals */
+ uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+ /* Position in xz_dec_lzma2_run(). */
+ enum lzma2_seq {
+ SEQ_CONTROL,
+ SEQ_UNCOMPRESSED_1,
+ SEQ_UNCOMPRESSED_2,
+ SEQ_COMPRESSED_0,
+ SEQ_COMPRESSED_1,
+ SEQ_PROPERTIES,
+ SEQ_LZMA_PREPARE,
+ SEQ_LZMA_RUN,
+ SEQ_COPY
+ } sequence;
+
+ /* Next position after decoding the compressed size of the chunk. */
+ enum lzma2_seq next_sequence;
+
+ /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+ uint32_t uncompressed;
+
+ /*
+ * Compressed size of LZMA chunk or compressed/uncompressed
+ * size of uncompressed chunk (64 KiB at maximum)
+ */
+ uint32_t compressed;
+
+ /*
+ * True if dictionary reset is needed. This is false before
+ * the first chunk (LZMA or uncompressed).
+ */
+ bool need_dict_reset;
+
+ /*
+ * True if new LZMA properties are needed. This is false
+ * before the first LZMA chunk.
+ */
+ bool need_props;
+};
+
+struct xz_dec_lzma2 {
+ /*
+ * The order below is important on x86 to reduce code size and
+ * it shouldn't hurt on other platforms. Everything up to and
+ * including lzma.pos_mask are in the first 128 bytes on x86-32,
+ * which allows using smaller instructions to access those
+ * variables. On x86-64, fewer variables fit into the first 128
+ * bytes, but this is still the best order without sacrificing
+ * the readability by splitting the structures.
+ */
+ struct rc_dec rc;
+ struct dictionary dict;
+ struct lzma2_dec lzma2;
+ struct lzma_dec lzma;
+
+ /*
+ * Temporary buffer which holds small number of input bytes between
+ * decoder calls. See lzma2_lzma() for details.
+ */
+ struct {
+ uint32_t size;
+ uint8_t buf[3 * LZMA_IN_REQUIRED];
+ } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+ if (DEC_IS_SINGLE(dict->mode)) {
+ dict->buf = b->out + b->out_pos;
+ dict->end = b->out_size - b->out_pos;
+ }
+
+ dict->start = 0;
+ dict->pos = 0;
+ dict->limit = 0;
+ dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+ if (dict->end - dict->pos <= out_max)
+ dict->limit = dict->end;
+ else
+ dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline bool dict_has_space(const struct dictionary *dict)
+{
+ return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+ size_t offset = dict->pos - dist - 1;
+
+ if (dist >= dict->pos)
+ offset += dict->end;
+
+ return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+ dict->buf[dict->pos++] = byte;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+ size_t back;
+ uint32_t left;
+
+ if (dist >= dict->full || dist >= dict->size)
+ return false;
+
+ left = min_t(size_t, dict->limit - dict->pos, *len);
+ *len -= left;
+
+ back = dict->pos - dist - 1;
+ if (dist >= dict->pos)
+ back += dict->end;
+
+ do {
+ dict->buf[dict->pos++] = dict->buf[back++];
+ if (back == dict->end)
+ back = 0;
+ } while (--left > 0);
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,
+ uint32_t *left)
+{
+ size_t copy_size;
+
+ while (*left > 0 && b->in_pos < b->in_size
+ && b->out_pos < b->out_size) {
+ copy_size = min(b->in_size - b->in_pos,
+ b->out_size - b->out_pos);
+ if (copy_size > dict->end - dict->pos)
+ copy_size = dict->end - dict->pos;
+ if (copy_size > *left)
+ copy_size = *left;
+
+ *left -= copy_size;
+
+ memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+ dict->pos += copy_size;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ if (DEC_IS_MULTI(dict->mode)) {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, b->in + b->in_pos,
+ copy_size);
+ }
+
+ dict->start = dict->pos;
+
+ b->out_pos += copy_size;
+ b->in_pos += copy_size;
+ }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+ size_t copy_size = dict->pos - dict->start;
+
+ if (DEC_IS_MULTI(dict->mode)) {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, dict->buf + dict->start,
+ copy_size);
+ }
+
+ dict->start = dict->pos;
+ b->out_pos += copy_size;
+ return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+ rc->range = (uint32_t)-1;
+ rc->code = 0;
+ rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+ while (rc->init_bytes_left > 0) {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ rc->code = (rc->code << 8) + b->in[b->in_pos++];
+ --rc->init_bytes_left;
+ }
+
+ return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool rc_limit_exceeded(const struct rc_dec *rc)
+{
+ return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool rc_is_finished(const struct rc_dec *rc)
+{
+ return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void rc_normalize(struct rc_dec *rc)
+{
+ if (rc->range < RC_TOP_VALUE) {
+ rc->range <<= RC_SHIFT_BITS;
+ rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+ }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+ uint32_t bound;
+ int bit;
+
+ rc_normalize(rc);
+ bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+ if (rc->code < bound) {
+ rc->range = bound;
+ *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+ bit = 0;
+ } else {
+ rc->range -= bound;
+ rc->code -= bound;
+ *prob -= *prob >> RC_MOVE_BITS;
+ bit = 1;
+ }
+
+ return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t rc_bittree(struct rc_dec *rc,
+ uint16_t *probs, uint32_t limit)
+{
+ uint32_t symbol = 1;
+
+ do {
+ if (rc_bit(rc, &probs[symbol]))
+ symbol = (symbol << 1) + 1;
+ else
+ symbol <<= 1;
+ } while (symbol < limit);
+
+ return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void rc_bittree_reverse(struct rc_dec *rc,
+ uint16_t *probs,
+ uint32_t *dest, uint32_t limit)
+{
+ uint32_t symbol = 1;
+ uint32_t i = 0;
+
+ do {
+ if (rc_bit(rc, &probs[symbol])) {
+ symbol = (symbol << 1) + 1;
+ *dest += 1 << i;
+ } else {
+ symbol <<= 1;
+ }
+ } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+ uint32_t mask;
+
+ do {
+ rc_normalize(rc);
+ rc->range >>= 1;
+ rc->code -= rc->range;
+ mask = (uint32_t)0 - (rc->code >> 31);
+ rc->code += rc->range & mask;
+ *dest = (*dest << 1) + (mask + 1);
+ } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+ uint32_t prev_byte = dict_get(&s->dict, 0);
+ uint32_t low = prev_byte >> (8 - s->lzma.lc);
+ uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+ return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ uint32_t symbol;
+ uint32_t match_byte;
+ uint32_t match_bit;
+ uint32_t offset;
+ uint32_t i;
+
+ probs = lzma_literal_probs(s);
+
+ if (lzma_state_is_literal(s->lzma.state)) {
+ symbol = rc_bittree(&s->rc, probs, 0x100);
+ } else {
+ symbol = 1;
+ match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+ offset = 0x100;
+
+ do {
+ match_bit = match_byte & offset;
+ match_byte <<= 1;
+ i = offset + match_bit + symbol;
+
+ if (rc_bit(&s->rc, &probs[i])) {
+ symbol = (symbol << 1) + 1;
+ offset &= match_bit;
+ } else {
+ symbol <<= 1;
+ offset &= ~match_bit;
+ }
+ } while (symbol < 0x100);
+ }
+
+ dict_put(&s->dict, (uint8_t)symbol);
+ lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+ uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t limit;
+
+ if (!rc_bit(&s->rc, &l->choice)) {
+ probs = l->low[pos_state];
+ limit = LEN_LOW_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN;
+ } else {
+ if (!rc_bit(&s->rc, &l->choice2)) {
+ probs = l->mid[pos_state];
+ limit = LEN_MID_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+ } else {
+ probs = l->high;
+ limit = LEN_HIGH_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+ + LEN_MID_SYMBOLS;
+ }
+ }
+
+ s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t dist_slot;
+ uint32_t limit;
+
+ lzma_state_match(&s->lzma.state);
+
+ s->lzma.rep3 = s->lzma.rep2;
+ s->lzma.rep2 = s->lzma.rep1;
+ s->lzma.rep1 = s->lzma.rep0;
+
+ lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+ probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+ dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+ if (dist_slot < DIST_MODEL_START) {
+ s->lzma.rep0 = dist_slot;
+ } else {
+ limit = (dist_slot >> 1) - 1;
+ s->lzma.rep0 = 2 + (dist_slot & 1);
+
+ if (dist_slot < DIST_MODEL_END) {
+ s->lzma.rep0 <<= limit;
+ probs = s->lzma.dist_special + s->lzma.rep0
+ - dist_slot - 1;
+ rc_bittree_reverse(&s->rc, probs,
+ &s->lzma.rep0, limit);
+ } else {
+ rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+ s->lzma.rep0 <<= ALIGN_BITS;
+ rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+ &s->lzma.rep0, ALIGN_BITS);
+ }
+ }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint32_t tmp;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+ s->lzma.state][pos_state])) {
+ lzma_state_short_rep(&s->lzma.state);
+ s->lzma.len = 1;
+ return;
+ }
+ } else {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+ tmp = s->lzma.rep1;
+ } else {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+ tmp = s->lzma.rep2;
+ } else {
+ tmp = s->lzma.rep3;
+ s->lzma.rep3 = s->lzma.rep2;
+ }
+
+ s->lzma.rep2 = s->lzma.rep1;
+ }
+
+ s->lzma.rep1 = s->lzma.rep0;
+ s->lzma.rep0 = tmp;
+ }
+
+ lzma_state_long_rep(&s->lzma.state);
+ lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool lzma_main(struct xz_dec_lzma2 *s)
+{
+ uint32_t pos_state;
+
+ /*
+ * If the dictionary was reached during the previous call, try to
+ * finish the possibly pending repeat in the dictionary.
+ */
+ if (dict_has_space(&s->dict) && s->lzma.len > 0)
+ dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+ /*
+ * Decode more LZMA symbols. One iteration may consume up to
+ * LZMA_IN_REQUIRED - 1 bytes.
+ */
+ while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+ pos_state = s->dict.pos & s->lzma.pos_mask;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_match[
+ s->lzma.state][pos_state])) {
+ lzma_literal(s);
+ } else {
+ if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+ lzma_rep_match(s, pos_state);
+ else
+ lzma_match(s, pos_state);
+
+ if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+ return false;
+ }
+ }
+
+ /*
+ * Having the range decoder always normalized when we are outside
+ * this function makes it easier to correctly handle end of the chunk.
+ */
+ rc_normalize(&s->rc);
+
+ return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ size_t i;
+
+ s->lzma.state = STATE_LIT_LIT;
+ s->lzma.rep0 = 0;
+ s->lzma.rep1 = 0;
+ s->lzma.rep2 = 0;
+ s->lzma.rep3 = 0;
+
+ /*
+ * All probabilities are initialized to the same value. This hack
+ * makes the code smaller by avoiding a separate loop for each
+ * probability array.
+ *
+ * This could be optimized so that only that part of literal
+ * probabilities that are actually required. In the common case
+ * we would write 12 KiB less.
+ */
+ probs = s->lzma.is_match[0];
+ for (i = 0; i < PROBS_TOTAL; ++i)
+ probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+ rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ if (props > (4 * 5 + 4) * 9 + 8)
+ return false;
+
+ s->lzma.pos_mask = 0;
+ while (props >= 9 * 5) {
+ props -= 9 * 5;
+ ++s->lzma.pos_mask;
+ }
+
+ s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+ s->lzma.literal_pos_mask = 0;
+ while (props >= 9) {
+ props -= 9;
+ ++s->lzma.literal_pos_mask;
+ }
+
+ s->lzma.lc = props;
+
+ if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+ return false;
+
+ s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+ lzma_reset(s);
+
+ return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+ size_t in_avail;
+ uint32_t tmp;
+
+ in_avail = b->in_size - b->in_pos;
+ if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+ tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+ if (tmp > s->lzma2.compressed - s->temp.size)
+ tmp = s->lzma2.compressed - s->temp.size;
+ if (tmp > in_avail)
+ tmp = in_avail;
+
+ memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+ if (s->temp.size + tmp == s->lzma2.compressed) {
+ memzero(s->temp.buf + s->temp.size + tmp,
+ sizeof(s->temp.buf)
+ - s->temp.size - tmp);
+ s->rc.in_limit = s->temp.size + tmp;
+ } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+ s->temp.size += tmp;
+ b->in_pos += tmp;
+ return true;
+ } else {
+ s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+ }
+
+ s->rc.in = s->temp.buf;
+ s->rc.in_pos = 0;
+
+ if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+ return false;
+
+ s->lzma2.compressed -= s->rc.in_pos;
+
+ if (s->rc.in_pos < s->temp.size) {
+ s->temp.size -= s->rc.in_pos;
+ memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+ s->temp.size);
+ return true;
+ }
+
+ b->in_pos += s->rc.in_pos - s->temp.size;
+ s->temp.size = 0;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail >= LZMA_IN_REQUIRED) {
+ s->rc.in = b->in;
+ s->rc.in_pos = b->in_pos;
+
+ if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+ s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+ else
+ s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+ if (!lzma_main(s))
+ return false;
+
+ in_avail = s->rc.in_pos - b->in_pos;
+ if (in_avail > s->lzma2.compressed)
+ return false;
+
+ s->lzma2.compressed -= in_avail;
+ b->in_pos = s->rc.in_pos;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail < LZMA_IN_REQUIRED) {
+ if (in_avail > s->lzma2.compressed)
+ in_avail = s->lzma2.compressed;
+
+ memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+ s->temp.size = in_avail;
+ b->in_pos += in_avail;
+ }
+
+ return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+ struct xz_buf *b)
+{
+ uint32_t tmp;
+
+ while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+ switch (s->lzma2.sequence) {
+ case SEQ_CONTROL:
+ /*
+ * LZMA2 control byte
+ *
+ * Exact values:
+ * 0x00 End marker
+ * 0x01 Dictionary reset followed by
+ * an uncompressed chunk
+ * 0x02 Uncompressed chunk (no dictionary reset)
+ *
+ * Highest three bits (s->control & 0xE0):
+ * 0xE0 Dictionary reset, new properties and state
+ * reset, followed by LZMA compressed chunk
+ * 0xC0 New properties and state reset, followed
+ * by LZMA compressed chunk (no dictionary
+ * reset)
+ * 0xA0 State reset using old properties,
+ * followed by LZMA compressed chunk (no
+ * dictionary reset)
+ * 0x80 LZMA chunk (no dictionary or state reset)
+ *
+ * For LZMA compressed chunks, the lowest five bits
+ * (s->control & 1F) are the highest bits of the
+ * uncompressed size (bits 16-20).
+ *
+ * A new LZMA2 stream must begin with a dictionary
+ * reset. The first LZMA chunk must set new
+ * properties and reset the LZMA state.
+ *
+ * Values that don't match anything described above
+ * are invalid and we return XZ_DATA_ERROR.
+ */
+ tmp = b->in[b->in_pos++];
+
+ if (tmp == 0x00)
+ return XZ_STREAM_END;
+
+ if (tmp >= 0xE0 || tmp == 0x01) {
+ s->lzma2.need_props = true;
+ s->lzma2.need_dict_reset = false;
+ dict_reset(&s->dict, b);
+ } else if (s->lzma2.need_dict_reset) {
+ return XZ_DATA_ERROR;
+ }
+
+ if (tmp >= 0x80) {
+ s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+ if (tmp >= 0xC0) {
+ /*
+ * When there are new properties,
+ * state reset is done at
+ * SEQ_PROPERTIES.
+ */
+ s->lzma2.need_props = false;
+ s->lzma2.next_sequence
+ = SEQ_PROPERTIES;
+
+ } else if (s->lzma2.need_props) {
+ return XZ_DATA_ERROR;
+
+ } else {
+ s->lzma2.next_sequence
+ = SEQ_LZMA_PREPARE;
+ if (tmp >= 0xA0)
+ lzma_reset(s);
+ }
+ } else {
+ if (tmp > 0x02)
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ s->lzma2.next_sequence = SEQ_COPY;
+ }
+
+ break;
+
+ case SEQ_UNCOMPRESSED_1:
+ s->lzma2.uncompressed
+ += (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+ break;
+
+ case SEQ_UNCOMPRESSED_2:
+ s->lzma2.uncompressed
+ += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ break;
+
+ case SEQ_COMPRESSED_0:
+ s->lzma2.compressed
+ = (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_COMPRESSED_1;
+ break;
+
+ case SEQ_COMPRESSED_1:
+ s->lzma2.compressed
+ += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = s->lzma2.next_sequence;
+ break;
+
+ case SEQ_PROPERTIES:
+ if (!lzma_props(s, b->in[b->in_pos++]))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+ case SEQ_LZMA_PREPARE:
+ if (s->lzma2.compressed < RC_INIT_BYTES)
+ return XZ_DATA_ERROR;
+
+ if (!rc_read_init(&s->rc, b))
+ return XZ_OK;
+
+ s->lzma2.compressed -= RC_INIT_BYTES;
+ s->lzma2.sequence = SEQ_LZMA_RUN;
+
+ case SEQ_LZMA_RUN:
+ /*
+ * Set dictionary limit to indicate how much we want
+ * to be encoded at maximum. Decode new data into the
+ * dictionary. Flush the new data from dictionary to
+ * b->out. Check if we finished decoding this chunk.
+ * In case the dictionary got full but we didn't fill
+ * the output buffer yet, we may run this loop
+ * multiple times without changing s->lzma2.sequence.
+ */
+ dict_limit(&s->dict, min_t(size_t,
+ b->out_size - b->out_pos,
+ s->lzma2.uncompressed));
+ if (!lzma2_lzma(s, b))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+ if (s->lzma2.uncompressed == 0) {
+ if (s->lzma2.compressed > 0 || s->lzma.len > 0
+ || !rc_is_finished(&s->rc))
+ return XZ_DATA_ERROR;
+
+ rc_reset(&s->rc);
+ s->lzma2.sequence = SEQ_CONTROL;
+
+ } else if (b->out_pos == b->out_size
+ || (b->in_pos == b->in_size
+ && s->temp.size
+ < s->lzma2.compressed)) {
+ return XZ_OK;
+ }
+
+ break;
+
+ case SEQ_COPY:
+ dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+ if (s->lzma2.compressed > 0)
+ return XZ_OK;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ break;
+ }
+ }
+
+ return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+ uint32_t dict_max)
+{
+ struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->dict.mode = mode;
+ s->dict.size_max = dict_max;
+
+ if (DEC_IS_PREALLOC(mode)) {
+ s->dict.buf = vmalloc(dict_max);
+ if (s->dict.buf == NULL) {
+ kfree(s);
+ return NULL;
+ }
+ } else if (DEC_IS_DYNALLOC(mode)) {
+ s->dict.buf = NULL;
+ s->dict.allocated = 0;
+ }
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+ if (props > 39)
+ return XZ_OPTIONS_ERROR;
+
+ s->dict.size = 2 + (props & 1);
+ s->dict.size <<= (props >> 1) + 11;
+
+ if (DEC_IS_MULTI(s->dict.mode)) {
+ if (s->dict.size > s->dict.size_max)
+ return XZ_MEMLIMIT_ERROR;
+
+ s->dict.end = s->dict.size;
+
+ if (DEC_IS_DYNALLOC(s->dict.mode)) {
+ if (s->dict.allocated < s->dict.size) {
+ vfree(s->dict.buf);
+ s->dict.buf = vmalloc(s->dict.size);
+ if (s->dict.buf == NULL) {
+ s->dict.allocated = 0;
+ return XZ_MEM_ERROR;
+ }
+ }
+ }
+ }
+
+ s->lzma.len = 0;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ s->lzma2.need_dict_reset = true;
+
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+ if (DEC_IS_MULTI(s->dict.mode))
+ vfree(s->dict.buf);
+
+ kfree(s);
+}
--- /dev/null
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+#ifdef XZ_USE_CRC64
+# define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
+#else
+# define IS_CRC64(check_type) false
+#endif
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+ vli_type unpadded;
+ vli_type uncompressed;
+ uint32_t crc32;
+};
+
+struct xz_dec {
+ /* Position in dec_main() */
+ enum {
+ SEQ_STREAM_HEADER,
+ SEQ_BLOCK_START,
+ SEQ_BLOCK_HEADER,
+ SEQ_BLOCK_UNCOMPRESS,
+ SEQ_BLOCK_PADDING,
+ SEQ_BLOCK_CHECK,
+ SEQ_INDEX,
+ SEQ_INDEX_PADDING,
+ SEQ_INDEX_CRC32,
+ SEQ_STREAM_FOOTER
+ } sequence;
+
+ /* Position in variable-length integers and Check fields */
+ uint32_t pos;
+
+ /* Variable-length integer decoded by dec_vli() */
+ vli_type vli;
+
+ /* Saved in_pos and out_pos */
+ size_t in_start;
+ size_t out_start;
+
+#ifdef XZ_USE_CRC64
+ /* CRC32 or CRC64 value in Block or CRC32 value in Index */
+ uint64_t crc;
+#else
+ /* CRC32 value in Block or Index */
+ uint32_t crc;
+#endif
+
+ /* Type of the integrity check calculated from uncompressed data */
+ enum xz_check check_type;
+
+ /* Operation mode */
+ enum xz_mode mode;
+
+ /*
+ * True if the next call to xz_dec_run() is allowed to return
+ * XZ_BUF_ERROR.
+ */
+ bool allow_buf_error;
+
+ /* Information stored in Block Header */
+ struct {
+ /*
+ * Value stored in the Compressed Size field, or
+ * VLI_UNKNOWN if Compressed Size is not present.
+ */
+ vli_type compressed;
+
+ /*
+ * Value stored in the Uncompressed Size field, or
+ * VLI_UNKNOWN if Uncompressed Size is not present.
+ */
+ vli_type uncompressed;
+
+ /* Size of the Block Header field */
+ uint32_t size;
+ } block_header;
+
+ /* Information collected when decoding Blocks */
+ struct {
+ /* Observed compressed size of the current Block */
+ vli_type compressed;
+
+ /* Observed uncompressed size of the current Block */
+ vli_type uncompressed;
+
+ /* Number of Blocks decoded so far */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Block sizes. This is used to
+ * validate the Index field.
+ */
+ struct xz_dec_hash hash;
+ } block;
+
+ /* Variables needed when verifying the Index field */
+ struct {
+ /* Position in dec_index() */
+ enum {
+ SEQ_INDEX_COUNT,
+ SEQ_INDEX_UNPADDED,
+ SEQ_INDEX_UNCOMPRESSED
+ } sequence;
+
+ /* Size of the Index in bytes */
+ vli_type size;
+
+ /* Number of Records (matches block.count in valid files) */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Records (matches block.hash in
+ * valid files).
+ */
+ struct xz_dec_hash hash;
+ } index;
+
+ /*
+ * Temporary buffer needed to hold Stream Header, Block Header,
+ * and Stream Footer. The Block Header is the biggest (1 KiB)
+ * so we reserve space according to that. buf[] has to be aligned
+ * to a multiple of four bytes; the size_t variables before it
+ * should guarantee this.
+ */
+ struct {
+ size_t pos;
+ size_t size;
+ uint8_t buf[1024];
+ } temp;
+
+ struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+ struct xz_dec_bcj *bcj;
+ bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+ 0,
+ 4, 4, 4,
+ 8, 8, 8,
+ 16, 16, 16,
+ 32, 32, 32,
+ 64, 64, 64
+};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t copy_size = min_t(size_t,
+ b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+ memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+ b->in_pos += copy_size;
+ s->temp.pos += copy_size;
+
+ if (s->temp.pos == s->temp.size) {
+ s->temp.pos = 0;
+ return true;
+ }
+
+ return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
+ size_t *in_pos, size_t in_size)
+{
+ uint8_t byte;
+
+ if (s->pos == 0)
+ s->vli = 0;
+
+ while (*in_pos < in_size) {
+ byte = in[*in_pos];
+ ++*in_pos;
+
+ s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+ if ((byte & 0x80) == 0) {
+ /* Don't allow non-minimal encodings. */
+ if (byte == 0 && s->pos != 0)
+ return XZ_DATA_ERROR;
+
+ s->pos = 0;
+ return XZ_STREAM_END;
+ }
+
+ s->pos += 7;
+ if (s->pos == 7 * VLI_BYTES_MAX)
+ return XZ_DATA_ERROR;
+ }
+
+ return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 or CRC64 value if presence of
+ * the CRC32 or CRC64 field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ s->in_start = b->in_pos;
+ s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+ if (s->bcj_active)
+ ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+ else
+#endif
+ ret = xz_dec_lzma2_run(s->lzma2, b);
+
+ s->block.compressed += b->in_pos - s->in_start;
+ s->block.uncompressed += b->out_pos - s->out_start;
+
+ /*
+ * There is no need to separately check for VLI_UNKNOWN, since
+ * the observed sizes are always smaller than VLI_UNKNOWN.
+ */
+ if (s->block.compressed > s->block_header.compressed
+ || s->block.uncompressed
+ > s->block_header.uncompressed)
+ return XZ_DATA_ERROR;
+
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->crc = xz_crc32(b->out + s->out_start,
+ b->out_pos - s->out_start, s->crc);
+#ifdef XZ_USE_CRC64
+ else if (s->check_type == XZ_CHECK_CRC64)
+ s->crc = xz_crc64(b->out + s->out_start,
+ b->out_pos - s->out_start, s->crc);
+#endif
+
+ if (ret == XZ_STREAM_END) {
+ if (s->block_header.compressed != VLI_UNKNOWN
+ && s->block_header.compressed
+ != s->block.compressed)
+ return XZ_DATA_ERROR;
+
+ if (s->block_header.uncompressed != VLI_UNKNOWN
+ && s->block_header.uncompressed
+ != s->block.uncompressed)
+ return XZ_DATA_ERROR;
+
+ s->block.hash.unpadded += s->block_header.size
+ + s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+ s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->block.hash.unpadded += 4;
+ else if (IS_CRC64(s->check_type))
+ s->block.hash.unpadded += 8;
+#endif
+
+ s->block.hash.uncompressed += s->block.uncompressed;
+ s->block.hash.crc32 = xz_crc32(
+ (const uint8_t *)&s->block.hash,
+ sizeof(s->block.hash), s->block.hash.crc32);
+
+ ++s->block.count;
+ }
+
+ return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+ size_t in_used = b->in_pos - s->in_start;
+ s->index.size += in_used;
+ s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ do {
+ ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+ if (ret != XZ_STREAM_END) {
+ index_update(s, b);
+ return ret;
+ }
+
+ switch (s->index.sequence) {
+ case SEQ_INDEX_COUNT:
+ s->index.count = s->vli;
+
+ /*
+ * Validate that the Number of Records field
+ * indicates the same number of Records as
+ * there were Blocks in the Stream.
+ */
+ if (s->index.count != s->block.count)
+ return XZ_DATA_ERROR;
+
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+
+ case SEQ_INDEX_UNPADDED:
+ s->index.hash.unpadded += s->vli;
+ s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+ break;
+
+ case SEQ_INDEX_UNCOMPRESSED:
+ s->index.hash.uncompressed += s->vli;
+ s->index.hash.crc32 = xz_crc32(
+ (const uint8_t *)&s->index.hash,
+ sizeof(s->index.hash),
+ s->index.hash.crc32);
+ --s->index.count;
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+ }
+ } while (s->index.count > 0);
+
+ return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four or eight input bytes match the value
+ * of s->crc. s->pos must be zero when starting to validate the first byte.
+ * The "bits" argument allows using the same code for both CRC32 and CRC64.
+ */
+static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
+ uint32_t bits)
+{
+ do {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
+ return XZ_DATA_ERROR;
+
+ s->pos += 8;
+
+ } while (s->pos < bits);
+
+ s->crc = 0;
+ s->pos = 0;
+
+ return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+ while (s->pos < check_sizes[s->check_type]) {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ ++b->in_pos;
+ ++s->pos;
+ }
+
+ s->pos = 0;
+
+ return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+ return XZ_FORMAT_ERROR;
+
+ if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+ != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+ return XZ_OPTIONS_ERROR;
+
+ /*
+ * Of integrity checks, we support none (Check ID = 0),
+ * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
+ * However, if XZ_DEC_ANY_CHECK is defined, we will accept other
+ * check types too, but then the check won't be verified and
+ * a warning (XZ_UNSUPPORTED_CHECK) will be given.
+ */
+ s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (s->check_type > XZ_CHECK_MAX)
+ return XZ_OPTIONS_ERROR;
+
+ if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+ return XZ_UNSUPPORTED_CHECK;
+#else
+ if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+ return XZ_OPTIONS_ERROR;
+#endif
+
+ return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+ return XZ_DATA_ERROR;
+
+ if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+ return XZ_DATA_ERROR;
+
+ /*
+ * Validate Backward Size. Note that we never added the size of the
+ * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+ * instead of s->index.size / 4 - 1.
+ */
+ if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+ return XZ_DATA_ERROR;
+
+ /*
+ * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+ * for the caller.
+ */
+ return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+ enum xz_ret ret;
+
+ /*
+ * Validate the CRC32. We know that the temp buffer is at least
+ * eight bytes so this is safe.
+ */
+ s->temp.size -= 4;
+ if (xz_crc32(s->temp.buf, s->temp.size, 0)
+ != get_le32(s->temp.buf + s->temp.size))
+ return XZ_DATA_ERROR;
+
+ s->temp.pos = 2;
+
+ /*
+ * Catch unsupported Block Flags. We support only one or two filters
+ * in the chain, so we catch that with the same test.
+ */
+#ifdef XZ_DEC_BCJ
+ if (s->temp.buf[1] & 0x3E)
+#else
+ if (s->temp.buf[1] & 0x3F)
+#endif
+ return XZ_OPTIONS_ERROR;
+
+ /* Compressed Size */
+ if (s->temp.buf[1] & 0x40) {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+ != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.compressed = s->vli;
+ } else {
+ s->block_header.compressed = VLI_UNKNOWN;
+ }
+
+ /* Uncompressed Size */
+ if (s->temp.buf[1] & 0x80) {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+ != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.uncompressed = s->vli;
+ } else {
+ s->block_header.uncompressed = VLI_UNKNOWN;
+ }
+
+#ifdef XZ_DEC_BCJ
+ /* If there are two filters, the first one must be a BCJ filter. */
+ s->bcj_active = s->temp.buf[1] & 0x01;
+ if (s->bcj_active) {
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_OPTIONS_ERROR;
+
+ ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /*
+ * We don't support custom start offset,
+ * so Size of Properties must be zero.
+ */
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+ }
+#endif
+
+ /* Valid Filter Flags always take at least two bytes. */
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_DATA_ERROR;
+
+ /* Filter ID = LZMA2 */
+ if (s->temp.buf[s->temp.pos++] != 0x21)
+ return XZ_OPTIONS_ERROR;
+
+ /* Size of Properties = 1-byte Filter Properties */
+ if (s->temp.buf[s->temp.pos++] != 0x01)
+ return XZ_OPTIONS_ERROR;
+
+ /* Filter Properties contains LZMA2 dictionary size. */
+ if (s->temp.size - s->temp.pos < 1)
+ return XZ_DATA_ERROR;
+
+ ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /* The rest must be Header Padding. */
+ while (s->temp.pos < s->temp.size)
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+
+ s->temp.pos = 0;
+ s->block.compressed = 0;
+ s->block.uncompressed = 0;
+
+ return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ /*
+ * Store the start position for the case when we are in the middle
+ * of the Index field.
+ */
+ s->in_start = b->in_pos;
+
+ while (true) {
+ switch (s->sequence) {
+ case SEQ_STREAM_HEADER:
+ /*
+ * Stream Header is copied to s->temp, and then
+ * decoded from there. This way if the caller
+ * gives us only little input at a time, we can
+ * still keep the Stream Header decoding code
+ * simple. Similar approach is used in many places
+ * in this file.
+ */
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ /*
+ * If dec_stream_header() returns
+ * XZ_UNSUPPORTED_CHECK, it is still possible
+ * to continue decoding if working in multi-call
+ * mode. Thus, update s->sequence before calling
+ * dec_stream_header().
+ */
+ s->sequence = SEQ_BLOCK_START;
+
+ ret = dec_stream_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ case SEQ_BLOCK_START:
+ /* We need one byte of input to continue. */
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ /* See if this is the beginning of the Index field. */
+ if (b->in[b->in_pos] == 0) {
+ s->in_start = b->in_pos++;
+ s->sequence = SEQ_INDEX;
+ break;
+ }
+
+ /*
+ * Calculate the size of the Block Header and
+ * prepare to decode it.
+ */
+ s->block_header.size
+ = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+ s->temp.size = s->block_header.size;
+ s->temp.pos = 0;
+ s->sequence = SEQ_BLOCK_HEADER;
+
+ case SEQ_BLOCK_HEADER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ ret = dec_block_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+ case SEQ_BLOCK_UNCOMPRESS:
+ ret = dec_block(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_PADDING;
+
+ case SEQ_BLOCK_PADDING:
+ /*
+ * Size of Compressed Data + Block Padding
+ * must be a multiple of four. We don't need
+ * s->block.compressed for anything else
+ * anymore, so we use it here to test the size
+ * of the Block Padding field.
+ */
+ while (s->block.compressed & 3) {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+
+ ++s->block.compressed;
+ }
+
+ s->sequence = SEQ_BLOCK_CHECK;
+
+ case SEQ_BLOCK_CHECK:
+ if (s->check_type == XZ_CHECK_CRC32) {
+ ret = crc_validate(s, b, 32);
+ if (ret != XZ_STREAM_END)
+ return ret;
+ }
+ else if (IS_CRC64(s->check_type)) {
+ ret = crc_validate(s, b, 64);
+ if (ret != XZ_STREAM_END)
+ return ret;
+ }
+#ifdef XZ_DEC_ANY_CHECK
+ else if (!check_skip(s, b)) {
+ return XZ_OK;
+ }
+#endif
+
+ s->sequence = SEQ_BLOCK_START;
+ break;
+
+ case SEQ_INDEX:
+ ret = dec_index(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_INDEX_PADDING;
+
+ case SEQ_INDEX_PADDING:
+ while ((s->index.size + (b->in_pos - s->in_start))
+ & 3) {
+ if (b->in_pos == b->in_size) {
+ index_update(s, b);
+ return XZ_OK;
+ }
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+ }
+
+ /* Finish the CRC32 value and Index size. */
+ index_update(s, b);
+
+ /* Compare the hashes to validate the Index field. */
+ if (!memeq(&s->block.hash, &s->index.hash,
+ sizeof(s->block.hash)))
+ return XZ_DATA_ERROR;
+
+ s->sequence = SEQ_INDEX_CRC32;
+
+ case SEQ_INDEX_CRC32:
+ ret = crc_validate(s, b, 32);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->temp.size = STREAM_HEADER_SIZE;
+ s->sequence = SEQ_STREAM_FOOTER;
+
+ case SEQ_STREAM_FOOTER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ return dec_stream_footer(s);
+ }
+ }
+
+ /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t in_start;
+ size_t out_start;
+ enum xz_ret ret;
+
+ if (DEC_IS_SINGLE(s->mode))
+ xz_dec_reset(s);
+
+ in_start = b->in_pos;
+ out_start = b->out_pos;
+ ret = dec_main(s, b);
+
+ if (DEC_IS_SINGLE(s->mode)) {
+ if (ret == XZ_OK)
+ ret = b->in_pos == b->in_size
+ ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+ if (ret != XZ_STREAM_END) {
+ b->in_pos = in_start;
+ b->out_pos = out_start;
+ }
+
+ } else if (ret == XZ_OK && in_start == b->in_pos
+ && out_start == b->out_pos) {
+ if (s->allow_buf_error)
+ ret = XZ_BUF_ERROR;
+
+ s->allow_buf_error = true;
+ } else {
+ s->allow_buf_error = false;
+ }
+
+ return ret;
+}
+
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+ struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+ s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+ if (s->bcj == NULL)
+ goto error_bcj;
+#endif
+
+ s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+ if (s->lzma2 == NULL)
+ goto error_lzma2;
+
+ xz_dec_reset(s);
+ return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+ kfree(s);
+ return NULL;
+}
+
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+{
+ s->sequence = SEQ_STREAM_HEADER;
+ s->allow_buf_error = false;
+ s->pos = 0;
+ s->crc = 0;
+ memzero(&s->block, sizeof(s->block));
+ memzero(&s->index, sizeof(s->index));
+ s->temp.pos = 0;
+ s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+{
+ if (s != NULL) {
+ xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+#endif
+ kfree(s);
+ }
+}
--- /dev/null
+/*
+ * XZ decoder module information
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/module.h>
+#include <linux/xz.h>
+
+EXPORT_SYMBOL(xz_dec_init);
+EXPORT_SYMBOL(xz_dec_reset);
+EXPORT_SYMBOL(xz_dec_run);
+EXPORT_SYMBOL(xz_dec_end);
+
+MODULE_DESCRIPTION("XZ decompressor");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org> and Igor Pavlov");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * XZ decoder tester
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/xz.h>
+
+/* Maximum supported dictionary size */
+#define DICT_MAX (1 << 20)
+
+/* Device name to pass to register_chrdev(). */
+#define DEVICE_NAME "xz_dec_test"
+
+/* Dynamically allocated device major number */
+static int device_major;
+
+/*
+ * We reuse the same decoder state, and thus can decode only one
+ * file at a time.
+ */
+static bool device_is_open;
+
+/* XZ decoder state */
+static struct xz_dec *state;
+
+/*
+ * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
+ * it has returned XZ_STREAM_END, so we make this static.
+ */
+static enum xz_ret ret;
+
+/*
+ * Input and output buffers. The input buffer is used as a temporary safe
+ * place for the data coming from the userspace.
+ */
+static uint8_t buffer_in[1024];
+static uint8_t buffer_out[1024];
+
+/*
+ * Structure to pass the input and output buffers to the XZ decoder.
+ * A few of the fields are never modified so we initialize them here.
+ */
+static struct xz_buf buffers = {
+ .in = buffer_in,
+ .out = buffer_out,
+ .out_size = sizeof(buffer_out)
+};
+
+/*
+ * CRC32 of uncompressed data. This is used to give the user a simple way
+ * to check that the decoder produces correct output.
+ */
+static uint32_t crc;
+
+static int xz_dec_test_open(struct inode *i, struct file *f)
+{
+ if (device_is_open)
+ return -EBUSY;
+
+ device_is_open = true;
+
+ xz_dec_reset(state);
+ ret = XZ_OK;
+ crc = 0xFFFFFFFF;
+
+ buffers.in_pos = 0;
+ buffers.in_size = 0;
+ buffers.out_pos = 0;
+
+ printk(KERN_INFO DEVICE_NAME ": opened\n");
+ return 0;
+}
+
+static int xz_dec_test_release(struct inode *i, struct file *f)
+{
+ device_is_open = false;
+
+ if (ret == XZ_OK)
+ printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
+
+ printk(KERN_INFO DEVICE_NAME ": closed\n");
+ return 0;
+}
+
+/*
+ * Decode the data given to us from the userspace. CRC32 of the uncompressed
+ * data is calculated and is printed at the end of successful decoding. The
+ * uncompressed data isn't stored anywhere for further use.
+ *
+ * The .xz file must have exactly one Stream and no Stream Padding. The data
+ * after the first Stream is considered to be garbage.
+ */
+static ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ size_t remaining;
+
+ if (ret != XZ_OK) {
+ if (size > 0)
+ printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
+ "garbage at the end of the file\n",
+ size);
+
+ return -ENOSPC;
+ }
+
+ printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
+ size);
+
+ remaining = size;
+ while ((remaining > 0 || buffers.out_pos == buffers.out_size)
+ && ret == XZ_OK) {
+ if (buffers.in_pos == buffers.in_size) {
+ buffers.in_pos = 0;
+ buffers.in_size = min(remaining, sizeof(buffer_in));
+ if (copy_from_user(buffer_in, buf, buffers.in_size))
+ return -EFAULT;
+
+ buf += buffers.in_size;
+ remaining -= buffers.in_size;
+ }
+
+ buffers.out_pos = 0;
+ ret = xz_dec_run(state, &buffers);
+ crc = crc32(crc, buffer_out, buffers.out_pos);
+ }
+
+ switch (ret) {
+ case XZ_OK:
+ printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
+ return size;
+
+ case XZ_STREAM_END:
+ printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
+ "CRC32 = 0x%08X\n", ~crc);
+ return size - remaining - (buffers.in_size - buffers.in_pos);
+
+ case XZ_MEMLIMIT_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
+ break;
+
+ case XZ_FORMAT_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
+ break;
+
+ case XZ_OPTIONS_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
+ break;
+
+ case XZ_DATA_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
+ break;
+
+ case XZ_BUF_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
+ break;
+
+ default:
+ printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
+ break;
+ }
+
+ return -EIO;
+}
+
+/* Allocate the XZ decoder state and register the character device. */
+static int __init xz_dec_test_init(void)
+{
+ static const struct file_operations fileops = {
+ .owner = THIS_MODULE,
+ .open = &xz_dec_test_open,
+ .release = &xz_dec_test_release,
+ .write = &xz_dec_test_write
+ };
+
+ state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
+ if (state == NULL)
+ return -ENOMEM;
+
+ device_major = register_chrdev(0, DEVICE_NAME, &fileops);
+ if (device_major < 0) {
+ xz_dec_end(state);
+ return device_major;
+ }
+
+ printk(KERN_INFO DEVICE_NAME ": module loaded\n");
+ printk(KERN_INFO DEVICE_NAME ": Create a device node with "
+ "'mknod " DEVICE_NAME " c %d 0' and write .xz files "
+ "to it.\n", device_major);
+ return 0;
+}
+
+static void __exit xz_dec_test_exit(void)
+{
+ unregister_chrdev(device_major, DEVICE_NAME);
+ xz_dec_end(state);
+ printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
+}
+
+module_init(xz_dec_test_init);
+module_exit(xz_dec_test_exit);
+
+MODULE_DESCRIPTION("XZ decompressor tester");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ * - Literal: One 8-bit byte
+ * - Match: Repeat a chunk of data at some distance
+ * - Long repeat: Multi-byte match at a recently seen distance
+ * - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+ STATE_LIT_LIT,
+ STATE_MATCH_LIT_LIT,
+ STATE_REP_LIT_LIT,
+ STATE_SHORTREP_LIT_LIT,
+ STATE_MATCH_LIT,
+ STATE_REP_LIT,
+ STATE_SHORTREP_LIT,
+ STATE_LIT_MATCH,
+ STATE_LIT_LONGREP,
+ STATE_LIT_SHORTREP,
+ STATE_NONLIT_MATCH,
+ STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+ if (*state <= STATE_SHORTREP_LIT_LIT)
+ *state = STATE_LIT_LIT;
+ else if (*state <= STATE_LIT_SHORTREP)
+ *state -= 3;
+ else
+ *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool lzma_state_is_literal(enum lzma_state state)
+{
+ return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ * - 0x001-0x0FF: Without match byte
+ * - 0x101-0x1FF: With match byte; match bit is 0
+ * - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length Bits
+ * 2-9 4 = Choice=0 + 3 bits
+ * 10-17 5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273 10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+ return len < DIST_STATES + MATCH_LEN_MIN
+ ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ * - distance slot: the highest two bits
+ * - direct bits: 2-26 bits below the highest two bits
+ * - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
--- /dev/null
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+# include <linux/xz.h>
+# include <linux/kernel.h>
+# include <asm/unaligned.h>
+ /* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+# ifndef XZ_PREBOOT
+# include <linux/slab.h>
+# include <linux/vmalloc.h>
+# include <linux/string.h>
+# ifdef CONFIG_XZ_DEC_X86
+# define XZ_DEC_X86
+# endif
+# ifdef CONFIG_XZ_DEC_POWERPC
+# define XZ_DEC_POWERPC
+# endif
+# ifdef CONFIG_XZ_DEC_IA64
+# define XZ_DEC_IA64
+# endif
+# ifdef CONFIG_XZ_DEC_ARM
+# define XZ_DEC_ARM
+# endif
+# ifdef CONFIG_XZ_DEC_ARMTHUMB
+# define XZ_DEC_ARMTHUMB
+# endif
+# ifdef CONFIG_XZ_DEC_SPARC
+# define XZ_DEC_SPARC
+# endif
+# define memeq(a, b, size) (memcmp(a, b, size) == 0)
+# define memzero(buf, size) memset(buf, 0, size)
+# endif
+# define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+#else
+ /*
+ * For userspace builds, use a separate header to define the required
+ * macros and functions. This makes it easier to adapt the code into
+ * different environments and avoids clutter in the Linux kernel tree.
+ */
+# include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
+ && !defined(XZ_DEC_DYNALLOC)
+# define XZ_DEC_SINGLE
+# define XZ_DEC_PREALLOC
+# define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+# define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+# define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+# define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+# define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+# define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+ || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+ || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+ || defined(XZ_DEC_SPARC)
+# define XZ_DEC_BCJ
+# endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+ uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
+ uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+ struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+ struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
--- /dev/null
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+# include <linux/crc32.h>
+# undef crc32
+# define xz_crc32(buf, size, crc) \
+ (~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+ XZ_CHECK_NONE = 0,
+ XZ_CHECK_CRC32 = 1,
+ XZ_CHECK_CRC64 = 4,
+ XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
--- /dev/null
+#!/bin/sh
+#
+# This is a wrapper for xz to compress the kernel image using appropriate
+# compression options depending on the architecture.
+#
+# Author: Lasse Collin <lasse.collin@tukaani.org>
+#
+# This file has been put into the public domain.
+# You can do whatever you want with this file.
+#
+
+BCJ=
+LZMA2OPTS=
+
+case $SRCARCH in
+ x86) BCJ=--x86 ;;
+ powerpc) BCJ=--powerpc ;;
+ ia64) BCJ=--ia64; LZMA2OPTS=pb=4 ;;
+ arm) BCJ=--arm ;;
+ sparc) BCJ=--sparc ;;
+esac
+
+exec xz --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
--- /dev/null
+/*
+ * Test application for xz_boot.c
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define STATIC static
+#define INIT
+
+static void error(/*const*/ char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+}
+
+/* Disable the CRC64 support even if it was enabled in the Makefile. */
+#undef XZ_USE_CRC64
+
+#include "../linux/lib/decompress_unxz.c"
+
+static uint8_t in[1024 * 1024];
+static uint8_t out[1024 * 1024];
+
+static int fill(void *buf, unsigned int size)
+{
+ return fread(buf, 1, size, stdin);
+}
+
+static int flush(/*const*/ void *buf, unsigned int size)
+{
+ return fwrite(buf, 1, size, stdout);
+}
+
+static void test_buf_to_buf(void)
+{
+ size_t in_size;
+ int ret;
+ in_size = fread(in, 1, sizeof(in), stdin);
+ ret = decompress(in, in_size, NULL, NULL, out, NULL, &error);
+ /* fwrite(out, 1, FIXME, stdout); */
+ fprintf(stderr, "ret = %d\n", ret);
+}
+
+static void test_buf_to_cb(void)
+{
+ size_t in_size;
+ int in_used;
+ int ret;
+ in_size = fread(in, 1, sizeof(in), stdin);
+ ret = decompress(in, in_size, NULL, &flush, NULL, &in_used, &error);
+ fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+static void test_cb_to_cb(void)
+{
+ int ret;
+ ret = decompress(NULL, 0, &fill, &flush, NULL, NULL, &error);
+ fprintf(stderr, "ret = %d\n", ret);
+}
+
+/*
+ * Not used by Linux <= 2.6.37-rc4 and newer probably won't use it either,
+ * but this kind of use case is still required to be supported by the API.
+ */
+static void test_cb_to_buf(void)
+{
+ int in_used;
+ int ret;
+ ret = decompress(in, 0, &fill, NULL, out, &in_used, &error);
+ /* fwrite(out, 1, FIXME, stdout); */
+ fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+ else if (strcmp(argv[1], "bb") == 0)
+ test_buf_to_buf();
+ else if (strcmp(argv[1], "bc") == 0)
+ test_buf_to_cb();
+ else if (strcmp(argv[1], "cc") == 0)
+ test_cb_to_cb();
+ else if (strcmp(argv[1], "cb") == 0)
+ test_cb_to_buf();
+ else
+ fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Test application to test buffer-to-buffer decoding
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+#define BUFFER_SIZE (1024 * 1024)
+
+static uint8_t in[BUFFER_SIZE];
+static uint8_t out[BUFFER_SIZE];
+
+int main(void)
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+
+ xz_crc32_init();
+
+ s = xz_dec_init(XZ_SINGLE, 0);
+ if (s == NULL) {
+ fputs("Initialization failed", stderr);
+ return 1;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = fread(in, 1, sizeof(in), stdin);
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = sizeof(out);
+
+ ret = xz_dec_run(s, &b);
+ xz_dec_end(s);
+
+ fwrite(out, 1, b.out_pos, stdout);
+ fprintf(stderr, "%d\n", ret);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Lazy test for the case when the output size is known
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "xz.h"
+
+static uint8_t in[1];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ const char *msg;
+ size_t uncomp_size;
+
+ if (argc != 2) {
+ fputs("Give uncompressed size as the argument", stderr);
+ return 1;
+ }
+
+ uncomp_size = atoi(argv[1]);
+
+ xz_crc32_init();
+
+ /*
+ * Support up to 64 MiB dictionary. The actually needed memory
+ * is allocated once the headers have been parsed.
+ */
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == NULL) {
+ msg = "Memory allocation failed\n";
+ goto error;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = uncomp_size < BUFSIZ ? uncomp_size : BUFSIZ;
+
+ while (true) {
+ if (b.in_pos == b.in_size) {
+ b.in_size = fread(in, 1, sizeof(in), stdin);
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out)) {
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ uncomp_size -= b.out_pos;
+ b.out_pos = 0;
+ b.out_size = uncomp_size < BUFSIZ
+ ? uncomp_size : BUFSIZ;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (ret == XZ_UNSUPPORTED_CHECK) {
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs("Unsupported check; not verifying "
+ "file integrity\n", stderr);
+ continue;
+ }
+#endif
+
+ if (uncomp_size != b.out_pos) {
+ msg = "Uncompressed size doesn't match\n";
+ goto error;
+ }
+
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+ || fclose(stdout)) {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ return 0;
+
+ case XZ_MEM_ERROR:
+ msg = "Memory allocation failed\n";
+ goto error;
+
+ case XZ_MEMLIMIT_ERROR:
+ msg = "Memory usage limit reached\n";
+ goto error;
+
+ case XZ_FORMAT_ERROR:
+ msg = "Not a .xz file\n";
+ goto error;
+
+ case XZ_OPTIONS_ERROR:
+ msg = "Unsupported options in the .xz headers\n";
+ goto error;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ msg = "File is corrupt\n";
+ goto error;
+
+ default:
+ msg = "Bug!\n";
+ goto error;
+ }
+ }
+
+error:
+ xz_dec_end(s);
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs(msg, stderr);
+ return 1;
+}
--- /dev/null
+/*
+ * Private includes and definitions for userspace use of XZ Embedded
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_CONFIG_H
+#define XZ_CONFIG_H
+
+/* Uncomment to enable CRC64 support. */
+/* #define XZ_USE_CRC64 */
+
+/* Uncomment as needed to enable BCJ filter decoders. */
+/* #define XZ_DEC_X86 */
+/* #define XZ_DEC_POWERPC */
+/* #define XZ_DEC_IA64 */
+/* #define XZ_DEC_ARM */
+/* #define XZ_DEC_ARMTHUMB */
+/* #define XZ_DEC_SPARC */
+
+/*
+ * MSVC doesn't support modern C but XZ Embedded is mostly C89
+ * so these are enough.
+ */
+#ifdef _MSC_VER
+typedef unsigned char bool;
+# define true 1
+# define false 0
+# define inline __inline
+#else
+# include <stdbool.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xz.h"
+
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+
+#ifndef min
+# define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#define min_t(type, x, y) min(x, y)
+
+/*
+ * Some functions have been marked with __always_inline to keep the
+ * performance reasonable even when the compiler is optimizing for
+ * small code size. You may be able to save a few bytes by #defining
+ * __always_inline to plain inline, but don't complain if the code
+ * becomes slow.
+ *
+ * NOTE: System headers on GNU/Linux may #define this macro already,
+ * so if you want to change it, you need to #undef it first.
+ */
+#ifndef __always_inline
+# ifdef __GNUC__
+# define __always_inline \
+ inline __attribute__((__always_inline__))
+# else
+# define __always_inline inline
+# endif
+#endif
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t get_unaligned_le32(const uint8_t *buf)
+{
+ return (uint32_t)buf[0]
+ | ((uint32_t)buf[1] << 8)
+ | ((uint32_t)buf[2] << 16)
+ | ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t get_unaligned_be32(const uint8_t *buf)
+{
+ return (uint32_t)(buf[0] << 24)
+ | ((uint32_t)buf[1] << 16)
+ | ((uint32_t)buf[2] << 8)
+ | (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+ buf[0] = (uint8_t)val;
+ buf[1] = (uint8_t)(val >> 8);
+ buf[2] = (uint8_t)(val >> 16);
+ buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+ buf[0] = (uint8_t)(val >> 24);
+ buf[1] = (uint8_t)(val >> 16);
+ buf[2] = (uint8_t)(val >> 8);
+ buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+# define get_le32 get_unaligned_le32
+#endif
+
+#endif
--- /dev/null
+/*
+ * Simple XZ decoder command line tool
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is really limited: Not all filters from .xz format are supported,
+ * only CRC32 is supported as the integrity check, and decoding of
+ * concatenated .xz streams is not supported. Thus, you may want to look
+ * at xzdec from XZ Utils if a few KiB bigger tool is not a problem.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+static uint8_t in[BUFSIZ];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ const char *msg;
+
+ if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
+ fputs("Uncompress a .xz file from stdin to stdout.\n"
+ "Arguments other than `--help' are ignored.\n",
+ stdout);
+ return 0;
+ }
+
+ xz_crc32_init();
+#ifdef XZ_USE_CRC64
+ xz_crc64_init();
+#endif
+
+ /*
+ * Support up to 64 MiB dictionary. The actually needed memory
+ * is allocated once the headers have been parsed.
+ */
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == NULL) {
+ msg = "Memory allocation failed\n";
+ goto error;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = BUFSIZ;
+
+ while (true) {
+ if (b.in_pos == b.in_size) {
+ b.in_size = fread(in, 1, sizeof(in), stdin);
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out)) {
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ b.out_pos = 0;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (ret == XZ_UNSUPPORTED_CHECK) {
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs("Unsupported check; not verifying "
+ "file integrity\n", stderr);
+ continue;
+ }
+#endif
+
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+ || fclose(stdout)) {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ return 0;
+
+ case XZ_MEM_ERROR:
+ msg = "Memory allocation failed\n";
+ goto error;
+
+ case XZ_MEMLIMIT_ERROR:
+ msg = "Memory usage limit reached\n";
+ goto error;
+
+ case XZ_FORMAT_ERROR:
+ msg = "Not a .xz file\n";
+ goto error;
+
+ case XZ_OPTIONS_ERROR:
+ msg = "Unsupported options in the .xz headers\n";
+ goto error;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ msg = "File is corrupt\n";
+ goto error;
+
+ default:
+ msg = "Bug!\n";
+ goto error;
+ }
+ }
+
+error:
+ xz_dec_end(s);
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs(msg, stderr);
+ return 1;
+}
--- /dev/null
+/******************************************************************************
+ * ventoy_http.c ---- ventoy http
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <ventoy_define.h>
+#include <ventoy_json.h>
+#include <ventoy_util.h>
+#include <ventoy_disk.h>
+#include <ventoy_http.h>
+#include "fat_filelib.h"
+
+static pthread_mutex_t g_api_mutex;
+static char g_cur_language[128];
+static int g_cur_part_style = 0;
+static char g_cur_server_token[64];
+static struct mg_context *g_ventoy_http_ctx = NULL;
+
+static uint32_t g_efi_part_offset = 0;
+static uint8_t *g_efi_part_raw_img = NULL;
+static uint8_t *g_grub_stg1_raw_img = NULL;
+
+static char g_cur_process_diskname[64];
+static char g_cur_process_type[64];
+static int g_cur_process_result = 0;
+static PROGRESS_POINT g_current_progress = PT_FINISH;
+
+static int ventoy_load_mbr_template(void)
+{
+ FILE *fp = NULL;
+
+ fp = fopen("boot/boot.img", "rb");
+ if (fp == NULL)
+ {
+ vlog("Failed to open file boot/boot.img\n");
+ return 1;
+ }
+
+ fread(g_mbr_template, 1, 512, fp);
+ fclose(fp);
+
+ ventoy_gen_preudo_uuid(g_mbr_template + 0x180);
+ return 0;
+}
+
+static int ventoy_disk_xz_flush(void *src, unsigned int size)
+{
+ memcpy(g_efi_part_raw_img + g_efi_part_offset, src, size);
+ g_efi_part_offset += size;
+
+ g_current_progress = PT_LOAD_DISK_IMG + (g_efi_part_offset / SIZE_1MB);
+ return (int)size;
+}
+
+static int ventoy_unxz_efipart_img(void)
+{
+ int rc;
+ int inlen;
+ int xzlen;
+ void *xzbuf = NULL;
+ uint8_t *buf = NULL;
+
+ rc = ventoy_read_file_to_buf(VENTOY_FILE_DISK_IMG, 0, &xzbuf, &xzlen);
+ vdebug("read disk.img.xz rc:%d len:%d\n", rc, xzlen);
+
+ if (g_efi_part_raw_img)
+ {
+ buf = g_efi_part_raw_img;
+ }
+ else
+ {
+ buf = malloc(VTOYEFI_PART_BYTES);
+ if (!buf)
+ {
+ check_free(xzbuf);
+ return 1;
+ }
+ }
+
+ g_efi_part_offset = 0;
+ g_efi_part_raw_img = buf;
+
+ rc = unxz(xzbuf, xzlen, NULL, ventoy_disk_xz_flush, buf, &inlen, NULL);
+ vdebug("ventoy_unxz_efipart_img len:%d rc:%d unxzlen:%u\n", inlen, rc, g_efi_part_offset);
+
+ check_free(xzbuf);
+ return 0;
+}
+
+static int ventoy_unxz_stg1_img(void)
+{
+ int rc;
+ int inlen;
+ int xzlen;
+ void *xzbuf = NULL;
+ uint8_t *buf = NULL;
+
+ rc = ventoy_read_file_to_buf(VENTOY_FILE_STG1_IMG, 0, &xzbuf, &xzlen);
+ vdebug("read core.img.xz rc:%d len:%d\n", rc, xzlen);
+
+ if (g_grub_stg1_raw_img)
+ {
+ buf = g_grub_stg1_raw_img;
+ }
+ else
+ {
+ buf = zalloc(SIZE_1MB);
+ if (!buf)
+ {
+ check_free(xzbuf);
+ return 1;
+ }
+ }
+
+ rc = unxz(xzbuf, xzlen, NULL, NULL, buf, &inlen, NULL);
+ vdebug("ventoy_unxz_stg1_img len:%d rc:%d\n", inlen, rc);
+
+ g_grub_stg1_raw_img = buf;
+
+ check_free(xzbuf);
+ return 0;
+}
+
+
+static int ventoy_http_save_cfg(void)
+{
+ FILE *fp;
+
+ fp = fopen("./Ventoy2Disk.ini", "w");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ fprintf(fp, "[Ventoy]\nLanguage=%s\nPartStyle=%d\n", g_cur_language, g_cur_part_style);
+
+ fclose(fp);
+ return 0;
+}
+
+static int ventoy_http_load_cfg(void)
+{
+ int i;
+ int len;
+ char line[256];
+ FILE *fp;
+
+ fp = fopen("./Ventoy2Disk.ini", "r");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ while (fgets(line, sizeof(line), fp))
+ {
+ len = (int)strlen(line);
+ for (i = len - 1; i >= 0; i--)
+ {
+ if (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n')
+ {
+ line[i] = 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ len = (int)strlen("Language=");
+ if (strncmp(line, "Language=", len) == 0)
+ {
+ scnprintf(g_cur_language, "%s", line + len);
+ }
+ else if (strncmp(line, "PartStyle=", strlen("PartStyle=")) == 0)
+ {
+ g_cur_part_style = (int)strtol(line + strlen("PartStyle="), NULL, 10);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+
+static int ventoy_json_result(struct mg_connection *conn, const char *err)
+{
+ mg_printf(conn,
+ "HTTP/1.1 200 OK \r\n"
+ "Content-Type: application/json\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n%s",
+ (int)strlen(err), err);
+ return 0;
+}
+
+static int ventoy_json_buffer(struct mg_connection *conn, const char *json_buf, int json_len)
+{
+ mg_printf(conn,
+ "HTTP/1.1 200 OK \r\n"
+ "Content-Type: application/json\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n%s",
+ json_len, json_buf);
+ return 0;
+}
+
+static int ventoy_api_sysinfo(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int busy = 0;
+ int pos = 0;
+ int buflen = 0;
+ char buf[512];
+
+ (void)json;
+
+ busy = (g_current_progress == PT_FINISH) ? 0 : 1;
+
+ buflen = sizeof(buf) - 1;
+ VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+ VTOY_JSON_FMT_OBJ_BEGIN();
+ VTOY_JSON_FMT_STRN("token", g_cur_server_token);
+ VTOY_JSON_FMT_STRN("language", g_cur_language);
+ VTOY_JSON_FMT_STRN("ventoy_ver", ventoy_get_local_version());
+ VTOY_JSON_FMT_UINT("partstyle", g_cur_part_style);
+ VTOY_JSON_FMT_BOOL("busy", busy);
+ VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname);
+ VTOY_JSON_FMT_STRN("process_type", g_cur_process_type);
+ VTOY_JSON_FMT_OBJ_END();
+ VTOY_JSON_FMT_END(pos);
+
+ ventoy_json_buffer(conn, buf, pos);
+ return 0;
+}
+
+static int ventoy_api_get_percent(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int pos = 0;
+ int buflen = 0;
+ int percent = 0;
+ char buf[128];
+
+ (void)json;
+
+ percent = g_current_progress * 100 / PT_FINISH;
+
+ buflen = sizeof(buf) - 1;
+ VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+ VTOY_JSON_FMT_OBJ_BEGIN();
+ VTOY_JSON_FMT_STRN("result", g_cur_process_result ? "failed" : "success");
+ VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname);
+ VTOY_JSON_FMT_STRN("process_type", g_cur_process_type);
+ VTOY_JSON_FMT_UINT("percent", percent);
+ VTOY_JSON_FMT_OBJ_END();
+ VTOY_JSON_FMT_END(pos);
+
+ ventoy_json_buffer(conn, buf, pos);
+ return 0;
+}
+
+static int ventoy_api_set_language(struct mg_connection *conn, VTOY_JSON *json)
+{
+ const char *lang = NULL;
+
+ lang = vtoy_json_get_string_ex(json, "language");
+ if (lang)
+ {
+ scnprintf(g_cur_language, "%s", lang);
+ ventoy_http_save_cfg();
+ }
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+static int ventoy_api_set_partstyle(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int ret;
+ int style = 0;
+
+ ret = vtoy_json_get_int(json, "partstyle", &style);
+ if (JSON_SUCCESS == ret)
+ {
+ if ((style == 0) || (style == 1))
+ {
+ g_cur_part_style = style;
+ ventoy_http_save_cfg();
+ }
+ }
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+static int ventoy_clean_disk(int fd, uint64_t size)
+{
+ int zerolen;
+ ssize_t len;
+ off_t offset;
+ void *buf = NULL;
+
+ vdebug("ventoy_clean_disk fd:%d size:%llu\n", fd, (_ull)size);
+
+ zerolen = 64 * 1024;
+ buf = zalloc(zerolen);
+ if (!buf)
+ {
+ vlog("failed to alloc clean buffer\n");
+ return 1;
+ }
+
+ offset = lseek(fd, 0, SEEK_SET);
+ len = write(fd, buf, zerolen);
+ vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen);
+
+ offset = lseek(fd, size - zerolen, SEEK_SET);
+ len = write(fd, buf, zerolen);
+ vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen);
+
+ free(buf);
+ return 0;
+}
+
+static int ventoy_write_legacy_grub(int fd, int partstyle)
+{
+ ssize_t len;
+ off_t offset;
+
+ if (partstyle)
+ {
+ vlog("Write GPT stage1 ...\n");
+
+ offset = lseek(fd, 512 * 34, SEEK_SET);
+
+ g_grub_stg1_raw_img[500] = 35;//update blocklist
+ len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512 * 34);
+
+ vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512 * 34, (_ull)len, SIZE_1MB - 512 * 34);
+ if (SIZE_1MB - 512 * 34 != len)
+ {
+ vlog("write length error\n");
+ return 1;
+ }
+ }
+ else
+ {
+ vlog("Write MBR stage1 ...\n");
+ offset = lseek(fd, 512, SEEK_SET);
+ len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512);
+
+ vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512, (_ull)len, SIZE_1MB - 512);
+ if (SIZE_1MB - 512 != len)
+ {
+ vlog("write length error\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int VentoyFatMemRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+ uint32 i;
+ uint32 offset;
+
+ for (i = 0; i < SectorCount; i++)
+ {
+ offset = (Sector + i) * 512;
+ memcpy(Buffer + i * 512, g_efi_part_raw_img + offset, 512);
+ }
+
+ return 1;
+}
+
+static int VentoyFatMemWrite(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+ uint32 i;
+ uint32 offset;
+
+ for (i = 0; i < SectorCount; i++)
+ {
+ offset = (Sector + i) * 512;
+ memcpy(g_efi_part_raw_img + offset, Buffer + i * 512, 512);
+ }
+
+ return 1;
+}
+
+static int VentoyProcSecureBoot(int SecureBoot)
+{
+ int rc = 0;
+ int size;
+ char *filebuf = NULL;
+ void *file = NULL;
+
+ vlog("VentoyProcSecureBoot %d ...\n", SecureBoot);
+
+ if (SecureBoot)
+ {
+ vlog("Secure boot is enabled ...\n");
+ return 0;
+ }
+
+ fl_init();
+
+ if (0 == fl_attach_media(VentoyFatMemRead, VentoyFatMemWrite))
+ {
+ file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+ vlog("Open ventoy efi file %p \n", file);
+ if (file)
+ {
+ fl_fseek(file, 0, SEEK_END);
+ size = (int)fl_ftell(file);
+ fl_fseek(file, 0, SEEK_SET);
+
+ vlog("ventoy efi file size %d ...\n", size);
+
+ filebuf = (char *)malloc(size);
+ if (filebuf)
+ {
+ fl_fread(filebuf, 1, size, file);
+ }
+
+ fl_fclose(file);
+
+ vlog("Now delete all efi files ...\n");
+ fl_remove("/EFI/BOOT/BOOTX64.EFI");
+ fl_remove("/EFI/BOOT/grubx64.efi");
+ fl_remove("/EFI/BOOT/grubx64_real.efi");
+ fl_remove("/EFI/BOOT/MokManager.efi");
+ fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer");
+
+ file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb");
+ vlog("Open bootx64 efi file %p \n", file);
+ if (file)
+ {
+ if (filebuf)
+ {
+ fl_fwrite(filebuf, 1, size, file);
+ }
+
+ fl_fflush(file);
+ fl_fclose(file);
+ }
+
+ if (filebuf)
+ {
+ free(filebuf);
+ }
+ }
+
+ file = fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb");
+ vlog("Open ventoy efi file %p\n", file);
+ if (file)
+ {
+ fl_fseek(file, 0, SEEK_END);
+ size = (int)fl_ftell(file);
+ fl_fseek(file, 0, SEEK_SET);
+
+ vlog("ventoy efi file size %d ...\n", size);
+
+ filebuf = (char *)malloc(size);
+ if (filebuf)
+ {
+ fl_fread(filebuf, 1, size, file);
+ }
+
+ fl_fclose(file);
+
+ vlog("Now delete all efi files ...\n");
+ fl_remove("/EFI/BOOT/BOOTIA32.EFI");
+ fl_remove("/EFI/BOOT/grubia32.efi");
+ fl_remove("/EFI/BOOT/grubia32_real.efi");
+ fl_remove("/EFI/BOOT/mmia32.efi");
+
+ file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
+ vlog("Open bootia32 efi file %p\n", file);
+ if (file)
+ {
+ if (filebuf)
+ {
+ fl_fwrite(filebuf, 1, size, file);
+ }
+
+ fl_fflush(file);
+ fl_fclose(file);
+ }
+
+ if (filebuf)
+ {
+ free(filebuf);
+ }
+ }
+
+ }
+ else
+ {
+ rc = 1;
+ }
+
+ fl_shutdown();
+
+ return rc;
+}
+
+static int ventoy_check_efi_part_data(int fd, uint64_t offset)
+{
+ int i;
+ ssize_t len;
+ char *buf;
+
+ buf = malloc(SIZE_1MB);
+ if (!buf)
+ {
+ return 0;
+ }
+
+ lseek(fd, offset, SEEK_SET);
+ for (i = 0; i < 32; i++)
+ {
+ len = read(fd, buf, SIZE_1MB);
+ if (len != SIZE_1MB || memcmp(buf, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB))
+ {
+ vlog("part2 data check failed i=%d len:%llu\n", i, (_ull)len);
+ return 1;
+ }
+
+ g_current_progress = PT_CHECK_PART2 + (i / 4);
+ }
+
+ return 0;
+}
+
+static int ventoy_write_efipart(int fd, uint64_t offset, uint32_t secureboot)
+{
+ int i;
+ ssize_t len;
+
+ vlog("Formatting part2 EFI offset:%llu ...\n", (_ull)offset);
+ lseek(fd, offset, SEEK_SET);
+
+ VentoyProcSecureBoot((int)secureboot);
+
+ g_current_progress = PT_WRITE_VENTOY_START;
+ for (i = 0; i < 32; i++)
+ {
+ len = write(fd, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB);
+ vlog("write disk writelen:%lld datalen:%d [ %s ]\n",
+ (_ll)len, SIZE_1MB, (len == SIZE_1MB) ? "success" : "failed");
+
+ if (len != SIZE_1MB)
+ {
+ vlog("failed to format part2 EFI\n");
+ return 1;
+ }
+
+ g_current_progress = PT_WRITE_VENTOY_START + i / 4;
+ }
+
+ return 0;
+}
+
+static int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
+{
+ uint64_t LBA;
+ uint64_t BackupLBA;
+
+ memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
+
+ LBA = pHead->EfiStartLBA;
+ BackupLBA = pHead->EfiBackupLBA;
+
+ pHead->EfiStartLBA = BackupLBA;
+ pHead->EfiBackupLBA = LBA;
+ pHead->PartTblStartLBA = BackupLBA + 1 - 33;
+
+ pHead->Crc = 0;
+ pHead->Crc = ventoy_crc32(pHead, pHead->Length);
+
+ return 0;
+}
+
+static int ventoy_write_gpt_part_table(int fd, uint64_t disksize, VTOY_GPT_INFO *gpt)
+{
+ ssize_t len;
+ off_t offset;
+ VTOY_GPT_HDR BackupHead;
+
+ VentoyFillBackupGptHead(gpt, &BackupHead);
+
+ offset = lseek(fd, disksize - 512, SEEK_SET);
+ len = write(fd, &BackupHead, sizeof(VTOY_GPT_HDR));
+ vlog("write backup gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+ if (offset != disksize - 512 || len != sizeof(VTOY_GPT_HDR))
+ {
+ return 1;
+ }
+
+ offset = lseek(fd, disksize - 512 * 33, SEEK_SET);
+ len = write(fd, gpt->PartTbl, sizeof(gpt->PartTbl));
+ vlog("write main gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+ if (offset != disksize - 512 * 33 || len != sizeof(gpt->PartTbl))
+ {
+ return 1;
+ }
+
+ offset = lseek(fd, 0, SEEK_SET);
+ len = write(fd, gpt, sizeof(VTOY_GPT_INFO));
+ vlog("write gpt part head off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+ if (offset != 0 || len != sizeof(VTOY_GPT_INFO))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void * ventoy_update_thread(void *data)
+{
+ int fd;
+ ssize_t len;
+ off_t offset;
+ MBR_HEAD MBR;
+ ventoy_disk *disk = NULL;
+ ventoy_thread_data *thread = (ventoy_thread_data *)data;
+
+ vdebug("ventoy_update_thread run ...\n");
+
+ fd = thread->diskfd;
+ disk = thread->disk;
+
+ g_current_progress = PT_PRAPARE_FOR_CLEAN;
+ vdebug("check disk %s\n", disk->disk_name);
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("disk is mounted, now try to unmount it ...\n");
+ ventoy_try_umount_disk(disk->disk_path);
+ }
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("%s is mounted and can't umount!\n", disk->disk_path);
+ goto err;
+ }
+ else
+ {
+ vlog("disk is not mounted now, we can do continue ...\n");
+ }
+
+ g_current_progress = PT_LOAD_CORE_IMG;
+ ventoy_unxz_stg1_img();
+
+ g_current_progress = PT_LOAD_DISK_IMG;
+ ventoy_unxz_efipart_img();
+
+ g_current_progress = PT_FORMAT_PART2;
+
+ vlog("Formatting part2 EFI ...\n");
+ if (0 != ventoy_write_efipart(fd, disk->vtoydata.part2_start_sector * 512, thread->secure_boot))
+ {
+ vlog("Failed to format part2 efi ...\n");
+ goto err;
+ }
+
+ g_current_progress = PT_WRITE_STG1_IMG;
+
+ vlog("Writing legacy grub ...\n");
+ if (0 != ventoy_write_legacy_grub(fd, disk->vtoydata.partition_style))
+ {
+ vlog("ventoy_write_legacy_grub failed ...\n");
+ goto err;
+ }
+
+ offset = lseek(fd, 512 * 2040, SEEK_SET);
+ len = write(fd, disk->vtoydata.rsvdata, sizeof(disk->vtoydata.rsvdata));
+ vlog("Writing reserve data offset:%llu len:%llu ...\n", (_ull)offset, (_ull)len);
+
+ memcpy(&MBR, &(disk->vtoydata.gptinfo.MBR), 512);
+ if (disk->vtoydata.partition_style == 0 && MBR.PartTbl[0].Active == 0)
+ {
+ MBR.PartTbl[0].Active = 0x80;
+ MBR.PartTbl[1].Active = 0;
+ MBR.PartTbl[2].Active = 0;
+ MBR.PartTbl[3].Active = 0;
+
+ offset = lseek(fd, 0, SEEK_SET);
+ len = write(fd, &MBR, 512);
+ vlog("set MBR partition 1 active flag enabled offset:%llu len:%llu\n", (_ull)offset, (_ull)len);
+ }
+
+ g_current_progress = PT_SYNC_DATA1;
+
+ vlog("fsync data1...\n");
+ fsync(fd);
+ vtoy_safe_close_fd(fd);
+
+ g_current_progress = PT_SYNC_DATA2;
+
+ vlog("====================================\n");
+ vlog("====== ventoy update success ======\n");
+ vlog("====================================\n");
+ goto end;
+
+err:
+ g_cur_process_result = 1;
+ vtoy_safe_close_fd(fd);
+
+end:
+ g_current_progress = PT_FINISH;
+
+ check_free(thread);
+
+ return NULL;
+}
+
+static void * ventoy_install_thread(void *data)
+{
+ int fd;
+ ssize_t len;
+ off_t offset;
+ MBR_HEAD MBR;
+ ventoy_disk *disk = NULL;
+ VTOY_GPT_INFO *gpt = NULL;
+ ventoy_thread_data *thread = (ventoy_thread_data *)data;
+ uint64_t Part1StartSector = 0;
+ uint64_t Part1SectorCount = 0;
+ uint64_t Part2StartSector = 0;
+
+ vdebug("ventoy_install_thread run ...\n");
+
+ fd = thread->diskfd;
+ disk = thread->disk;
+
+ g_current_progress = PT_PRAPARE_FOR_CLEAN;
+ vdebug("check disk %s\n", disk->disk_name);
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("disk is mounted, now try to unmount it ...\n");
+ ventoy_try_umount_disk(disk->disk_path);
+ }
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("%s is mounted and can't umount!\n", disk->disk_path);
+ goto err;
+ }
+ else
+ {
+ vlog("disk is not mounted now, we can do continue ...\n");
+ }
+
+ g_current_progress = PT_DEL_ALL_PART;
+ ventoy_clean_disk(fd, disk->size_in_byte);
+
+ g_current_progress = PT_LOAD_CORE_IMG;
+ ventoy_unxz_stg1_img();
+
+ g_current_progress = PT_LOAD_DISK_IMG;
+ ventoy_unxz_efipart_img();
+
+ if (thread->partstyle)
+ {
+ vdebug("Fill GPT part table\n");
+ gpt = zalloc(sizeof(VTOY_GPT_INFO));
+ ventoy_fill_gpt(disk->size_in_byte, thread->reserveBytes, thread->align4kb, gpt);
+ Part1StartSector = gpt->PartTbl[0].StartLBA;
+ Part1SectorCount = gpt->PartTbl[0].LastLBA - Part1StartSector + 1;
+ Part2StartSector = gpt->PartTbl[1].StartLBA;
+ }
+ else
+ {
+ vdebug("Fill MBR part table\n");
+ ventoy_fill_mbr(disk->size_in_byte, thread->reserveBytes, thread->align4kb, 0, &MBR);
+ Part1StartSector = MBR.PartTbl[0].StartSectorId;
+ Part1SectorCount = MBR.PartTbl[0].SectorCount;
+ Part2StartSector = MBR.PartTbl[1].StartSectorId;
+ }
+
+ vlog("Part1StartSector:%llu Part1SectorCount:%llu Part2StartSector:%llu\n",
+ (_ull)Part1StartSector, (_ull)Part1SectorCount, (_ull)Part2StartSector);
+
+ if (thread->partstyle != disk->partstyle)
+ {
+ vlog("Wait for format part1 (partstyle changed) ...\n");
+ sleep(1);
+ }
+
+ g_current_progress = PT_FORMAT_PART1;
+ vlog("Formatting part1 exFAT %s ...\n", disk->disk_path);
+ if (0 != mkexfat_main(disk->disk_path, fd, Part1SectorCount))
+ {
+ vlog("Failed to format exfat ...\n");
+ goto err;
+ }
+
+ g_current_progress = PT_FORMAT_PART2;
+ vlog("Formatting part2 EFI ...\n");
+ if (0 != ventoy_write_efipart(fd, Part2StartSector * 512, thread->secure_boot))
+ {
+ vlog("Failed to format part2 efi ...\n");
+ goto err;
+ }
+
+ g_current_progress = PT_WRITE_STG1_IMG;
+ vlog("Writing legacy grub ...\n");
+ if (0 != ventoy_write_legacy_grub(fd, thread->partstyle))
+ {
+ vlog("ventoy_write_legacy_grub failed ...\n");
+ goto err;
+ }
+
+ g_current_progress = PT_SYNC_DATA1;
+ vlog("fsync data1...\n");
+ fsync(fd);
+ vtoy_safe_close_fd(fd);
+
+ /* reopen for check part2 data */
+ vlog("Checking part2 efi data %s ...\n", disk->disk_path);
+ g_current_progress = PT_CHECK_PART2;
+ fd = open(disk->disk_path, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ vlog("failed to open %s for check fd:%d err:%d\n", disk->disk_path, fd, errno);
+ goto err;
+ }
+
+ if (0 == ventoy_check_efi_part_data(fd, Part2StartSector * 512))
+ {
+ vlog("efi part data check success\n");
+ }
+ else
+ {
+ vlog("efi part data check failed\n");
+ goto err;
+ }
+
+ vtoy_safe_close_fd(fd);
+
+ /* reopen for write part table */
+ g_current_progress = PT_WRITE_PART_TABLE;
+ vlog("Writting Partition Table style:%d...\n", thread->partstyle);
+
+ fd = open(disk->disk_path, O_RDWR | O_BINARY);
+ if (fd < 0)
+ {
+ vlog("failed to open %s for part table fd:%d err:%d\n", disk->disk_path, fd, errno);
+ goto err;
+ }
+
+ if (thread->partstyle)
+ {
+ ventoy_write_gpt_part_table(fd, disk->size_in_byte, gpt);
+ }
+ else
+ {
+ offset = lseek(fd, 0, SEEK_SET);
+ len = write(fd, &MBR, 512);
+ vlog("Writting MBR Partition Table %llu %llu\n", (_ull)offset, (_ull)len);
+ if (offset != 0 || len != 512)
+ {
+ goto err;
+ }
+ }
+
+ g_current_progress = PT_SYNC_DATA2;
+ vlog("fsync data2...\n");
+ fsync(fd);
+ vtoy_safe_close_fd(fd);
+
+
+ vlog("====================================\n");
+ vlog("====== ventoy install success ======\n");
+ vlog("====================================\n");
+ goto end;
+
+err:
+ g_cur_process_result = 1;
+ vtoy_safe_close_fd(fd);
+
+end:
+ g_current_progress = PT_FINISH;
+
+ check_free(gpt);
+ check_free(thread);
+
+ return NULL;
+}
+
+static int ventoy_api_clean(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int i = 0;
+ int fd = 0;
+ ventoy_disk *disk = NULL;
+ const char *diskname = NULL;
+ char path[128];
+
+ if (g_current_progress != PT_FINISH)
+ {
+ ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+ return 0;
+ }
+
+ diskname = vtoy_json_get_string_ex(json, "disk");
+ if (diskname == NULL)
+ {
+ ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+ return 0;
+ }
+
+ for (i = 0; i < g_disk_num; i++)
+ {
+ if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+ {
+ disk = g_disk_list + i;
+ break;
+ }
+ }
+
+ if (disk == NULL)
+ {
+ vlog("disk %s not found\n", diskname);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ scnprintf(path, "/sys/block/%s", diskname);
+ if (access(path, F_OK) < 0)
+ {
+ vlog("File %s not exist anymore\n", path);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ vlog("==================================\n");
+ vlog("===== ventoy clean %s =====\n", disk->disk_path);
+ vlog("==================================\n");
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("disk is mounted, now try to unmount it ...\n");
+ ventoy_try_umount_disk(disk->disk_path);
+ }
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("%s is mounted and can't umount!\n", disk->disk_path);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+ else
+ {
+ vlog("disk is not mounted now, we can do the clean ...\n");
+ }
+
+ fd = open(disk->disk_path, O_RDWR | O_BINARY);
+ if (fd < 0)
+ {
+ vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ vdebug("start clean %s ...\n", disk->disk_model);
+ ventoy_clean_disk(fd, disk->size_in_byte);
+
+ vtoy_safe_close_fd(fd);
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+static int ventoy_api_install(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int i = 0;
+ int ret = 0;
+ int fd = 0;
+ uint32_t align4kb = 0;
+ uint32_t style = 0;
+ uint32_t secure_boot = 0;
+ uint64_t reserveBytes = 0;
+ ventoy_disk *disk = NULL;
+ const char *diskname = NULL;
+ const char *reserve_space = NULL;
+ ventoy_thread_data *thread = NULL;
+ char path[128];
+
+ if (g_current_progress != PT_FINISH)
+ {
+ ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+ return 0;
+ }
+
+ diskname = vtoy_json_get_string_ex(json, "disk");
+ reserve_space = vtoy_json_get_string_ex(json, "reserve_space");
+ ret += vtoy_json_get_uint(json, "partstyle", &style);
+ ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot);
+ ret += vtoy_json_get_uint(json, "align_4kb", &align4kb);
+
+ if (diskname == NULL || reserve_space == NULL || ret != JSON_SUCCESS)
+ {
+ ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+ return 0;
+ }
+
+ reserveBytes = (uint64_t)strtoull(reserve_space, NULL, 10);
+
+ for (i = 0; i < g_disk_num; i++)
+ {
+ if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+ {
+ disk = g_disk_list + i;
+ break;
+ }
+ }
+
+ if (disk == NULL)
+ {
+ vlog("disk %s not found\n", diskname);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ scnprintf(path, "/sys/block/%s", diskname);
+ if (access(path, F_OK) < 0)
+ {
+ vlog("File %s not exist anymore\n", path);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ if (disk->size_in_byte > 2199023255552ULL && style == 0)
+ {
+ vlog("disk %s is more than 2TB and GPT is needed\n", path);
+ ventoy_json_result(conn, VTOY_JSON_MBR_2TB_RET);
+ return 0;
+ }
+
+ if ((reserveBytes + VTOYEFI_PART_BYTES * 2) > disk->size_in_byte)
+ {
+ vlog("reserve space %llu is too big for disk %s %llu\n", (_ull)reserveBytes, path, (_ull)disk->size_in_byte);
+ ventoy_json_result(conn, VTOY_JSON_INVALID_RSV_RET);
+ return 0;
+ }
+
+ vlog("==================================================================================\n");
+ vlog("===== ventoy install %s style:%s secureboot:%u align4K:%u reserve:%llu =========\n",
+ disk->disk_path, (style ? "GPT" : "MBR"), secure_boot, align4kb, (_ull)reserveBytes);
+ vlog("==================================================================================\n");
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("disk is mounted, now try to unmount it ...\n");
+ ventoy_try_umount_disk(disk->disk_path);
+ }
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("%s is mounted and can't umount!\n", disk->disk_path);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+ else
+ {
+ vlog("disk is not mounted now, we can do the install ...\n");
+ }
+
+ fd = open(disk->disk_path, O_RDWR | O_BINARY);
+ if (fd < 0)
+ {
+ vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ vdebug("start install thread %s ...\n", disk->disk_model);
+ thread = zalloc(sizeof(ventoy_thread_data));
+ if (!thread)
+ {
+ vtoy_safe_close_fd(fd);
+ vlog("failed to alloc thread data err:%d\n", errno);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ g_current_progress = PT_START;
+ g_cur_process_result = 0;
+ scnprintf(g_cur_process_type, "%s", "install");
+ scnprintf(g_cur_process_diskname, "%s", disk->disk_name);
+
+ thread->disk = disk;
+ thread->diskfd = fd;
+ thread->align4kb = align4kb;
+ thread->partstyle = style;
+ thread->secure_boot = secure_boot;
+ thread->reserveBytes = reserveBytes;
+
+ mg_start_thread(ventoy_install_thread, thread);
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+static int ventoy_api_update(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int i = 0;
+ int ret = 0;
+ int fd = 0;
+ uint32_t secure_boot = 0;
+ ventoy_disk *disk = NULL;
+ const char *diskname = NULL;
+ ventoy_thread_data *thread = NULL;
+ char path[128];
+
+ if (g_current_progress != PT_FINISH)
+ {
+ ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+ return 0;
+ }
+
+ diskname = vtoy_json_get_string_ex(json, "disk");
+ ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot);
+ if (diskname == NULL || ret != JSON_SUCCESS)
+ {
+ ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+ return 0;
+ }
+
+ for (i = 0; i < g_disk_num; i++)
+ {
+ if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+ {
+ disk = g_disk_list + i;
+ break;
+ }
+ }
+
+ if (disk == NULL)
+ {
+ vlog("disk %s not found\n", diskname);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ if (disk->vtoydata.ventoy_valid == 0)
+ {
+ vlog("disk %s is not ventoy disk\n", diskname);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ scnprintf(path, "/sys/block/%s", diskname);
+ if (access(path, F_OK) < 0)
+ {
+ vlog("File %s not exist anymore\n", path);
+ ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+ return 0;
+ }
+
+ vlog("==========================================================\n");
+ vlog("===== ventoy update %s new_secureboot:%u =========\n", disk->disk_path, secure_boot);
+ vlog("==========================================================\n");
+
+ vlog("%s version:%s partstyle:%u oldsecureboot:%u reserve:%llu\n",
+ disk->disk_path, disk->vtoydata.ventoy_ver,
+ disk->vtoydata.partition_style,
+ disk->vtoydata.secure_boot_flag,
+ (_ull)(disk->vtoydata.preserved_space)
+ );
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("disk is mounted, now try to unmount it ...\n");
+ ventoy_try_umount_disk(disk->disk_path);
+ }
+
+ if (ventoy_is_disk_mounted(disk->disk_path))
+ {
+ vlog("%s is mounted and can't umount!\n", disk->disk_path);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+ else
+ {
+ vlog("disk is not mounted now, we can do the update ...\n");
+ }
+
+ fd = open(disk->disk_path, O_RDWR | O_BINARY);
+ if (fd < 0)
+ {
+ vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ vdebug("start update thread %s ...\n", disk->disk_model);
+ thread = zalloc(sizeof(ventoy_thread_data));
+ if (!thread)
+ {
+ vtoy_safe_close_fd(fd);
+ vlog("failed to alloc thread data err:%d\n", errno);
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ g_current_progress = PT_START;
+ g_cur_process_result = 0;
+ scnprintf(g_cur_process_type, "%s", "update");
+ scnprintf(g_cur_process_diskname, "%s", disk->disk_name);
+
+ thread->disk = disk;
+ thread->diskfd = fd;
+ thread->secure_boot = secure_boot;
+
+ mg_start_thread(ventoy_update_thread, thread);
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+
+static int ventoy_api_refresh_device(struct mg_connection *conn, VTOY_JSON *json)
+{
+ (void)json;
+
+ if (g_current_progress == PT_FINISH)
+ {
+ g_disk_num = 0;
+ ventoy_disk_enumerate_all();
+ }
+
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+}
+
+static int ventoy_api_get_dev_list(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int i = 0;
+ int rc = 0;
+ int pos = 0;
+ int buflen = 0;
+ uint32_t alldev = 0;
+ char *buf = NULL;
+ ventoy_disk *cur = NULL;
+
+ rc = vtoy_json_get_uint(json, "alldev", &alldev);
+ if (JSON_SUCCESS != rc)
+ {
+ alldev = 0;
+ }
+
+ buflen = g_disk_num * 1024;
+ buf = (char *)malloc(buflen + 1024);
+ if (!buf)
+ {
+ ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+ return 0;
+ }
+
+ VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+ VTOY_JSON_FMT_OBJ_BEGIN();
+ VTOY_JSON_FMT_KEY("list");
+ VTOY_JSON_FMT_ARY_BEGIN();
+
+ for (i = 0; i < g_disk_num; i++)
+ {
+ cur = g_disk_list + i;
+
+ if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
+ {
+ continue;
+ }
+
+ VTOY_JSON_FMT_OBJ_BEGIN();
+ VTOY_JSON_FMT_STRN("name", cur->disk_name);
+ VTOY_JSON_FMT_STRN("model", cur->disk_model);
+ VTOY_JSON_FMT_STRN("size", cur->human_readable_size);
+ VTOY_JSON_FMT_UINT("vtoy_valid", cur->vtoydata.ventoy_valid);
+ VTOY_JSON_FMT_STRN("vtoy_ver", cur->vtoydata.ventoy_ver);
+ VTOY_JSON_FMT_UINT("vtoy_secure_boot", cur->vtoydata.secure_boot_flag);
+ VTOY_JSON_FMT_UINT("vtoy_partstyle", cur->vtoydata.partition_style);
+ VTOY_JSON_FMT_OBJ_ENDEX();
+ }
+
+ VTOY_JSON_FMT_ARY_END();
+ VTOY_JSON_FMT_OBJ_END();
+ VTOY_JSON_FMT_END(pos);
+
+ ventoy_json_buffer(conn, buf, pos);
+ return 0;
+}
+
+static JSON_CB g_ventoy_json_cb[] =
+{
+ { "sysinfo", ventoy_api_sysinfo },
+ { "sel_language", ventoy_api_set_language },
+ { "sel_partstyle", ventoy_api_set_partstyle },
+ { "refresh_device", ventoy_api_refresh_device },
+ { "get_dev_list", ventoy_api_get_dev_list },
+ { "install", ventoy_api_install },
+ { "update", ventoy_api_update },
+ { "clean", ventoy_api_clean },
+ { "get_percent", ventoy_api_get_percent },
+};
+
+static int ventoy_json_handler(struct mg_connection *conn, VTOY_JSON *json)
+{
+ int i;
+ const char *token = NULL;
+ const char *method = NULL;
+
+ method = vtoy_json_get_string_ex(json, "method");
+ if (!method)
+ {
+ ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+ return 0;
+ }
+
+ if (strcmp(method, "sysinfo"))
+ {
+ token = vtoy_json_get_string_ex(json, "token");
+ if (token == NULL || strcmp(token, g_cur_server_token))
+ {
+ ventoy_json_result(conn, VTOY_JSON_TOKEN_ERR_RET);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < (int)(sizeof(g_ventoy_json_cb) / sizeof(g_ventoy_json_cb[0])); i++)
+ {
+ if (strcmp(method, g_ventoy_json_cb[i].method) == 0)
+ {
+ g_ventoy_json_cb[i].callback(conn, json);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ventoy_request_handler(struct mg_connection *conn)
+{
+ int post_data_len;
+ int post_buf_len;
+ VTOY_JSON *json = NULL;
+ char *post_data_buf = NULL;
+ const struct mg_request_info *ri = NULL;
+ char stack_buf[512];
+
+ ri = mg_get_request_info(conn);
+
+ if (strcmp(ri->uri, "/vtoy/json") == 0)
+ {
+ if (ri->content_length > 500)
+ {
+ post_data_buf = malloc(ri->content_length + 4);
+ post_buf_len = ri->content_length + 1;
+ }
+ else
+ {
+ post_data_buf = stack_buf;
+ post_buf_len = sizeof(stack_buf);
+ }
+
+ post_data_len = mg_read(conn, post_data_buf, post_buf_len);
+ post_data_buf[post_data_len] = 0;
+
+ json = vtoy_json_create();
+ if (JSON_SUCCESS == vtoy_json_parse(json, post_data_buf))
+ {
+ pthread_mutex_lock(&g_api_mutex);
+ ventoy_json_handler(conn, json->pstChild);
+ pthread_mutex_unlock(&g_api_mutex);
+ }
+ else
+ {
+ ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+ }
+
+ vtoy_json_destroy(json);
+
+ if (post_data_buf != stack_buf)
+ {
+ free(post_data_buf);
+ }
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int ventoy_http_start(const char *ip, const char *port)
+{
+ uint8_t uuid[16];
+ char addr[128];
+ struct mg_callbacks callbacks;
+ const char *options[] =
+ {
+ "listening_ports", "24680",
+ "document_root", "WebUI",
+ "error_log_file", VTOY_LOG_FILE,
+ "request_timeout_ms", "10000",
+ NULL
+ };
+
+ /* unique token */
+ ventoy_gen_preudo_uuid(uuid);
+ scnprintf(g_cur_server_token, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+
+ /* option */
+ scnprintf(addr, "%s:%s", ip, port);
+ options[1] = addr;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.begin_request = ventoy_request_handler;
+ g_ventoy_http_ctx = mg_start(&callbacks, NULL, options);
+
+ return g_ventoy_http_ctx ? 0 : 1;
+}
+
+int ventoy_http_stop(void)
+{
+ if (g_ventoy_http_ctx)
+ {
+ mg_stop(g_ventoy_http_ctx);
+ }
+ return 0;
+}
+
+int ventoy_http_init(void)
+{
+ pthread_mutex_init(&g_api_mutex, NULL);
+
+ ventoy_http_load_cfg();
+
+ ventoy_load_mbr_template();
+
+ return 0;
+}
+
+void ventoy_http_exit(void)
+{
+ pthread_mutex_destroy(&g_api_mutex);
+
+ check_free(g_efi_part_raw_img);
+ g_efi_part_raw_img = NULL;
+}
+
--- /dev/null
+/******************************************************************************
+ * ventoy_http.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_HTTP_H__
+#define __VENTOY_HTTP_H__
+
+#include <civetweb.h>
+
+typedef enum PROGRESS_POINT
+{
+ PT_START = 1,
+
+ PT_PRAPARE_FOR_CLEAN,
+ PT_DEL_ALL_PART,
+
+ PT_LOAD_CORE_IMG,
+ PT_LOAD_DISK_IMG,
+ PT_UNXZ_DISK_IMG_FINISH = PT_LOAD_DISK_IMG + 32,
+
+ PT_FORMAT_PART1, //10
+
+ PT_FORMAT_PART2,
+
+ PT_WRITE_VENTOY_START,
+ PT_WRITE_VENTOY_FINISH = PT_WRITE_VENTOY_START + 8,
+
+ PT_WRITE_STG1_IMG,//45
+ PT_SYNC_DATA1,
+
+ PT_CHECK_PART2,
+ PT_CHECK_PART2_FINISH = PT_CHECK_PART2 + 8,
+
+ PT_WRITE_PART_TABLE,//52
+ PT_SYNC_DATA2,
+
+ PT_FINISH
+}PROGRESS_POINT;
+
+typedef int (*ventoy_json_callback)(struct mg_connection *conn, VTOY_JSON *json);
+typedef struct JSON_CB
+{
+ const char *method;
+ ventoy_json_callback callback;
+}JSON_CB;
+
+typedef struct ventoy_thread_data
+{
+ int diskfd;
+ uint32_t align4kb;
+ uint32_t partstyle;
+ uint32_t secure_boot;
+ uint64_t reserveBytes;
+ ventoy_disk *disk;
+}ventoy_thread_data;
+
+extern int g_vtoy_exfat_disk_fd;
+extern uint64_t g_vtoy_exfat_part_size;
+
+int ventoy_http_init(void);
+void ventoy_http_exit(void);
+int ventoy_http_start(const char *ip, const char *port);
+int ventoy_http_stop(void);
+int mkexfat_main(const char *devpath, int fd, uint64_t part_sector_count);
+
+#endif /* __VENTOY_HTTP_H__ */
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <ventoy_define.h>
+#include <ventoy_util.h>
+#include <ventoy_json.h>
+#include <ventoy_disk.h>
+#include <ventoy_http.h>
+
+int ventoy_log_init(void);
+void ventoy_log_exit(void);
+
+void ventoy_signal_stop(int sig)
+{
+ vlog("ventoy server exit due to signal ...\n");
+ printf("ventoy server exit ...\n");
+
+ ventoy_http_stop();
+ ventoy_http_exit();
+ ventoy_disk_exit();
+ ventoy_log_exit();
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ const char *ip = "127.0.0.1";
+ const char *port = "24680";
+
+ if (argc != 3)
+ {
+ printf("Invalid argc %d\n", argc);
+ return 1;
+ }
+
+ if (isdigit(argv[1][0]))
+ {
+ ip = argv[1];
+ }
+
+ if (isdigit(argv[2][0]))
+ {
+ port = argv[2];
+ }
+
+ ventoy_log_init();
+
+ vlog("===============================================\n");
+ vlog("===== Ventoy2Disk %s %s:%s =====\n", ventoy_get_local_version(), ip, port);
+ vlog("===============================================\n");
+
+ ventoy_disk_init();
+
+ ventoy_http_init();
+
+ rc = ventoy_http_start(ip, port);
+ if (rc)
+ {
+ printf("failed to start http server\n");
+ }
+ else
+ {
+ signal(SIGINT, ventoy_signal_stop);
+ signal(SIGTSTP, ventoy_signal_stop);
+ signal(SIGQUIT, ventoy_signal_stop);
+ while (1)
+ {
+ sleep(100);
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+<html>\r
+<head>\r
+ <meta charset="utf-8">\r
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">\r
+ <!-- HTTP 1.1 --> \r
+ <meta http-equiv="pragma" content="no-cache"> \r
+ <!-- HTTP 1.0 --> \r
+ <meta http-equiv="cache-control" content="no-cache"> \r
+ <title>Ventoy2Disk</title>\r
+ <!-- Tell the browser to be responsive to screen width -->\r
+ <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">\r
+ <!-- Bootstrap 3.3.5 -->\r
+ <link rel="stylesheet" href="static/bootstrap/css/bootstrap.min.css">\r
+ <!-- Font Awesome -->\r
+ <link rel="stylesheet" href="static/css/font-awesome.min.css">\r
+ <!-- Ionicons -->\r
+ <link rel="stylesheet" href="static/css/ionicons.min.css">\r
+ <!-- Theme style -->\r
+ <link rel="stylesheet" href="static/AdminLTE/css/AdminLTE.min.css">\r
+ <!-- AdminLTE Skins. Choose a skin from the css/skins\r
+ folder instead of downloading all of them to reduce the load. --> \r
+ \r
+ <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->\r
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\r
+ <!--[if lt IE 9]>\r
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>\r
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>\r
+ <![endif]-->\r
+ \r
+ <!-- ./wrapper -->\r
+ <!-- jQuery 2.1.4 -->\r
+ <script src="static/js/jQuery-2.1.4.min.js"></script>\r
+ <!-- jquery validate -->\r
+ <script src="static/js/jquery.validate.min.js"></script>\r
+ <!-- Bootstrap 3.3.5 -->\r
+ <script src="static/bootstrap/js/bootstrap.min.js"></script>\r
+ <!-- AdminLTE App -->\r
+ <script src="static/AdminLTE/js/app.min.js"></script> \r
+ <script src="static/js/jquery.vtoy.alert.js"></script>\r
+ <script src="static/js/vtoy.js"></script> \r
+ <script src="static/js/languages.js"></script> \r
+ \r
+ \r
+ <style type="text/css">\r
+ * {\r
+ font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;\r
+ }\r
+ .modal {\r
+ padding-top: 80px;\r
+ }\r
+ .treeview-menu a {\r
+ margin-left: 20px;\r
+ }\r
+ span.vtoy_ver {\r
+ display:inline-block;\r
+ font-size:28px;\r
+ font-weight:bold;\r
+ color:red; \r
+ width:60%; \r
+ text-align:center;\r
+ }\r
+ span.vtoy_part {\r
+ display:inline-block;\r
+ font-size:14px;\r
+ font-weight:bold;\r
+ width:15%; \r
+ text-align:right;\r
+ }\r
+ span.vtoy_secure {\r
+ display:inline-block;\r
+ font-size:20px;\r
+ font-weight:bold;\r
+ width:15%; \r
+ color:#ea991f;\r
+ text-align:center;\r
+ visibility: hidden;\r
+ }\r
+ .noselect {\r
+ -webkit-touch-callout: none; /* iOS Safari */\r
+ -webkit-user-select: none; /* Safari */\r
+ -khtml-user-select: none; /* Konqueror HTML */\r
+ -moz-user-select: none; /* Old versions of Firefox */\r
+ -ms-user-select: none; /* Internet Explorer/Edge */\r
+ user-select: none; /* Non-prefixed version, currently\r
+ supported by Chrome, Opera and Firefox */\r
+ }\r
+ span.select {\r
+ visibility:hidden;\r
+ color:green;\r
+ }\r
+ \r
+ .dropdown-submenu {\r
+ position: relative;\r
+ }\r
+\r
+ .dropdown-submenu>.dropdown-menu {\r
+ top: 0;\r
+ left: 100%;\r
+ margin-top: -6px;\r
+ margin-left: -1px;\r
+ -webkit-border-radius: 0 6px 6px 6px;\r
+ -moz-border-radius: 0 6px 6px;\r
+ border-radius: 0 6px 6px 6px;\r
+ }\r
+\r
+ .dropdown-submenu:hover>.dropdown-menu {\r
+ display: block;\r
+ }\r
+\r
+ .dropdown-submenu>a:after {\r
+ display: block;\r
+ content: " ";\r
+ float: right;\r
+ width: 0;\r
+ height: 0;\r
+ border-color: transparent;\r
+ border-style: solid;\r
+ border-width: 5px 0 5px 5px;\r
+ border-left-color: #ccc;\r
+ margin-top: 5px;\r
+ margin-right: -10px;\r
+ }\r
+\r
+ .dropdown-submenu:hover>a:after {\r
+ border-left-color: #fff;\r
+ }\r
+\r
+ .dropdown-submenu.pull-left {\r
+ float: none;\r
+ }\r
+\r
+ .dropdown-submenu.pull-left>.dropdown-menu {\r
+ left: -100%;\r
+ margin-left: 10px;\r
+ -webkit-border-radius: 6px 0 6px 6px;\r
+ -moz-border-radius: 6px 0 6px 6px;\r
+ border-radius: 6px 0 6px 6px;\r
+ }\r
+ \r
+ select.vtoyselect {\r
+ -moz-appearance: none;\r
+ -webkit-appearance: none;\r
+ appearance: none;\r
+ background-image: url("static/img/dropdown.png");\r
+ background-repeat: no-repeat;\r
+ background-position: calc(100% - 7px) 50%;\r
+ background-size: 2% auto;\r
+ border-radius:3px;\r
+ padding:0;\r
+ padding-left:15px;\r
+ }\r
+ \r
+ select.vtoyselect2 {\r
+ -moz-appearance: none;\r
+ -webkit-appearance: none;\r
+ appearance: none;\r
+ background-image: url("static/img/dropdown.png");\r
+ background-repeat: no-repeat;\r
+ background-position: calc(100% - 7px) 50%;\r
+ background-size: 10% auto;\r
+ border-radius:3px;\r
+ padding:0;\r
+ padding-left:15px;\r
+ }\r
+ \r
+ select:-moz-focusring {\r
+ color: transparent;\r
+ text-shadow: 0 0 0 #555;\r
+ }\r
+ \r
+ </style>\r
+</head>\r
+\r
+<body style="overflow:hidden">\r
+ <div class="wrapper" >\r
+ <!-- Content Wrapper. Contains page content -->\r
+ <div id='vtoy_main_div' style='width:540px; position:absolute;'>\r
+ \r
+<div class="modal" id="vtoy_set_part_cfg_modal" >\r
+ <div class="modal-dialog" id="vtoy_set_part_cfg_modal_dlg" style="position:absolute;">\r
+ <div class="modal-content">\r
+ <form id="vtoy_set_part_cfg_form" class="form-horizontal">\r
+ <div class="modal-header">\r
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">\r
+ <span aria-hidden="true">×</span>\r
+ </button>\r
+ <h4 id='part_config_dlg_title'>Configuration</h4>\r
+ </div>\r
+ <div class="modal-body">\r
+ <div class="form-group">\r
+ <div class="col-sm-10 checkbox">\r
+ <label >\r
+ <input type="checkbox" id="vtoy_preserve_space_checkbox" onclick="on_enable_preserve_space()"/>\r
+ <span id="vtoy_preserve_space_tip" style="font-size:14px;font-weight:bold;">在磁盘最后保留一段空间</span>\r
+ </label>\r
+ </div> \r
+ </div>\r
+ \r
+ <div class="form-group" style="width:520px;">\r
+ <div class="col-sm-6" style="float:left; width:350px;">\r
+ <input type="number" min="1" class="form-control" id="vtoy_preserve_space" style="font-family: couriew new;font-size: 14px;"/> \r
+ </div>\r
+ <div class="col-sm-6" style="float:left; width:150px;">\r
+ <select id="vtoy_space_unit_dropbox" class="form-control valid vtoyselect2" aria-invalid="false">\r
+ <option value="0" selected="selected">GB</option>\r
+ <option value="1">MB</option>\r
+ </select>\r
+ </div>\r
+ </div>\r
+ \r
+ <hr/>\r
+ \r
+ <div class="form-group">\r
+ <div class="col-sm-10 checkbox">\r
+ <label >\r
+ <input type="checkbox" id="vtoy_part_align_4kb"/>\r
+ <span id="vtoy_part_4kb_align_tip" style="font-size:14px;font-weight:bold;">分区按照 4KB 对齐</span>\r
+ </label>\r
+ </div> \r
+ </div>\r
+ \r
+ </div>\r
+ <div class="modal-footer">\r
+ <button type="button" id='vtoy_modal_btn_ok' class="btn btn-primary btn-flat">确定</button> \r
+ <button type="button" id='vtoy_modal_btn_cancel' class="btn btn-default btn-flat">取消</button>\r
+ </div>\r
+ </form>\r
+ </div>\r
+ </div>\r
+</div>\r
+\r
+ \r
+<!-- Main content -->\r
+<section class="content" id="vtoy-content">\r
+<div >\r
+ <div class="row" style="margin-top:-10px; width:535px; font-family:courier new; font-weight:bold;align:center;text-align:center;">\r
+ <ul class="nav nav-tabs" style="margin-left:5px;">\r
+ <li class="dropdown">\r
+ <a class="dropdown-toggle noselect" id='vtoy_option' data-toggle="dropdown" aria-expanded="false" style="cursor: pointer;">配置选项 <span class="caret"></span></a>\r
+ <ul class="dropdown-menu noselect">\r
+ <li role="presentation"><a role="menuitem" onclick="on_secure_boot()"><span id='vtoy_check_secure_boot' class="fa fa-check select"></span><span id='vtoy_menu_secure_boot'>安全启动支持</span><span class="fa fa-check select"></span></a></li> \r
+\r
+ <li class="dropdown-submenu">\r
+ <a id='vtoy_menu_secure_boot'><span class="fa fa-check select"></span><span id='vtoy_menu_part_style'>分区类型</span><span class="fa fa-check select"></span></a>\r
+ <ul class="dropdown-menu">\r
+ <li><a onclick="on_select_mbr_click()"><span id='vtoy_mbr_check' class="fa fa-check select"></span> MBR</a></li>\r
+ <li><a onclick="on_select_gpt_click()"><span id='vtoy_gpt_check' class="fa fa-check select"></span> GPT</a></li>\r
+ </ul>\r
+ </li> \r
+ <li role="presentation"><a role="menuitem" onclick="on_config_partition()"><span class="fa fa-check select"></span><span id='vtoy_menu_part_config'>分区设置</span><span class="fa fa-check select"></span></a></li> \r
+ <li role="presentation"><a role="menuitem" onclick="on_clean_ventoy()"><span class="fa fa-check select"></span><span id='vtoy_menu_clean_ventoy'>清除 Ventoy</span><span class="fa fa-check select"></span></a></li> \r
+ <li role="presentation"><a role="menuitem" onclick="on_show_all_device()"><span id='vtoy_check_show_all_dev' class="fa fa-check select"></span><span id='vtoy_menu_show_all_device'>显示所有设备</span><span class="fa fa-check select"></span></a></li> \r
+ </ul>\r
+ </li>\r
+ <li class="dropdown">\r
+ <a class="dropdown-toggle noselect" id='vtoy_language' data-toggle="dropdown" aria-expanded="false" style="cursor: pointer;;">Languages <span class="caret"></span></a>\r
+ <ul class="dropdown-menu pull-left noselect" style="height:300px;overflow:scroll" id='vtoy_language_dropbox'> \r
+ </ul>\r
+ </li>\r
+ </ul>\r
+ </div>\r
+\r
+ <div class="box-body" style=" font-weight:bold; " > \r
+ <div class="row" style="width:520px;">\r
+ <div style="float:left; width:480px;">\r
+ <span id="vtoy_dev_title" style="font-weight:bold; margin-left:2px;">设备</span>\r
+ </div>\r
+ \r
+ <div style="float:left; width:480px; margin-top:2px;" >\r
+ <select id="vtoy_dev_list" class="form-control valid vtoyselect" aria-invalid="false"> \r
+ </select>\r
+ </div>\r
+ \r
+ <div style="float:right;">\r
+ <img src="static/img/refresh.ico" alt="" id="refresh_dev_img" style="cursor: pointer;"></img>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="row" style="width:520px;margin-top:20px;"> \r
+ <div class="box box-primary box-solid" style="float:left; width:250px;">\r
+ <div class="box-header with-border" style="text-align:center;">\r
+ <h3 class="box-title" id="vtoy_local_ver_title" style="font-size: 14px;font-weight: bold;" >安装包内 Ventoy 版本</h3> \r
+ </div>\r
+ <div class="box-body no-padding" style="height:40px;">\r
+ <span class="vtoy_secure fa fa-lock" id="vtoy_select_secure_icon"></span>\r
+ <span class="vtoy_ver" id="vtoy_local_ver"></span>\r
+ <span class="vtoy_part" id="vtoy_local_part_style"></span>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="box box-primary box-solid" style="float:right; width:250px;">\r
+ <div class="box-header with-border" style="text-align:center;">\r
+ <h3 class="box-title" id="vtoy_dev_ver_title" style="font-size: 14px;font-weight: bold;" >设备内部 Ventoy 版本</h3> \r
+ </div>\r
+ <div class="box-body no-padding" style="height:40px;">\r
+ <span class="vtoy_secure fa fa-lock" id="vtoy_dev_secure_icon"></span>\r
+ <span class="vtoy_ver" id="vtoy_dev_ver"></span>\r
+ <span class="vtoy_part" id="vtoy_dev_part_style"></span>\r
+ </div>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="row" style="width:520px;">\r
+ <span id="vtoy_status_title" style="font-weight:bold; margin-left:2px;">状态 - 准备就绪</span>\r
+ <div class="progress">\r
+ <div id='vtoy_progress_bar' class="progress-bar progress-bar-success progress-bar-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"> \r
+ </div>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="row" style="width:520px;margin-top:2px;">\r
+ <button id="VtoyBtnInstall" onclick="on_vtoy_install()" class="btn btn-default" style="font-weight:bold; width:150px;margin-left:70px;">安装</button>\r
+ <button id="VtoyBtnUpdate" onclick="on_vtoy_update()" class="btn btn-default" style="font-weight:bold; width:150px;margin-left:70px;">升级</button>\r
+ </div>\r
+ </div> \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+</div>\r
+ </section>\r
+ <!-- /.content -->\r
+ </div>\r
+ \r
+ <!-- /.content-wrapper -->\r
+ </div>\r
+ \r
+<script type="text/javascript">\r
+ \r
+ var vtoy_in_progress = false;\r
+ var vtoy_app_width = 550;\r
+ var vtoy_app_height = 400;\r
+ var vtoy_cur_language;\r
+ var vtoy_cur_lang_index = -1;\r
+ var vtoy_client_language;\r
+ var vtoy_selected_part_style = 0;\r
+ var vtoy_current_token = 'xx';\r
+ var vtoy_cur_dev_list;\r
+ var vtoy_cur_process_disk_name;\r
+ var vtoy_chrome_app_mode = (window.location.href.indexOf('chrome-app') >= 0) ? 1 : 0;\r
+\r
+ function sort_language_list() {\r
+ var tmp;\r
+ for (var i = 0; i < vtoy_language_data.length; i++) {\r
+ for (var j = i + 1; j < vtoy_language_data.length; j++) {\r
+ if (vtoy_language_data[i].name === 'Chinese Simplified (简体中文)') {\r
+ break;\r
+ }\r
+ else if (vtoy_language_data[j].name === 'Chinese Simplified (简体中文)' || vtoy_language_data[i].name > vtoy_language_data[j].name) {\r
+ tmp = vtoy_language_data[i];\r
+ vtoy_language_data[i] = vtoy_language_data[j];\r
+ vtoy_language_data[j] = tmp;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ function fill_language_list() {\r
+ var html;\r
+ var dropbox = $('ul#vtoy_language_dropbox'); \r
+ for (var i = 0; i < vtoy_language_data.length; i++) {\r
+ html = '<li role="presentation"><a onclick="on_language_click(' + i + ')" role="menuitem" tabindex="' + i + '">' + \r
+ '<span id="vtoy_check_' + i + '" class="fa fa-check" style="visibility:hidden;color:green;" ></span> ' + \r
+ vtoy_language_data[i].name + '</a></li>';\r
+ dropbox.append(html);\r
+ }\r
+ }\r
+\r
+ function update_language(lastIndex, newIndex) {\r
+ if (lastIndex >= 0) {\r
+ $('span#vtoy_check_' + lastIndex).css("visibility","hidden");\r
+ } \r
+ $('span#vtoy_check_' + newIndex).css("visibility","visible");\r
+ \r
+ vtoy_cur_lang_index = newIndex;\r
+ \r
+ $('a#vtoy_option').html(vtoy_cur_language.STR_MENU_OPTION + ' <span class="caret"></span>');\r
+ $('h3#vtoy_local_ver_title').text(vtoy_cur_language.STR_LOCAL_VER);\r
+ $('h3#vtoy_dev_ver_title').text(vtoy_cur_language.STR_DISK_VER);\r
+ $('span#vtoy_status_title').text(vtoy_cur_language.STR_STATUS);\r
+ $('span#vtoy_dev_title').text(vtoy_cur_language.STR_DEVICE);\r
+ \r
+ $('span#vtoy_menu_secure_boot').text(vtoy_cur_language.STR_MENU_SECURE_BOOT);\r
+ $('span#vtoy_menu_part_style').text(vtoy_cur_language.STR_MENU_PART_STYLE);\r
+ $('span#vtoy_menu_part_config').text(vtoy_cur_language.STR_MENU_PART_CFG);\r
+ $('span#vtoy_menu_clean_ventoy').text(vtoy_cur_language.STR_MENU_CLEAR);\r
+ $('span#vtoy_menu_show_all_device').text(vtoy_cur_language.STR_SHOW_ALL_DEV);\r
+ \r
+ $('span#vtoy_preserve_space_tip').text(vtoy_cur_language.STR_PRESERVE_SPACE); \r
+ $('span#vtoy_part_4kb_align_tip').text(vtoy_cur_language.STR_PART_ALIGN_4KB); \r
+ \r
+ $('button#VtoyBtnInstall').text(vtoy_cur_language.STR_INSTALL);\r
+ $('button#VtoyBtnUpdate').text(vtoy_cur_language.STR_UPDATE);\r
+ \r
+ $('button#vtoy_modal_btn_ok').text(vtoy_cur_language.STR_BTN_OK);\r
+ $('button#vtoy_modal_btn_cancel').text(vtoy_cur_language.STR_BTN_CANCEL);\r
+ \r
+ $('h4#part_config_dlg_title').text(vtoy_cur_language.STR_MENU_PART_CFG);\r
+ }\r
+ \r
+ function select_language_by_name(name, sendback) {\r
+ for (var j = 0; j < vtoy_language_data.length; j++) {\r
+ if (vtoy_language_data[j].name.indexOf(name) == 0) {\r
+ vtoy_cur_language = vtoy_language_data[j];\r
+ update_language(vtoy_cur_lang_index, j);\r
+ \r
+ if (sendback > 0) {\r
+ var idx = name.indexOf(' (');\r
+ if (idx >= 0) {\r
+ callVtoy({method : 'sel_language', token:vtoy_current_token, language:name.substr(0, idx)});\r
+ } else {\r
+ callVtoy({method : 'sel_language', token:vtoy_current_token, language:name});\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ function select_language_by_index(index, sendback) {\r
+ if (index < vtoy_language_data.length) {\r
+ vtoy_cur_language = vtoy_language_data[index];\r
+ update_language(vtoy_cur_lang_index, index); \r
+ \r
+ if (sendback > 0) {\r
+ var idx = vtoy_cur_language.name.indexOf(' (');\r
+ if (idx >= 0) {\r
+ callVtoy({method : 'sel_language', token:vtoy_current_token, language:vtoy_cur_language.name.substr(0, idx)});\r
+ } else {\r
+ callVtoy({method : 'sel_language', token:vtoy_current_token, language:vtoy_cur_language.name});\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ function on_language_click(index) {\r
+ if (index != vtoy_cur_lang_index) {\r
+ select_language_by_index(index, 1);\r
+ }\r
+ }\r
+ \r
+ function on_select_mbr() {\r
+ vtoy_selected_part_style = 0;\r
+ $('span#vtoy_mbr_check').css("visibility","visible");\r
+ $('span#vtoy_gpt_check').css("visibility","hidden");\r
+ $('span#vtoy_local_part_style').text('MBR'); \r
+ }\r
+ \r
+ function on_select_mbr_click() {\r
+ on_select_mbr();\r
+ callVtoy({method : 'sel_partstyle', token:vtoy_current_token, partstyle:0});\r
+ }\r
+ \r
+ function on_select_gpt() { \r
+ vtoy_selected_part_style = 1;\r
+ $('span#vtoy_mbr_check').css("visibility","hidden");\r
+ $('span#vtoy_gpt_check').css("visibility","visible");\r
+ $('span#vtoy_local_part_style').text('GPT');\r
+ }\r
+ \r
+ function on_select_gpt_click() { \r
+ on_select_gpt(); \r
+ callVtoy({method : 'sel_partstyle', token:vtoy_current_token, partstyle:1});\r
+ }\r
+\r
+ function secure_boot_check(secure) {\r
+ if (secure > 0) {\r
+ $('span#vtoy_check_secure_boot').css("visibility","visible");\r
+ $('span#vtoy_select_secure_icon').css("visibility","visible");\r
+ } else {\r
+ $('span#vtoy_check_secure_boot').css("visibility","hidden");\r
+ $('span#vtoy_select_secure_icon').css("visibility","hidden");\r
+ }\r
+ }\r
+\r
+ function on_secure_boot() {\r
+ var secure;\r
+ if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+ secure = 1;\r
+ } else {\r
+ secure = 0;\r
+ }\r
+ secure_boot_check(1 - secure);\r
+ }\r
+ \r
+ function on_show_all_device() {\r
+ if ($('span#vtoy_check_show_all_dev').css("visibility") === 'visible') {\r
+ $('span#vtoy_check_show_all_dev').css("visibility", "hidden");\r
+ } else {\r
+ $('span#vtoy_check_show_all_dev').css("visibility", "visible");\r
+ }\r
+ get_and_fill_dev_list(); \r
+ }\r
+ \r
+ \r
+ var vtoy_part_align_4kb_tmp;\r
+ var vtoy_preserve_space_checkbox_tmp;\r
+ var vtoy_preserve_space_tmp;\r
+ var vtoy_space_unit_dropbox_tmp;\r
+ \r
+ function on_config_partition() { \r
+ if (vtoy_chrome_app_mode) {\r
+ $("#vtoy_set_part_cfg_modal_dlg").css("width", "520px");\r
+ $("#vtoy_set_part_cfg_modal_dlg").css("margin-top", "-30px");\r
+ } else {\r
+ $("#vtoy_set_part_cfg_modal_dlg").css("width", "540px");\r
+ \r
+ if (document.body.clientWidth > 550) {\r
+ $("#vtoy_set_part_cfg_modal_dlg").css("left", (document.body.clientWidth - 550) / 2);\r
+ }\r
+ \r
+ if (document.body.clientHeight > 400) {\r
+ $("#vtoy_set_part_cfg_modal_dlg").css("top", (document.body.clientHeight - 400) / 2);\r
+ }\r
+ }\r
+ \r
+ vtoy_part_align_4kb_tmp = $('#vtoy_part_align_4kb').prop("checked");\r
+ vtoy_preserve_space_checkbox_tmp = $('#vtoy_preserve_space_checkbox').prop("checked");\r
+ vtoy_preserve_space_tmp = $('#vtoy_preserve_space').val();\r
+ vtoy_space_unit_dropbox_tmp = $('#vtoy_space_unit_dropbox').val();\r
+ \r
+ $('#vtoy_set_part_cfg_modal').modal({backdrop: 'static', keyboard: false});\r
+ }\r
+ \r
+ $("#vtoy_modal_btn_ok").click(function(){ \r
+ $("#vtoy_set_part_cfg_modal").modal('hide');\r
+ });\r
+ \r
+ $("#vtoy_modal_btn_cancel").click(function(){ \r
+ $("#vtoy_set_part_cfg_modal").modal('hide');\r
+ $('#vtoy_part_align_4kb').prop("checked", vtoy_part_align_4kb_tmp);\r
+ $('#vtoy_preserve_space_checkbox').prop("checked", vtoy_preserve_space_checkbox_tmp);\r
+ $('#vtoy_preserve_space').val(vtoy_preserve_space_tmp);\r
+ $('#vtoy_space_unit_dropbox').val(vtoy_space_unit_dropbox_tmp);\r
+ });\r
+ \r
+ function on_enable_preserve_space() {\r
+ if ($('#vtoy_preserve_space_checkbox').is(':checked')) {\r
+ $('#vtoy_preserve_space').attr("disabled",false);\r
+ $('#vtoy_space_unit_dropbox').attr("disabled",false);\r
+ } else {\r
+ $('#vtoy_preserve_space').attr("disabled",true);\r
+ $('#vtoy_space_unit_dropbox').attr("disabled",true);\r
+ }\r
+ }\r
+ \r
+ function ResizeWindow() {\r
+ //console.log(window.screen.width + ' [x1] ' + window.screen.height); \r
+ //console.log(vtoy_app_width + ' [x2] ' + vtoy_app_height);\r
+ //console.log((window.screen.width - vtoy_app_width) / 2 + ' [x3] ' + (window.screen.height - vtoy_app_height) / 2); \r
+ window.moveTo((window.screen.width - vtoy_app_width) / 2, (window.screen.height - vtoy_app_height) / 2);\r
+ window.resizeTo(vtoy_app_width, vtoy_app_height);\r
+ }\r
+\r
+ function MoveMainDivToCenter() {\r
+ $('#vtoy_main_div').css("left", (document.body.clientWidth - 550) / 2);\r
+ $('#vtoy_main_div').css("top",(document.body.clientHeight - 400) / 2);\r
+ }\r
+\r
+ // disable F5\r
+ function disableF5(e) {\r
+ if ((e.which || e.keyCode) == 116) {\r
+ e.preventDefault(); \r
+ };\r
+ }\r
+ \r
+ function on_dev_sel_change() {\r
+ var index = $("#vtoy_dev_list").val();\r
+ \r
+ $('span#vtoy_dev_secure_icon').css("visibility","hidden");\r
+ \r
+ if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+ return;\r
+ }\r
+ \r
+ if (vtoy_cur_dev_list[index].vtoy_valid > 0) {\r
+ $('span#vtoy_dev_ver').text(vtoy_cur_dev_list[index].vtoy_ver);\r
+ $('span#vtoy_dev_part_style').text(vtoy_cur_dev_list[index].vtoy_partstyle ? 'GPT' : 'MBR');\r
+ if (vtoy_cur_dev_list[index].vtoy_secure_boot) {\r
+ $('span#vtoy_dev_secure_icon').css("visibility","visible");\r
+ }\r
+\r
+ secure_boot_check(vtoy_cur_dev_list[index].vtoy_secure_boot);\r
+ \r
+ $('button#VtoyBtnUpdate').prop("disabled", false);\r
+ \r
+ } else {\r
+ $('span#vtoy_dev_ver').text('');\r
+ $('span#vtoy_dev_part_style').text('');\r
+ $('button#VtoyBtnUpdate').prop("disabled", true);\r
+ }\r
+ }\r
+ \r
+ $("#vtoy_dev_list").change(on_dev_sel_change);\r
+ \r
+ function ventoy_display_alert(type, vmsg) {\r
+ var titlestr;\r
+ var msgstr;\r
+ var message;\r
+ \r
+ if (type === 'error') {\r
+ titlestr = '<span class="fa fa-minus-circle" style="color:#dd4b39; font-weight:bold;"> ' + vtoy_cur_language.STR_ERROR + '</span>';\r
+ } else if (type === 'warning') {\r
+ titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+ } else {\r
+ titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+ }\r
+ \r
+ msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vmsg + '</span>';\r
+ message = msgstr.replace("#@", "<br/>");\r
+ \r
+ Modal.alert({title:titlestr, msg:message, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL });\r
+ }\r
+ \r
+ function set_progress_bar(percent) {\r
+ var per = percent + '%';\r
+ \r
+ $('#vtoy_progress_bar').css('width', per);\r
+ \r
+ if (percent === 0) {\r
+ $('span#vtoy_status_title').text(vtoy_cur_language.STR_STATUS);\r
+ } else {\r
+ var status = vtoy_cur_language.STR_STATUS;\r
+ var idx = status.indexOf('- ');\r
+ \r
+ if (idx >= 0) {\r
+ $('span#vtoy_status_title').text(status.substr(0, idx + 2) + per);\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ function progressDisableItem(in_progress) {\r
+ $('button#VtoyBtnInstall').prop("disabled", in_progress);\r
+ $('button#VtoyBtnUpdate').prop("disabled", in_progress);\r
+ }\r
+ \r
+ function queryProgress(op) {\r
+ callVtoySync({\r
+ method : 'get_percent',\r
+ token : vtoy_current_token\r
+ }, function(data) {\r
+ if (data.result === 'success') {\r
+ set_progress_bar(data.percent); \r
+ if (data.percent === 100) {\r
+ var titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+ var msgstr;\r
+\r
+ if (op === 1) {\r
+ msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_INSTALL_SUCCESS + '</span>';\r
+ } else {\r
+ msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_UPDATE_SUCCESS + '</span>';\r
+ }\r
+ var message = msgstr.replace("#@", "<br/>");\r
+\r
+ callVtoySync({\r
+ method : 'refresh_device',\r
+ token : vtoy_current_token\r
+ }, function(data) { \r
+ get_and_fill_dev_list();\r
+ \r
+ for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+ if (vtoy_cur_dev_list[i].name === vtoy_cur_process_disk_name) {\r
+ $("#vtoy_dev_list").val(i);\r
+ on_dev_sel_change();\r
+ break;\r
+ } \r
+ }\r
+ });\r
+ \r
+ Modal.alert({title:titlestr, msg:message, btnok:vtoy_cur_language.STR_BTN_OK }).on(function(e) {\r
+ vtoy_in_progress = false;\r
+ progressDisableItem(vtoy_in_progress);\r
+ set_progress_bar(0);\r
+ });\r
+ } else {\r
+ setTimeout(function() {\r
+ queryProgress(op);\r
+ }, 300);\r
+ } \r
+ } else {\r
+ \r
+ if (data.result === 'mbr2tb') {\r
+ ventoy_display_alert('error', vtoy_cur_language.STR_DISK_2TB_MBR_ERROR);\r
+ } else if (data.result === 'reserve_invalid') {\r
+ ventoy_display_alert('error', vtoy_cur_language.STR_SPACE_VAL_INVALID);\r
+ } else {\r
+ ventoy_display_alert('error', (op === 1) ? vtoy_cur_language.STR_INSTALL_FAILED : vtoy_cur_language.STR_UPDATE_FAILED);\r
+ }\r
+ \r
+ vtoy_in_progress = false;\r
+ progressDisableItem(vtoy_in_progress);\r
+ set_progress_bar(0);\r
+ }\r
+ });\r
+ }\r
+ \r
+ function install_ventoy(index, reservesize) {\r
+ var secureboot;\r
+ var curDev = vtoy_cur_dev_list[index];\r
+ var align = $('#vtoy_part_align_4kb').prop("checked");\r
+ \r
+ vtoy_cur_process_disk_name = curDev.name;\r
+ \r
+ if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+ secureboot = 1;\r
+ } else {\r
+ secureboot = 0;\r
+ }\r
+ \r
+ callVtoySync({\r
+ method : 'install',\r
+ token : vtoy_current_token,\r
+ disk : curDev.name,\r
+ partstyle : vtoy_selected_part_style,\r
+ secure_boot : secureboot,\r
+ align_4kb : align ? 1 : 0,\r
+ reserve_space : reservesize + '' \r
+ }, function(data) { \r
+ if (data.result === 'success') {\r
+ vtoy_in_progress = true;\r
+ progressDisableItem(vtoy_in_progress);\r
+ queryProgress(1);\r
+ } else {\r
+ ventoy_display_alert('error', vtoy_cur_language.STR_INSTALL_FAILED);\r
+ }\r
+ });\r
+ }\r
+ \r
+ \r
+ function on_vtoy_install() {\r
+ var model;\r
+ var reserve;\r
+ var titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+ var msgstr1 = vtoy_cur_language.STR_INSTALL_TIP.replace("#@", "<br/>");\r
+ var msgstr2 = vtoy_cur_language.STR_INSTALL_TIP2.replace("#@", "<br/>");\r
+ var index = $("#vtoy_dev_list").val();\r
+ \r
+ if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+ return;\r
+ }\r
+ \r
+ if (vtoy_in_progress) {\r
+ return;\r
+ }\r
+ \r
+ if ($('#vtoy_preserve_space_checkbox').is(':checked')) {\r
+ var valx = $('#vtoy_preserve_space').val();\r
+ if (valx > 0) {\r
+ if ($('#vtoy_space_unit_dropbox').val() > 0) {\r
+ reserve = valx * 1024 * 1024;\r
+ } else {\r
+ reserve = valx * 1024 * 1024 * 1024;\r
+ } \r
+ }\r
+ } else {\r
+ reserve = 0;\r
+ }\r
+ \r
+ var curDev = vtoy_cur_dev_list[index];\r
+ model = curDev.name + ' [' + curDev.size + '] ' + curDev.model + '<br/>'; \r
+ var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+ var msgstrex2 = '<span style="font-size:14px; font-weight:bold; color:#f39c12;"> ' + model + msgstr2 + '</span>';\r
+ \r
+ Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+ if (e) {\r
+ Modal.confirm({title:titlestr, msg:msgstrex2, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e1) {\r
+ if (e1) {\r
+ install_ventoy(index, reserve);\r
+ }\r
+ });\r
+ }\r
+ });\r
+ }\r
+\r
+\r
+ function update_ventoy(index) {\r
+ var secureboot;\r
+ var curDev = vtoy_cur_dev_list[index];\r
+ \r
+ vtoy_cur_process_disk_name = curDev.name;\r
+ \r
+ if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+ secureboot = 1;\r
+ } else {\r
+ secureboot = 0;\r
+ }\r
+ \r
+ callVtoySync({\r
+ method : 'update',\r
+ token : vtoy_current_token,\r
+ disk : curDev.name,\r
+ secure_boot : secureboot\r
+ }, function(data) { \r
+ if (data.result === 'success') {\r
+ vtoy_in_progress = true;\r
+ progressDisableItem(vtoy_in_progress);\r
+ queryProgress(2);\r
+ } else {\r
+ ventoy_display_alert('error', vtoy_cur_language.STR_UPDATE_FAILED);\r
+ }\r
+ });\r
+ }\r
+\r
+ function on_vtoy_update() {\r
+ var model;\r
+ var reserve;\r
+ var titlestr = '<span class="fa fa-info" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+ var msgstr1 = vtoy_cur_language.STR_UPDATE_TIP.replace("#@", "<br/>");\r
+ var index = $("#vtoy_dev_list").val();\r
+ \r
+ if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+ return;\r
+ }\r
+ \r
+ if (vtoy_in_progress) {\r
+ return;\r
+ }\r
+ \r
+ var curDev = vtoy_cur_dev_list[index];\r
+ model = curDev.name + ' [' + curDev.size + '] ' + curDev.model + '<br/>'; \r
+ var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+ \r
+ Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+ if (e) {\r
+ update_ventoy(index);\r
+ }\r
+ });\r
+ }\r
+ \r
+ function on_clean_ventoy() {\r
+ var model;\r
+ var reserve;\r
+ var titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+ var msgstr1 = vtoy_cur_language.STR_INSTALL_TIP.replace("#@", "<br/>");\r
+ var msgstr2 = vtoy_cur_language.STR_INSTALL_TIP2.replace("#@", "<br/>");\r
+ var index = $("#vtoy_dev_list").val();\r
+ \r
+ if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+ return;\r
+ }\r
+ \r
+ if (vtoy_in_progress) {\r
+ return;\r
+ }\r
+ \r
+ var curDev = vtoy_cur_dev_list[index];\r
+ model = curDev.name + ' [' + curDev.size + '] ' + curDev.model + '<br/>'; \r
+ var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+ var msgstrex2 = '<span style="font-size:14px; font-weight:bold; color:#f39c12;"> ' + model + msgstr2 + '</span>';\r
+ \r
+ Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+ if (e) {\r
+ Modal.confirm({title:titlestr, msg:msgstrex2, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e1) {\r
+ if (e1) {\r
+ callVtoySync({\r
+ method : 'clean',\r
+ token : vtoy_current_token,\r
+ disk : curDev.name \r
+ }, function(data) { \r
+ if (data.result === 'success') {\r
+ ventoy_display_alert('success', vtoy_cur_language.STR_CLEAR_SUCCESS);\r
+ \r
+ callVtoySync({\r
+ method : 'refresh_device',\r
+ token : vtoy_current_token\r
+ }, function(data) { \r
+ get_and_fill_dev_list();\r
+ \r
+ for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+ if (vtoy_cur_dev_list[i].name === curDev.name) {\r
+ $("#vtoy_dev_list").val(i);\r
+ on_dev_sel_change();\r
+ break;\r
+ } \r
+ }\r
+ });\r
+ \r
+ } else {\r
+ ventoy_display_alert('error', vtoy_cur_language.STR_CLEAR_FAILED);\r
+ }\r
+ });\r
+ }\r
+ });\r
+ }\r
+ });\r
+ }\r
+ \r
+ \r
+ \r
+ function fill_dev_list_dropbox() {\r
+ var model; \r
+ $('#vtoy_dev_list').empty();\r
+ $('span#vtoy_dev_secure_icon').css("visibility","hidden");\r
+ \r
+ for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+ model = vtoy_cur_dev_list[i].name + ' [' + vtoy_cur_dev_list[i].size + '] ' + vtoy_cur_dev_list[i].model; \r
+ $("#vtoy_dev_list").append("<option value='" + i + "'>" + model + "</option>");\r
+ }\r
+ \r
+ $('span#vtoy_dev_ver').text('');\r
+ $('span#vtoy_dev_part_style').text('');\r
+ \r
+ if (vtoy_cur_dev_list.length > 0) {\r
+ if (vtoy_cur_dev_list[0].vtoy_valid > 0) {\r
+ $('span#vtoy_dev_ver').text(vtoy_cur_dev_list[0].vtoy_ver);\r
+ $('span#vtoy_dev_part_style').text(vtoy_cur_dev_list[0].vtoy_partstyle ? 'GPT' : 'MBR');\r
+ if (vtoy_cur_dev_list[0].vtoy_secure_boot) {\r
+ $('span#vtoy_dev_secure_icon').css("visibility","visible");\r
+ }\r
+ \r
+ secure_boot_check(vtoy_cur_dev_list[0].vtoy_secure_boot);\r
+ \r
+ $('button#VtoyBtnUpdate').prop("disabled", false);\r
+ } else {\r
+ $('button#VtoyBtnUpdate').prop("disabled", true);\r
+ }\r
+ $('button#VtoyBtnInstall').prop("disabled", false);\r
+ } else {\r
+ $('button#VtoyBtnInstall').prop("disabled", true);\r
+ $('button#VtoyBtnUpdate').prop("disabled", true);\r
+ }\r
+ }\r
+ \r
+ function get_and_fill_dev_list() {\r
+ var showall = 0;\r
+ if ($('span#vtoy_check_show_all_dev').css("visibility") === 'visible') {\r
+ showall = 1;\r
+ }\r
+ \r
+ callVtoySync({\r
+ method : 'get_dev_list',\r
+ alldev : showall,\r
+ token : vtoy_current_token\r
+ }, function(data) { \r
+ vtoy_cur_dev_list = data.list;\r
+ fill_dev_list_dropbox();\r
+ });\r
+ }\r
+ \r
+ function on_click_refresh_device() {\r
+ if (vtoy_in_progress === true) {\r
+ return;\r
+ }\r
+ callVtoySync({\r
+ method : 'refresh_device',\r
+ token : vtoy_current_token\r
+ }, function(data) { \r
+ get_and_fill_dev_list();\r
+ });\r
+ }\r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ //SCRIPT_DEL_THIS $(document).on("keydown", disableF5);\r
+ //SCRIPT_DEL_THIS $(document).on("contextmenu",function(e){ return false; });\r
+\r
+ $('#refresh_dev_img').click(on_click_refresh_device);\r
+ $('#vtoy_part_align_4kb').prop("checked", true);\r
+ \r
+ if (vtoy_chrome_app_mode) {\r
+ window.onresize = function() {\r
+ ResizeWindow();\r
+ }\r
+ ResizeWindow();\r
+ } else {\r
+ $('#vtoy_main_div').css("border", '2px solid #f4f4f4');\r
+ window.onresize = function() {\r
+ MoveMainDivToCenter();\r
+ }\r
+ MoveMainDivToCenter();\r
+ }\r
+\r
+ sort_language_list();\r
+ fill_language_list();\r
+\r
+ if (navigator.language) {\r
+ vtoy_client_language = navigator.language.toLowerCase();\r
+ }\r
+ else {\r
+ vtoy_client_language = navigator.browserLanguage.toLowerCase();\r
+ }\r
+ \r
+ if (vtoy_client_language === 'zh-cn') {\r
+ select_language_by_index(0, 0);\r
+ } else {\r
+ select_language_by_name('English (English)', 0);\r
+ }\r
+ \r
+ on_select_mbr();\r
+ secure_boot_check(0);\r
+ \r
+ on_enable_preserve_space();\r
+ \r
+ callVtoySync({\r
+ method : 'sysinfo'\r
+ }, function(data) { \r
+ vtoy_current_token = data.token; \r
+ if (data.language.length > 0) {\r
+ select_language_by_name(data.language, 0);\r
+ }\r
+ \r
+ if (data.busy === true) {\r
+ vtoy_cur_process_disk_name = data.process_disk;\r
+ vtoy_in_progress = true;\r
+ } else {\r
+ vtoy_in_progress = false;\r
+ }\r
+ \r
+ if (data.partstyle == 1) {\r
+ on_select_gpt();\r
+ } else {\r
+ on_select_mbr();\r
+ }\r
+ \r
+ $('span#vtoy_local_ver').text(data.ventoy_ver);\r
+ \r
+ callVtoySync({\r
+ method : 'refresh_device',\r
+ token : vtoy_current_token\r
+ }, function(data) { \r
+ get_and_fill_dev_list();\r
+ });\r
+ \r
+ if (vtoy_in_progress) {\r
+ for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+ if (vtoy_cur_dev_list[i].name === vtoy_cur_process_disk_name) {\r
+ $("#vtoy_dev_list").val(i);\r
+ on_dev_sel_change();\r
+ break;\r
+ } \r
+ }\r
+ \r
+ progressDisableItem(vtoy_in_progress);\r
+ if (data.process_type === 'install') {\r
+ queryProgress(1);\r
+ } else if (data.process_type === 'update') {\r
+ queryProgress(2);\r
+ }\r
+ }\r
+ });\r
+ \r
+</script>\r
+</body>\r
+\r
+</html>\r
--- /dev/null
+/**@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic); **/
+/*!
+ * AdminLTE v2.3.0
+ * Author: Almsaeed Studio
+ * Website: Almsaeed Studio <http://almsaeedstudio.com>
+ * License: Open source - MIT
+ * Please visit http://opensource.org/licenses/MIT for more information
+!*/html,body{/*min-height:100%*/}.layout-boxed html,.layout-boxed body{height:100%}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif;font-weight:400;overflow-x:hidden;overflow-y:auto}.wrapper{min-height:100%;position:static;overflow:hidden}.wrapper:before,.wrapper:after{content:" ";display:table}.wrapper:after{clear:both}.layout-boxed .wrapper{max-width:1250px;margin:0 auto;min-height:100%;box-shadow:0 0 8px rgba(0,0,0,0.5);position:relative}.layout-boxed{background:url('../img/boxed-bg.jpg') repeat fixed}.content-wrapper,.right-side,.main-footer{-webkit-transition:-webkit-transform .3s ease-in-out,margin .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,margin .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,margin .3s ease-in-out;transition:transform .3s ease-in-out,margin .3s ease-in-out;margin-left:230px;z-index:820}.layout-top-nav .content-wrapper,.layout-top-nav .right-side,.layout-top-nav .main-footer{margin-left:0}@media (max-width:767px){.content-wrapper,.right-side,.main-footer{margin-left:0}}@media (min-width:768px){.sidebar-collapse .content-wrapper,.sidebar-collapse .right-side,.sidebar-collapse .main-footer{margin-left:0}}@media (max-width:767px){.sidebar-open .content-wrapper,.sidebar-open .right-side,.sidebar-open .main-footer{-webkit-transform:translate(230px, 0);-ms-transform:translate(230px, 0);-o-transform:translate(230px, 0);transform:translate(230px, 0)}}.content-wrapper,.right-side{min-height:100%;background-color:#ecf0f5;z-index:800}.main-footer{background:#fff;padding:15px;color:#444;border-top:1px solid #d2d6de}.fixed .main-header,.fixed .main-sidebar,.fixed .left-side{position:fixed}.fixed .main-header{top:0;right:0;left:0}.fixed .content-wrapper,.fixed .right-side{padding-top:50px}@media (max-width:767px){.fixed .content-wrapper,.fixed .right-side{padding-top:100px}}.fixed.layout-boxed .wrapper{max-width:100%}body.hold-transition .content-wrapper,body.hold-transition .right-side,body.hold-transition .main-footer,body.hold-transition .main-sidebar,body.hold-transition .left-side,body.hold-transition .main-header>.navbar,body.hold-transition .main-header .logo{-webkit-transition:none;-o-transition:none;transition:none}.content{min-height:250px;padding:15px;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif}a{color:#3c8dbc}a:hover,a:active,a:focus{outline:none;text-decoration:none;color:#72afd2}.page-header{margin:10px 0 20px 0;font-size:22px}.page-header>small{color:#666;display:block;margin-top:5px}.main-header{position:relative;max-height:100px;z-index:1030}.main-header>.navbar{-webkit-transition:margin-left .3s ease-in-out;-o-transition:margin-left .3s ease-in-out;transition:margin-left .3s ease-in-out;margin-bottom:0;margin-left:230px;border:none;min-height:50px;border-radius:0}.layout-top-nav .main-header>.navbar{margin-left:0}.main-header #navbar-search-input.form-control{background:rgba(255,255,255,0.2);border-color:transparent}.main-header #navbar-search-input.form-control:focus,.main-header #navbar-search-input.form-control:active{border-color:rgba(0,0,0,0.1);background:rgba(255,255,255,0.9)}.main-header #navbar-search-input.form-control::-moz-placeholder{color:#ccc;opacity:1}.main-header #navbar-search-input.form-control:-ms-input-placeholder{color:#ccc}.main-header #navbar-search-input.form-control::-webkit-input-placeholder{color:#ccc}.main-header .navbar-custom-menu,.main-header .navbar-right{float:right}@media (max-width:991px){.main-header .navbar-custom-menu a,.main-header .navbar-right a{color:inherit;background:transparent}}@media (max-width:767px){.main-header .navbar-right{float:none}.navbar-collapse .main-header .navbar-right{margin:7.5px -15px}.main-header .navbar-right>li{color:inherit;border:0}}.main-header .sidebar-toggle{float:left;background-color:transparent;background-image:none;padding:15px 15px;font-family:fontAwesome}.main-header .sidebar-toggle:before{content:"\f0c9"}.main-header .sidebar-toggle:hover{color:#fff}.main-header .sidebar-toggle:focus,.main-header .sidebar-toggle:active{background:transparent}.main-header .sidebar-toggle .icon-bar{display:none}.main-header .navbar .nav>li.user>a>.fa,.main-header .navbar .nav>li.user>a>.glyphicon,.main-header .navbar .nav>li.user>a>.ion{margin-right:5px}.main-header .navbar .nav>li>a>.label{position:absolute;top:9px;right:7px;text-align:center;font-size:9px;padding:2px 3px;line-height:.9}.main-header .logo{-webkit-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out;display:block;float:left;height:50px;font-size:20px;line-height:50px;text-align:center;width:230px;font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif;padding:0 15px;font-weight:300;overflow:hidden}.main-header .logo .logo-lg{display:block}.main-header .logo .logo-mini{display:none}.main-header .navbar-brand{color:#fff}.content-header{position:relative;padding:15px 15px 0 15px}.content-header>h1{margin:0;font-size:24px}.content-header>h1>small{font-size:15px;display:inline-block;padding-left:4px;font-weight:300}.content-header>.breadcrumb{float:right;background:transparent;margin-top:0;margin-bottom:0;font-size:12px;padding:7px 5px;position:absolute;top:15px;right:10px;border-radius:2px}.content-header>.breadcrumb>li>a{color:#444;text-decoration:none;display:inline-block}.content-header>.breadcrumb>li>a>.fa,.content-header>.breadcrumb>li>a>.glyphicon,.content-header>.breadcrumb>li>a>.ion{margin-right:5px}.content-header>.breadcrumb>li+li:before{content:'>\00a0'}@media (max-width:991px){.content-header>.breadcrumb{position:relative;margin-top:5px;top:0;right:0;float:none;background:#d2d6de;padding-left:10px}.content-header>.breadcrumb li:before{color:#97a0b3}}.navbar-toggle{color:#fff;border:0;margin:0;padding:15px 15px}@media (max-width:991px){.navbar-custom-menu .navbar-nav>li{float:left}.navbar-custom-menu .navbar-nav{margin:0;float:left}.navbar-custom-menu .navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px}}@media (max-width:767px){.main-header{position:relative}.main-header .logo,.main-header .navbar{width:100%;float:none}.main-header .navbar{margin:0}.main-header .navbar-custom-menu{float:right}}@media (max-width:991px){.navbar-collapse.pull-left{float:none!important}.navbar-collapse.pull-left+.navbar-custom-menu{display:block;position:absolute;top:0;right:40px}}.main-sidebar,.left-side{position:absolute;top:0;left:0;padding-top:50px;min-height:100%;width:230px;z-index:810;-webkit-transition:-webkit-transform .3s ease-in-out,width .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,width .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,width .3s ease-in-out;transition:transform .3s ease-in-out,width .3s ease-in-out}@media (max-width:767px){.main-sidebar,.left-side{padding-top:100px}}@media (max-width:767px){.main-sidebar,.left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (min-width:768px){.sidebar-collapse .main-sidebar,.sidebar-collapse .left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (max-width:767px){.sidebar-open .main-sidebar,.sidebar-open .left-side{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}}.sidebar{padding-bottom:10px}.sidebar-form input:focus{border-color:transparent}.user-panel{position:relative;width:100%;padding:10px;overflow:hidden}.user-panel:before,.user-panel:after{content:" ";display:table}.user-panel:after{clear:both}.user-panel>.image>img{width:100%;max-width:45px;height:auto}.user-panel>.info{padding:5px 5px 5px 15px;line-height:1;position:absolute;left:55px}.user-panel>.info>p{font-weight:600;margin-bottom:9px}.user-panel>.info>a{text-decoration:none;padding-right:5px;margin-top:3px;font-size:11px}.user-panel>.info>a>.fa,.user-panel>.info>a>.ion,.user-panel>.info>a>.glyphicon{margin-right:3px}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu>li{position:relative;margin:0;padding:0}.sidebar-menu>li>a{padding:12px 5px 12px 15px;display:block}.sidebar-menu>li>a>.fa,.sidebar-menu>li>a>.glyphicon,.sidebar-menu>li>a>.ion{width:20px}.sidebar-menu>li .label,.sidebar-menu>li .badge{margin-top:3px;margin-right:5px}.sidebar-menu li.header{padding:10px 25px 10px 15px;font-size:12px}.sidebar-menu li>a>.fa-angle-left{width:auto;height:auto;padding:0;margin-right:10px;margin-top:3px}.sidebar-menu li.active>a>.fa-angle-left{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu li.active>.treeview-menu{display:block}.sidebar-menu .treeview-menu{display:none;list-style:none;padding:0;margin:0;padding-left:5px}.sidebar-menu .treeview-menu .treeview-menu{padding-left:20px}.sidebar-menu .treeview-menu>li{margin:0}.sidebar-menu .treeview-menu>li>a{padding:5px 5px 5px 15px;display:block;font-size:14px}.sidebar-menu .treeview-menu>li>a>.fa,.sidebar-menu .treeview-menu>li>a>.glyphicon,.sidebar-menu .treeview-menu>li>a>.ion{width:20px}.sidebar-menu .treeview-menu>li>a>.fa-angle-left,.sidebar-menu .treeview-menu>li>a>.fa-angle-down{width:auto}@media (min-width:768px){.sidebar-mini.sidebar-collapse .content-wrapper,.sidebar-mini.sidebar-collapse .right-side,.sidebar-mini.sidebar-collapse .main-footer{margin-left:50px!important;z-index:840}.sidebar-mini.sidebar-collapse .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);width:50px!important;z-index:850}.sidebar-mini.sidebar-collapse .sidebar-menu>li{position:relative}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a{margin-right:0}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span{border-top-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:not(.treeview)>a>span{border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{padding-top:5px;padding-bottom:5px;border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span:not(.pull-right),.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{display:block!important;position:absolute;width:180px;left:50px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span{top:0;margin-left:-3px;padding:12px 5px 12px 20px;background-color:inherit}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{top:44px;margin-left:0}.sidebar-mini.sidebar-collapse .main-sidebar .user-panel>.info,.sidebar-mini.sidebar-collapse .sidebar-form,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span,.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu li.header{display:none!important;-webkit-transform:translateZ(0)}.sidebar-mini.sidebar-collapse .main-header .logo{width:50px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-mini{display:block;margin-left:-15px;margin-right:-15px;font-size:18px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-lg{display:none}.sidebar-mini.sidebar-collapse .main-header .navbar{margin-left:50px}}.sidebar-menu,.main-sidebar .user-panel,.sidebar-menu>li.header{white-space:nowrap;overflow:hidden}.sidebar-menu:hover{overflow:visible}.sidebar-form,.sidebar-menu>li.header{overflow:hidden;text-overflow:clip}.sidebar-menu li>a{position:relative}.sidebar-menu li>a>.pull-right{position:absolute;top:50%;right:10px;margin-top:-7px}.control-sidebar-bg{position:fixed;z-index:1000;bottom:0}.control-sidebar-bg,.control-sidebar{top:0;right:-230px;width:230px;-webkit-transition:right .3s ease-in-out;-o-transition:right .3s ease-in-out;transition:right .3s ease-in-out}.control-sidebar{position:absolute;padding-top:50px;z-index:1010}@media (max-width:768px){.control-sidebar{padding-top:100px}}.control-sidebar>.tab-content{padding:10px 15px}.control-sidebar.control-sidebar-open,.control-sidebar.control-sidebar-open+.control-sidebar-bg{right:0}.control-sidebar-open .control-sidebar-bg,.control-sidebar-open .control-sidebar{right:0}@media (min-width:768px){.control-sidebar-open .content-wrapper,.control-sidebar-open .right-side,.control-sidebar-open .main-footer{margin-right:230px}}.nav-tabs.control-sidebar-tabs>li:first-of-type>a,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:hover,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:focus{border-left-width:0}.nav-tabs.control-sidebar-tabs>li>a{border-radius:0}.nav-tabs.control-sidebar-tabs>li>a,.nav-tabs.control-sidebar-tabs>li>a:hover{border-top:none;border-right:none;border-left:1px solid transparent;border-bottom:1px solid transparent}.nav-tabs.control-sidebar-tabs>li>a .icon{font-size:16px}.nav-tabs.control-sidebar-tabs>li.active>a,.nav-tabs.control-sidebar-tabs>li.active>a:hover,.nav-tabs.control-sidebar-tabs>li.active>a:focus,.nav-tabs.control-sidebar-tabs>li.active>a:active{border-top:none;border-right:none;border-bottom:none}@media (max-width:768px){.nav-tabs.control-sidebar-tabs{display:table}.nav-tabs.control-sidebar-tabs>li{display:table-cell}}.control-sidebar-heading{font-weight:400;font-size:16px;padding:10px 0;margin-bottom:10px}.control-sidebar-subheading{display:block;font-weight:400;font-size:14px}.control-sidebar-menu{list-style:none;padding:0;margin:0 -15px}.control-sidebar-menu>li>a{display:block;padding:10px 15px}.control-sidebar-menu>li>a:before,.control-sidebar-menu>li>a:after{content:" ";display:table}.control-sidebar-menu>li>a:after{clear:both}.control-sidebar-menu>li>a>.control-sidebar-subheading{margin-top:0}.control-sidebar-menu .menu-icon{float:left;width:35px;height:35px;border-radius:50%;text-align:center;line-height:35px}.control-sidebar-menu .menu-info{margin-left:45px;margin-top:3px}.control-sidebar-menu .menu-info>.control-sidebar-subheading{margin:0}.control-sidebar-menu .menu-info>p{margin:0;font-size:11px}.control-sidebar-menu .progress{margin:0}.control-sidebar-dark{color:#b8c7ce}.control-sidebar-dark,.control-sidebar-dark+.control-sidebar-bg{background:#222d32}.control-sidebar-dark .nav-tabs.control-sidebar-tabs{border-bottom:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a{background:#181f23;color:#b8c7ce}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#141a1d;border-bottom-color:#141a1d}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:active{background:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover{color:#fff}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#222d32;color:#fff}.control-sidebar-dark .control-sidebar-heading,.control-sidebar-dark .control-sidebar-subheading{color:#fff}.control-sidebar-dark .control-sidebar-menu>li>a:hover{background:#1e282c}.control-sidebar-dark .control-sidebar-menu>li>a .menu-info>p{color:#b8c7ce}.control-sidebar-light{color:#5e5e5e}.control-sidebar-light,.control-sidebar-light+.control-sidebar-bg{background:#f9fafc;border-left:1px solid #d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs{border-bottom:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a{background:#e8ecf4;color:#444}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#d2d6de;border-bottom-color:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:active{background:#eff1f7}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#f9fafc;color:#111}.control-sidebar-light .control-sidebar-heading,.control-sidebar-light .control-sidebar-subheading{color:#111}.control-sidebar-light .control-sidebar-menu{margin-left:-14px}.control-sidebar-light .control-sidebar-menu>li>a:hover{background:#f4f4f5}.control-sidebar-light .control-sidebar-menu>li>a .menu-info>p{color:#5e5e5e}.dropdown-menu{box-shadow:none;border-color:#eee}.dropdown-menu>li>a{color:#777}.dropdown-menu>li>a>.glyphicon,.dropdown-menu>li>a>.fa,.dropdown-menu>li>a>.ion{margin-right:10px}.dropdown-menu>li>a:hover{background-color:#e1e3e9;color:#333}.dropdown-menu>.divider{background-color:#eee}.navbar-nav>.notifications-menu>.dropdown-menu,.navbar-nav>.messages-menu>.dropdown-menu,.navbar-nav>.tasks-menu>.dropdown-menu{width:280px;padding:0 0 0 0;margin:0;top:100%}.navbar-nav>.notifications-menu>.dropdown-menu>li,.navbar-nav>.messages-menu>.dropdown-menu>li,.navbar-nav>.tasks-menu>.dropdown-menu>li{position:relative}.navbar-nav>.notifications-menu>.dropdown-menu>li.header,.navbar-nav>.messages-menu>.dropdown-menu>li.header,.navbar-nav>.tasks-menu>.dropdown-menu>li.header{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;background-color:#ffffff;padding:7px 10px;border-bottom:1px solid #f4f4f4;color:#444444;font-size:14px}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;font-size:12px;background-color:#fff;padding:7px 10px;border-bottom:1px solid #eeeeee;color:#444!important;text-align:center}@media (max-width:991px){.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{background:#fff!important;color:#444!important}}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a:hover{text-decoration:none;font-weight:normal}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu,.navbar-nav>.messages-menu>.dropdown-menu>li .menu,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu{max-height:200px;margin:0;padding:0;list-style:none;overflow-x:hidden}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{display:block;white-space:nowrap;border-bottom:1px solid #f4f4f4}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a:hover{background:#f4f4f4;text-decoration:none}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a{color:#444444;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:10px}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.glyphicon,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.fa,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.ion{width:20px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a{margin:0;padding:10px 10px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>div>img{margin:auto 10px auto auto;width:40px;height:40px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4{padding:0;margin:0 0 0 45px;color:#444444;font-size:15px;position:relative}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4>small{color:#999999;font-size:10px;position:absolute;top:0;right:0}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>p{margin:0 0 0 45px;font-size:12px;color:#888888}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:before,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{content:" ";display:table}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{clear:both}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{padding:10px}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>h3{font-size:14px;padding:0;margin:0 0 10px 0;color:#666666}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>.progress{padding:0;margin:0}.navbar-nav>.user-menu>.dropdown-menu{border-top-right-radius:0;border-top-left-radius:0;padding:1px 0 0 0;border-top-width:0;width:280px}.navbar-nav>.user-menu>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header{height:175px;padding:10px;text-align:center}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img{z-index:5;height:90px;width:90px;border:3px solid;border-color:transparent;border-color:rgba(255,255,255,0.2)}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p{z-index:5;color:#fff;color:rgba(255,255,255,0.8);font-size:17px;margin-top:10px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p>small{display:block;font-size:12px}.navbar-nav>.user-menu>.dropdown-menu>.user-body{padding:15px;border-bottom:1px solid #f4f4f4;border-top:1px solid #dddddd}.navbar-nav>.user-menu>.dropdown-menu>.user-body:before,.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-body a{color:#444 !important}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-body a{background:#fff !important;color:#444 !important}}.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background-color:#f9f9f9;padding:10px}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:before,.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default{color:#666666}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default:hover{background-color:#f9f9f9}}.navbar-nav>.user-menu .user-image{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px;margin-top:-2px}@media (max-width:767px){.navbar-nav>.user-menu .user-image{float:none;margin-right:0;margin-top:-8px;line-height:10px}}.open:not(.dropup)>.animated-dropdown-menu{backface-visibility:visible !important;-webkit-animation:flipInX .7s both;-o-animation:flipInX .7s both;animation:flipInX .7s both}@keyframes flipInX{0%{transform:perspective(400px) rotate3d(1, 0, 0, 90deg);transition-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotate3d(1, 0, 0, -20deg);transition-timing-function:ease-in}60%{transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{transform:perspective(400px)}}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 90deg);-webkit-transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -20deg);-webkit-transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{-webkit-transform:perspective(400px)}}.navbar-custom-menu>.navbar-nav>li{position:relative}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:0;left:auto}@media (max-width:991px){.navbar-custom-menu>.navbar-nav{float:right}.navbar-custom-menu>.navbar-nav>li{position:static}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:5%;left:auto;border:1px solid #ddd;background:#fff}}.form-control{border-radius:0;box-shadow:none;border-color:#d2d6de}.form-control:focus{border-color:#3c8dbc;box-shadow:none}.form-control::-moz-placeholder,.form-control:-ms-input-placeholder,.form-control::-webkit-input-placeholder{color:#bbb;opacity:1}.form-control:not(select){-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-group.has-success label{color:#00a65a}.form-group.has-success .form-control{border-color:#00a65a;box-shadow:none}.form-group.has-warning label{color:#f39c12}.form-group.has-warning .form-control{border-color:#f39c12;box-shadow:none}.form-group.has-error label{color:#dd4b39}.form-group.has-error .form-control{border-color:#dd4b39;box-shadow:none}.input-group .input-group-addon{border-radius:0;border-color:#d2d6de;background-color:#fff}.btn-group-vertical .btn.btn-flat:first-of-type,.btn-group-vertical .btn.btn-flat:last-of-type{border-radius:0}.icheck>label{padding-left:0}.form-control-feedback.fa{line-height:34px}.input-lg+.form-control-feedback.fa,.input-group-lg+.form-control-feedback.fa,.form-group-lg .form-control+.form-control-feedback.fa{line-height:46px}.input-sm+.form-control-feedback.fa,.input-group-sm+.form-control-feedback.fa,.form-group-sm .form-control+.form-control-feedback.fa{line-height:30px}.progress,.progress>.progress-bar{-webkit-box-shadow:none;box-shadow:none}.progress,.progress>.progress-bar,.progress .progress-bar,.progress>.progress-bar .progress-bar{border-radius:1px}.progress.sm,.progress-sm{height:10px}.progress.sm,.progress-sm,.progress.sm .progress-bar,.progress-sm .progress-bar{border-radius:1px}.progress.xs,.progress-xs{height:7px}.progress.xs,.progress-xs,.progress.xs .progress-bar,.progress-xs .progress-bar{border-radius:1px}.progress.xxs,.progress-xxs{height:3px}.progress.xxs,.progress-xxs,.progress.xxs .progress-bar,.progress-xxs .progress-bar{border-radius:1px}.progress.vertical{position:relative;width:30px;height:200px;display:inline-block;margin-right:10px}.progress.vertical>.progress-bar{width:100%;position:absolute;bottom:0}.progress.vertical.sm,.progress.vertical.progress-sm{width:20px}.progress.vertical.xs,.progress.vertical.progress-xs{width:10px}.progress.vertical.xxs,.progress.vertical.progress-xxs{width:3px}.progress-group .progress-text{font-weight:600}.progress-group .progress-number{float:right}.table tr>td .progress{margin:0}.progress-bar-light-blue,.progress-bar-primary{background-color:#3c8dbc}.progress-striped .progress-bar-light-blue,.progress-striped .progress-bar-primary{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-green,.progress-bar-success{background-color:#00a65a}.progress-striped .progress-bar-green,.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-aqua,.progress-bar-info{background-color:#00c0ef}.progress-striped .progress-bar-aqua,.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-yellow,.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-yellow,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-red,.progress-bar-danger{background-color:#dd4b39}.progress-striped .progress-bar-red,.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.small-box{border-radius:2px;position:relative;display:block;margin-bottom:20px;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.small-box>.inner{padding:10px}.small-box>.small-box-footer{position:relative;text-align:center;padding:3px 0;color:#fff;color:rgba(255,255,255,0.8);display:block;z-index:10;background:rgba(0,0,0,0.1);text-decoration:none}.small-box>.small-box-footer:hover{color:#fff;background:rgba(0,0,0,0.15)}.small-box h3{font-size:38px;font-weight:bold;margin:0 0 10px 0;white-space:nowrap;padding:0}.small-box p{font-size:15px}.small-box p>small{display:block;color:#f9f9f9;font-size:13px;margin-top:5px}.small-box h3,.small-box p{z-index:5px}.small-box .icon{-webkit-transition:all .3s linear;-o-transition:all .3s linear;transition:all .3s linear;position:absolute;top:-10px;right:10px;z-index:0;font-size:90px;color:rgba(0,0,0,0.15)}.small-box:hover{text-decoration:none;color:#f9f9f9}.small-box:hover .icon{font-size:95px}@media (max-width:767px){.small-box{text-align:center}.small-box .icon{display:none}.small-box p{font-size:12px}}.box{position:relative;border-radius:3px;background:#ffffff;border-top:3px solid #d2d6de;margin-bottom:20px;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.box.box-primary{border-top-color:#3c8dbc}.box.box-info{border-top-color:#00c0ef}.box.box-danger{border-top-color:#dd4b39}.box.box-warning{border-top-color:#f39c12}.box.box-success{border-top-color:#00a65a}.box.box-default{border-top-color:#d2d6de}.box.collapsed-box .box-body,.box.collapsed-box .box-footer{display:none}.box .nav-stacked>li{border-bottom:1px solid #f4f4f4;margin:0}.box .nav-stacked>li:last-of-type{border-bottom:none}.box.height-control .box-body{max-height:300px;overflow:auto}.box .border-right{border-right:1px solid #f4f4f4}.box .border-left{border-left:1px solid #f4f4f4}.box.box-solid{border-top:0}.box.box-solid>.box-header .btn.btn-default{background:transparent}.box.box-solid>.box-header .btn:hover,.box.box-solid>.box-header a:hover{background:rgba(0,0,0,0.1)}.box.box-solid.box-default{border:1px solid #d2d6de}.box.box-solid.box-default>.box-header{color:#444;background:#d2d6de;background-color:#d2d6de}.box.box-solid.box-default>.box-header a,.box.box-solid.box-default>.box-header .btn{color:#444}.box.box-solid.box-primary{border:1px solid #3c8dbc}.box.box-solid.box-primary>.box-header{color:#fff;background:#3c8dbc;background-color:#3c8dbc}.box.box-solid.box-primary>.box-header a,.box.box-solid.box-primary>.box-header .btn{color:#fff}.box.box-solid.box-info{border:1px solid #00c0ef}.box.box-solid.box-info>.box-header{color:#fff;background:#00c0ef;background-color:#00c0ef}.box.box-solid.box-info>.box-header a,.box.box-solid.box-info>.box-header .btn{color:#fff}.box.box-solid.box-danger{border:1px solid #dd4b39}.box.box-solid.box-danger>.box-header{color:#fff;background:#dd4b39;background-color:#dd4b39}.box.box-solid.box-danger>.box-header a,.box.box-solid.box-danger>.box-header .btn{color:#fff}.box.box-solid.box-warning{border:1px solid #f39c12}.box.box-solid.box-warning>.box-header{color:#fff;background:#f39c12;background-color:#f39c12}.box.box-solid.box-warning>.box-header a,.box.box-solid.box-warning>.box-header .btn{color:#fff}.box.box-solid.box-success{border:1px solid #00a65a}.box.box-solid.box-success>.box-header{color:#fff;background:#00a65a;background-color:#00a65a}.box.box-solid.box-success>.box-header a,.box.box-solid.box-success>.box-header .btn{color:#fff}.box.box-solid>.box-header>.box-tools .btn{border:0;box-shadow:none}.box.box-solid[class*='bg']>.box-header{color:#fff}.box .box-group>.box{margin-bottom:5px}.box .knob-label{text-align:center;color:#333;font-weight:100;font-size:12px;margin-bottom:0.3em}.box>.overlay,.overlay-wrapper>.overlay,.box>.loading-img,.overlay-wrapper>.loading-img{position:absolute;top:0;left:0;width:100%;height:100%}.box .overlay,.overlay-wrapper .overlay{z-index:50;background:rgba(255,255,255,0.7);border-radius:3px}.box .overlay>.fa,.overlay-wrapper .overlay>.fa{position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px;color:#000;font-size:30px}.box .overlay.dark,.overlay-wrapper .overlay.dark{background:rgba(0,0,0,0.5)}.box-header:before,.box-body:before,.box-footer:before,.box-header:after,.box-body:after,.box-footer:after{content:" ";display:table}.box-header:after,.box-body:after,.box-footer:after{clear:both}.box-header{color:#444;display:block;padding:10px;position:relative}.box-header.with-border{border-bottom:1px solid #f4f4f4}.collapsed-box .box-header.with-border{border-bottom:none}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion,.box-header .box-title{display:inline-block;font-size:18px;margin:0;line-height:1}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion{margin-right:5px}.box-header>.box-tools{position:absolute;right:10px;top:5px}.box-header>.box-tools [data-toggle="tooltip"]{position:relative}.box-header>.box-tools.pull-right .dropdown-menu{right:0;left:auto}.btn-box-tool{padding:5px;font-size:12px;background:transparent;color:#97a0b3}.open .btn-box-tool,.btn-box-tool:hover{color:#606c84}.btn-box-tool.btn:active{box-shadow:none}.box-body{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;padding:10px}.no-header .box-body{border-top-right-radius:3px;border-top-left-radius:3px}.box-body>.table{margin-bottom:0}.box-body .fc{margin-top:5px}.box-body .full-width-chart{margin:-19px}.box-body.no-padding .full-width-chart{margin:-9px}.box-body .box-pane{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:3px}.box-body .box-pane-right{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:0}.box-footer{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;border-top:1px solid #f4f4f4;padding:10px;background-color:#fff}.chart-legend{margin:10px 0}@media (max-width:991px){.chart-legend>li{float:left;margin-right:10px}}.box-comments{background:#f7f7f7}.box-comments .box-comment{padding:8px 0;border-bottom:1px solid #eee}.box-comments .box-comment:before,.box-comments .box-comment:after{content:" ";display:table}.box-comments .box-comment:after{clear:both}.box-comments .box-comment:last-of-type{border-bottom:0}.box-comments .box-comment:first-of-type{padding-top:0}.box-comments .box-comment img{float:left}.box-comments .comment-text{margin-left:40px;color:#555}.box-comments .username{color:#444;display:block;font-weight:600}.box-comments .text-muted{font-weight:400;font-size:12px}.todo-list{margin:0;padding:0;list-style:none;overflow:auto}.todo-list>li{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:2px;border-left:2px solid #e6e7e8;color:#444}.todo-list>li:last-of-type{margin-bottom:0}.todo-list>li>input[type='checkbox']{margin:0 10px 0 5px}.todo-list>li .text{display:inline-block;margin-left:5px;font-weight:600}.todo-list>li .label{margin-left:10px;font-size:9px}.todo-list>li .tools{display:none;float:right;color:#dd4b39}.todo-list>li .tools>.fa,.todo-list>li .tools>.glyphicon,.todo-list>li .tools>.ion{margin-right:5px;cursor:pointer}.todo-list>li:hover .tools{display:inline-block}.todo-list>li.done{color:#999}.todo-list>li.done .text{text-decoration:line-through;font-weight:500}.todo-list>li.done .label{background:#d2d6de !important}.todo-list .danger{border-left-color:#dd4b39}.todo-list .warning{border-left-color:#f39c12}.todo-list .info{border-left-color:#00c0ef}.todo-list .success{border-left-color:#00a65a}.todo-list .primary{border-left-color:#3c8dbc}.todo-list .handle{display:inline-block;cursor:move;margin:0 5px}.chat{padding:5px 20px 5px 10px}.chat .item{margin-bottom:10px}.chat .item:before,.chat .item:after{content:" ";display:table}.chat .item:after{clear:both}.chat .item>img{width:40px;height:40px;border:2px solid transparent;border-radius:50%}.chat .item>.online{border:2px solid #00a65a}.chat .item>.offline{border:2px solid #dd4b39}.chat .item>.message{margin-left:55px;margin-top:-40px}.chat .item>.message>.name{display:block;font-weight:600}.chat .item>.attachment{border-radius:3px;background:#f4f4f4;margin-left:65px;margin-right:15px;padding:10px}.chat .item>.attachment>h4{margin:0 0 5px 0;font-weight:600;font-size:14px}.chat .item>.attachment>p,.chat .item>.attachment>.filename{font-weight:600;font-size:13px;font-style:italic;margin:0}.chat .item>.attachment:before,.chat .item>.attachment:after{content:" ";display:table}.chat .item>.attachment:after{clear:both}.box-input{max-width:200px}.modal .panel-body{color:#444}.info-box{display:block;min-height:90px;background:#fff;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:2px;margin-bottom:15px}.info-box small{font-size:14px}.info-box .progress{background:rgba(0,0,0,0.2);margin:5px -10px 5px -10px;height:2px}.info-box .progress,.info-box .progress .progress-bar{border-radius:0}.info-box .progress .progress-bar{background:#fff}.info-box-icon{border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px;display:block;float:left;height:90px;width:90px;text-align:center;font-size:45px;line-height:90px;background:rgba(0,0,0,0.2)}.info-box-icon>img{max-width:100%}.info-box-content{padding:5px 10px;margin-left:90px}.info-box-number{display:block;font-weight:bold;font-size:18px}.progress-description,.info-box-text{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.info-box-text{text-transform:uppercase}.info-box-more{display:block}.progress-description{margin:0}.timeline{position:relative;margin:0 0 30px 0;padding:0;list-style:none}.timeline:before{content:'';position:absolute;top:0;bottom:0;width:4px;background:#ddd;left:31px;margin:0;border-radius:2px}.timeline>li{position:relative;margin-right:10px;margin-bottom:15px}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-item{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;margin-top:0;background:#fff;color:#444;margin-left:60px;margin-right:15px;padding:0;position:relative}.timeline>li>.timeline-item>.time{color:#999;float:right;padding:10px;font-size:12px}.timeline>li>.timeline-item>.timeline-header{margin:0;color:#555;border-bottom:1px solid #f4f4f4;padding:10px;font-size:16px;line-height:1.1}.timeline>li>.timeline-item>.timeline-header>a{font-weight:600}.timeline>li>.timeline-item>.timeline-body,.timeline>li>.timeline-item>.timeline-footer{padding:10px}.timeline>li>.fa,.timeline>li>.glyphicon,.timeline>li>.ion{width:30px;height:30px;font-size:15px;line-height:30px;position:absolute;color:#666;background:#d2d6de;border-radius:50%;text-align:center;left:18px;top:0}.timeline>.time-label>span{font-weight:600;padding:5px;display:inline-block;background-color:#fff;border-radius:4px}.timeline-inverse>li>.timeline-item{background:#f0f0f0;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none}.timeline-inverse>li>.timeline-item>.timeline-header{border-bottom-color:#ddd}.btn{border-radius:3px;-webkit-box-shadow:none;box-shadow:none;border:1px solid transparent}.btn.uppercase{text-transform:uppercase}.btn.btn-flat{border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-width:1px}.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:focus{outline:none}.btn.btn-file{position:relative;overflow:hidden}.btn.btn-file>input[type='file']{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;opacity:0;filter:alpha(opacity=0);outline:none;background:white;cursor:inherit;display:block}.btn-default{background-color:#f4f4f4;color:#444;border-color:#ddd}.btn-default:hover,.btn-default:active,.btn-default.hover{background-color:#e7e7e7}.btn-primary{background-color:#3c8dbc;border-color:#367fa9}.btn-primary:hover,.btn-primary:active,.btn-primary.hover{background-color:#367fa9}.btn-success{background-color:#00a65a;border-color:#008d4c}.btn-success:hover,.btn-success:active,.btn-success.hover{background-color:#008d4c}.btn-info{background-color:#00c0ef;border-color:#00acd6}.btn-info:hover,.btn-info:active,.btn-info.hover{background-color:#00acd6}.btn-danger{background-color:#dd4b39;border-color:#d73925}.btn-danger:hover,.btn-danger:active,.btn-danger.hover{background-color:#d73925}.btn-warning{background-color:#f39c12;border-color:#e08e0b}.btn-warning:hover,.btn-warning:active,.btn-warning.hover{background-color:#e08e0b}.btn-outline{border:1px solid #fff;background:transparent;color:#fff}.btn-outline:hover,.btn-outline:focus,.btn-outline:active{color:rgba(255,255,255,0.7);border-color:rgba(255,255,255,0.7)}.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn[class*='bg-']:hover{-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,0.2);box-shadow:inset 0 0 100px rgba(0,0,0,0.2)}.btn-app{border-radius:3px;position:relative;padding:15px 5px;margin:0 0 10px 10px;min-width:80px;height:60px;text-align:center;color:#666;border:1px solid #ddd;background-color:#f4f4f4;font-size:12px}.btn-app>.fa,.btn-app>.glyphicon,.btn-app>.ion{font-size:20px;display:block}.btn-app:hover{background:#f4f4f4;color:#444;border-color:#aaa}.btn-app:active,.btn-app:focus{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-app>.badge{position:absolute;top:-3px;right:-10px;font-size:10px;font-weight:400}.callout{border-radius:3px;margin:0 0 20px 0;padding:15px 30px 15px 15px;border-left:5px solid #eee}.callout a{color:#fff;text-decoration:underline}.callout a:hover{color:#eee}.callout h4{margin-top:0;font-weight:600}.callout p:last-child{margin-bottom:0}.callout code,.callout .highlight{background-color:#fff}.callout.callout-danger{border-color:#c23321}.callout.callout-warning{border-color:#c87f0a}.callout.callout-info{border-color:#0097bc}.callout.callout-success{border-color:#00733e}.alert{border-radius:3px}.alert h4{font-weight:600}.alert .icon{margin-right:10px}.alert .close{color:#000;opacity:.2;filter:alpha(opacity=20)}.alert .close:hover{opacity:.5;filter:alpha(opacity=50)}.alert a{color:#fff;text-decoration:underline}.alert-success{border-color:#008d4c}.alert-danger,.alert-error{border-color:#d73925}.alert-warning{border-color:#e08e0b}.alert-info{border-color:#00acd6}.nav>li>a:hover,.nav>li>a:active,.nav>li>a:focus{color:#444;background:#f7f7f7}.nav-pills>li>a{border-radius:0;border-top:3px solid transparent;color:#444}.nav-pills>li>a>.fa,.nav-pills>li>a>.glyphicon,.nav-pills>li>a>.ion{margin-right:5px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{border-top-color:#3c8dbc}.nav-pills>li.active>a{font-weight:600}.nav-stacked>li>a{border-radius:0;border-top:0;border-left:3px solid transparent;color:#444}.nav-stacked>li.active>a,.nav-stacked>li.active>a:hover{background:transparent;color:#444;border-top:0;border-left-color:#3c8dbc}.nav-stacked>li.header{border-bottom:1px solid #ddd;color:#777;margin-bottom:10px;padding:5px 10px;text-transform:uppercase}.nav-tabs-custom{margin-bottom:20px;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px}.nav-tabs-custom>.nav-tabs{margin:0;/* border-bottom-color: #3C8DBC /*#f4f4f4*/; */border-top-right-radius:3px;border-top-left-radius:3px}.nav-tabs-custom>.nav-tabs>li{border-top:3px solid transparent;/*margin-bottom:-2px;*/margin-right:5px}.nav-tabs-custom>.nav-tabs>li>a{color:#444;border-radius:0}.nav-tabs-custom>.nav-tabs>li>a.text-muted{color:#999}.nav-tabs-custom>.nav-tabs>li>a,.nav-tabs-custom>.nav-tabs>li>a:hover{background:transparent;margin:0}.nav-tabs-custom>.nav-tabs>li>a:hover{color:#999}.nav-tabs-custom>.nav-tabs>li:not(.active)>a:hover,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:focus,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:active{border-color:transparent}.nav-tabs-custom>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a{background-color:#fff;color:#444}.nav-tabs-custom>.nav-tabs>li.active>a{border-top:1px solid #3C8DBC;border-left:1px solid #3C8DBC;border-right:1px solid #3C8DBC;}.nav-tabs-custom>.nav-tabs>li:first-of-type{margin-left:0}.nav-tabs-custom>.nav-tabs>li:first-of-type.active>a{border-left-color:transparent}.nav-tabs-custom>.nav-tabs.pull-right{float:none!important}.nav-tabs-custom>.nav-tabs.pull-right>li{float:right}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type{margin-right:0}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type>a{border-left-width:1px}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a{border-left-color:#f4f4f4;border-right-color:transparent}.nav-tabs-custom>.nav-tabs>li.header{line-height:35px;padding:0 10px;font-size:20px;color:#444}.nav-tabs-custom>.nav-tabs>li.header>.fa,.nav-tabs-custom>.nav-tabs>li.header>.glyphicon,.nav-tabs-custom>.nav-tabs>li.header>.ion{margin-right:5px}.nav-tabs-custom>.tab-content{background:#fff;padding:10px;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.nav-tabs-custom .dropdown.open>a:active,.nav-tabs-custom .dropdown.open>a:focus{background:transparent;color:#999}.pagination>li>a{background:#fafafa;color:#666}.pagination.pagination-flat>li>a{border-radius:0 !important}.products-list{list-style:none;margin:0;padding:0}.products-list>.item{border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:10px 0;background:#fff}.products-list>.item:before,.products-list>.item:after{content:" ";display:table}.products-list>.item:after{clear:both}.products-list .product-img{float:left}.products-list .product-img img{width:50px;height:50px}.products-list .product-info{margin-left:60px}.products-list .product-title{font-weight:600}.products-list .product-description{display:block;color:#999;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.product-list-in-box>.item{-webkit-box-shadow:none;box-shadow:none;border-radius:0;border-bottom:1px solid #f4f4f4}.product-list-in-box>.item:last-of-type{border-bottom-width:0}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{border-top:1px solid #f4f4f4}.table>thead>tr>th{border-bottom:2px solid #f4f4f4}.table tr td .progress{margin-top:5px}.table-bordered{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table.no-border,.table.no-border td,.table.no-border th{border:0}table.text-center,table.text-center td,table.text-center th{text-align:center}.table.align th{text-align:left}.table.align td{text-align:right}.label-default{background-color:#d2d6de;color:#444}.direct-chat .box-body{border-bottom-right-radius:0;border-bottom-left-radius:0;position:relative;overflow-x:hidden;padding:0}.direct-chat.chat-pane-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-messages{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);padding:10px;height:250px;overflow:auto}.direct-chat-msg,.direct-chat-text{display:block}.direct-chat-msg{margin-bottom:10px}.direct-chat-msg:before,.direct-chat-msg:after{content:" ";display:table}.direct-chat-msg:after{clear:both}.direct-chat-messages,.direct-chat-contacts{-webkit-transition:-webkit-transform .5s ease-in-out;-moz-transition:-moz-transform .5s ease-in-out;-o-transition:-o-transform .5s ease-in-out;transition:transform .5s ease-in-out}.direct-chat-text{border-radius:5px;position:relative;padding:5px 10px;background:#d2d6de;border:1px solid #d2d6de;margin:5px 0 0 50px;color:#444}.direct-chat-text:after,.direct-chat-text:before{position:absolute;right:100%;top:15px;border:solid transparent;border-right-color:#d2d6de;content:' ';height:0;width:0;pointer-events:none}.direct-chat-text:after{border-width:5px;margin-top:-5px}.direct-chat-text:before{border-width:6px;margin-top:-6px}.right .direct-chat-text{margin-right:50px;margin-left:0}.right .direct-chat-text:after,.right .direct-chat-text:before{right:auto;left:100%;border-right-color:transparent;border-left-color:#d2d6de}.direct-chat-img{border-radius:50%;float:left;width:40px;height:40px}.right .direct-chat-img{float:right}.direct-chat-info{display:block;margin-bottom:2px;font-size:12px}.direct-chat-name{font-weight:600}.direct-chat-timestamp{color:#999}.direct-chat-contacts-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-contacts{-webkit-transform:translate(101%, 0);-ms-transform:translate(101%, 0);-o-transform:translate(101%, 0);transform:translate(101%, 0);position:absolute;top:0;bottom:0;height:250px;width:100%;background:#222d32;color:#fff;overflow:auto}.contacts-list>li{border-bottom:1px solid rgba(0,0,0,0.2);padding:10px;margin:0}.contacts-list>li:before,.contacts-list>li:after{content:" ";display:table}.contacts-list>li:after{clear:both}.contacts-list>li:last-of-type{border-bottom:none}.contacts-list-img{border-radius:50%;width:40px;float:left}.contacts-list-info{margin-left:45px;color:#fff}.contacts-list-name,.contacts-list-status{display:block}.contacts-list-name{font-weight:600}.contacts-list-status{font-size:12px}.contacts-list-date{color:#aaa;font-weight:normal}.contacts-list-msg{color:#999}.direct-chat-danger .right>.direct-chat-text{background:#dd4b39;border-color:#dd4b39;color:#fff}.direct-chat-danger .right>.direct-chat-text:after,.direct-chat-danger .right>.direct-chat-text:before{border-left-color:#dd4b39}.direct-chat-primary .right>.direct-chat-text{background:#3c8dbc;border-color:#3c8dbc;color:#fff}.direct-chat-primary .right>.direct-chat-text:after,.direct-chat-primary .right>.direct-chat-text:before{border-left-color:#3c8dbc}.direct-chat-warning .right>.direct-chat-text{background:#f39c12;border-color:#f39c12;color:#fff}.direct-chat-warning .right>.direct-chat-text:after,.direct-chat-warning .right>.direct-chat-text:before{border-left-color:#f39c12}.direct-chat-info .right>.direct-chat-text{background:#00c0ef;border-color:#00c0ef;color:#fff}.direct-chat-info .right>.direct-chat-text:after,.direct-chat-info .right>.direct-chat-text:before{border-left-color:#00c0ef}.direct-chat-success .right>.direct-chat-text{background:#00a65a;border-color:#00a65a;color:#fff}.direct-chat-success .right>.direct-chat-text:after,.direct-chat-success .right>.direct-chat-text:before{border-left-color:#00a65a}.users-list>li{width:25%;float:left;padding:10px;text-align:center}.users-list>li img{border-radius:50%;max-width:100%;height:auto}.users-list>li>a:hover,.users-list>li>a:hover .users-list-name{color:#999}.users-list-name,.users-list-date{display:block}.users-list-name{font-weight:600;color:#444;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.users-list-date{color:#999;font-size:12px}.carousel-control.left,.carousel-control.right{background-image:none}.carousel-control>.fa{font-size:40px;position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-20px}.modal{background:rgba(0,0,0,0.3)}.modal-content{border-radius:0;-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125);border:0}@media (min-width:768px){.modal-content{-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125)}}.modal-header{border-bottom-color:#f4f4f4}.modal-footer{border-top-color:#f4f4f4}.modal-primary .modal-header,.modal-primary .modal-footer{border-color:#307095}.modal-warning .modal-header,.modal-warning .modal-footer{border-color:#c87f0a}.modal-info .modal-header,.modal-info .modal-footer{border-color:#0097bc}.modal-success .modal-header,.modal-success .modal-footer{border-color:#00733e}.modal-danger .modal-header,.modal-danger .modal-footer{border-color:#c23321}.box-widget{border:none;position:relative}.widget-user .widget-user-header{padding:20px;height:120px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user .widget-user-username{margin-top:0;margin-bottom:5px;font-size:25px;font-weight:300;text-shadow:0 1px 1px rgba(0,0,0,0.2)}.widget-user .widget-user-desc{margin-top:0}.widget-user .widget-user-image{position:absolute;top:65px;left:50%;margin-left:-45px}.widget-user .widget-user-image>img{width:90px;height:auto;border:3px solid #fff}.widget-user .box-footer{padding-top:30px}.widget-user-2 .widget-user-header{padding:20px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user-2 .widget-user-username{margin-top:5px;margin-bottom:5px;font-size:25px;font-weight:300}.widget-user-2 .widget-user-desc{margin-top:0}.widget-user-2 .widget-user-username,.widget-user-2 .widget-user-desc{margin-left:75px}.widget-user-2 .widget-user-image>img{width:65px;height:auto;float:left}.mailbox-messages>.table{margin:0}.mailbox-controls{padding:5px}.mailbox-controls.with-border{border-bottom:1px solid #f4f4f4}.mailbox-read-info{border-bottom:1px solid #f4f4f4;padding:10px}.mailbox-read-info h3{font-size:20px;margin:0}.mailbox-read-info h5{margin:0;padding:5px 0 0 0}.mailbox-read-time{color:#999;font-size:13px}.mailbox-read-message{padding:10px}.mailbox-attachments li{float:left;width:200px;border:1px solid #eee;margin-bottom:10px;margin-right:10px}.mailbox-attachment-name{font-weight:bold;color:#666}.mailbox-attachment-icon,.mailbox-attachment-info,.mailbox-attachment-size{display:block}.mailbox-attachment-info{padding:10px;background:#f4f4f4}.mailbox-attachment-size{color:#999;font-size:12px}.mailbox-attachment-icon{text-align:center;font-size:65px;color:#666;padding:20px 10px}.mailbox-attachment-icon.has-img{padding:0}.mailbox-attachment-icon.has-img>img{max-width:100%;height:auto}.lockscreen{background:#d2d6de}.lockscreen-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.lockscreen-logo a{color:#444}.lockscreen-wrapper{max-width:400px;margin:0 auto;margin-top:10%}.lockscreen .lockscreen-name{text-align:center;font-weight:600}.lockscreen-item{border-radius:4px;padding:0;background:#fff;position:relative;margin:10px auto 30px auto;width:290px}.lockscreen-image{border-radius:50%;position:absolute;left:-10px;top:-25px;background:#fff;padding:5px;z-index:10}.lockscreen-image>img{border-radius:50%;width:70px;height:70px}.lockscreen-credentials{margin-left:70px}.lockscreen-credentials .form-control{border:0}.lockscreen-credentials .btn{background-color:#fff;border:0;padding:0 10px}.lockscreen-footer{margin-top:10px}.login-logo,.register-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.login-logo a,.register-logo a{color:#444}.login-page,.register-page{background:#d2d6de}.login-box,.register-box{width:360px;margin:7% auto}@media (max-width:768px){.login-box,.register-box{width:90%;margin-top:20px}}.login-box-body,.register-box-body{background:#fff;padding:20px;border-top:0;color:#666}.login-box-body .form-control-feedback,.register-box-body .form-control-feedback{color:#777}.login-box-msg,.register-box-msg{margin:0;text-align:center;padding:0 20px 20px 20px}.social-auth-links{margin:10px 0}.error-page{width:600px;margin:20px auto 0 auto}@media (max-width:991px){.error-page{width:100%}}.error-page>.headline{float:left;font-size:100px;font-weight:300}@media (max-width:991px){.error-page>.headline{float:none;text-align:center}}.error-page>.error-content{margin-left:190px;display:block}@media (max-width:991px){.error-page>.error-content{margin-left:0}}.error-page>.error-content>h3{font-weight:300;font-size:25px}@media (max-width:991px){.error-page>.error-content>h3{text-align:center}}.invoice{position:relative;background:#fff;border:1px solid #f4f4f4;padding:20px;margin:10px 25px}.invoice-title{margin-top:0}.profile-user-img{margin:0 auto;width:100px;padding:3px;border:3px solid #d2d6de}.profile-username{font-size:21px;margin-top:5px}.post{border-bottom:1px solid #d2d6de;margin-bottom:15px;padding-bottom:15px;color:#666}.post:last-of-type{border-bottom:0;margin-bottom:0;padding-bottom:0}.post .user-block{margin-bottom:15px}.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon>:first-child{border:none;text-align:center;width:100%}.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0}.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0}.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0}.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn.focus,.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}.btn-adn .badge{color:#d87a68;background-color:#fff}.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket.focus,.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}.btn-bitbucket .badge{color:#205081;background-color:#fff}.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox.focus,.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}.btn-dropbox .badge{color:#1087dd;background-color:#fff}.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook.focus,.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}.btn-facebook .badge{color:#3b5998;background-color:#fff}.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr.focus,.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}.btn-flickr .badge{color:#ff0084;background-color:#fff}.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare.focus,.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}.btn-foursquare .badge{color:#f94877;background-color:#fff}.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:hover,.btn-github:focus,.btn-github.focus,.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}.btn-github .badge{color:#444;background-color:#fff}.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:hover,.btn-google:focus,.btn-google.focus,.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none}.btn-google .badge{color:#dd4b39;background-color:#fff}.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram.focus,.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}.btn-instagram .badge{color:#3f729b;background-color:#fff}.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin.focus,.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}.btn-linkedin .badge{color:#007bb6;background-color:#fff}.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft.focus,.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}.btn-microsoft .badge{color:#2672ec;background-color:#fff}.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid.focus,.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}.btn-openid .badge{color:#f7931e;background-color:#fff}.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:hover,.btn-pinterest:focus,.btn-pinterest.focus,.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}.btn-pinterest .badge{color:#cb2027;background-color:#fff}.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit.focus,.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}.btn-reddit .badge{color:#eff7ff;background-color:#000}.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud.focus,.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}.btn-soundcloud .badge{color:#f50;background-color:#fff}.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr.focus,.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}.btn-tumblr .badge{color:#2c4762;background-color:#fff}.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter.focus,.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}.btn-twitter .badge{color:#55acee;background-color:#fff}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo.focus,.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}.btn-vimeo .badge{color:#1ab7ea;background-color:#fff}.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk.focus,.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}.btn-vk .badge{color:#587ea3;background-color:#fff}.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo.focus,.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}.btn-yahoo .badge{color:#720e9e;background-color:#fff}.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none;border:1px solid #3c8dbc}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}.pad{padding:10px}.margin{margin:10px}.margin-bottom{margin-bottom:20px}.margin-bottom-none{margin-bottom:0}.margin-r-5{margin-right:5px}.inline{display:inline}.description-block{display:block;margin:10px 0;text-align:center}.description-block.margin-bottom{margin-bottom:25px}.description-block>.description-header{margin:0;padding:0;font-weight:600;font-size:16px}.description-block>.description-text{text-transform:uppercase}.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.alert-success,.alert-danger,.alert-error,.alert-warning,.alert-info,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer{color:#fff !important}.bg-gray{color:#000;background-color:#d2d6de !important}.bg-gray-light{background-color:#f7f7f7}.bg-black{background-color:#111 !important}.bg-red,.callout.callout-danger,.alert-danger,.alert-error,.label-danger,.modal-danger .modal-body{background-color:#dd4b39 !important}.bg-yellow,.callout.callout-warning,.alert-warning,.label-warning,.modal-warning .modal-body{background-color:#f39c12 !important}.bg-aqua,.callout.callout-info,.alert-info,.label-info,.modal-info .modal-body{background-color:#00c0ef !important}.bg-blue{background-color:#0073b7 !important}.bg-light-blue,.label-primary,.modal-primary .modal-body{background-color:#3c8dbc !important}.bg-green,.callout.callout-success,.alert-success,.label-success,.modal-success .modal-body{background-color:#00a65a !important}.bg-navy{background-color:#001f3f !important}.bg-teal{background-color:#39cccc !important}.bg-olive{background-color:#3d9970 !important}.bg-lime{background-color:#01ff70 !important}.bg-orange{background-color:#ff851b !important}.bg-fuchsia{background-color:#f012be !important}.bg-purple{background-color:#605ca8 !important}.bg-maroon{background-color:#d81b60 !important}.bg-gray-active{color:#000;background-color:#b5bbc8 !important}.bg-black-active{background-color:#000 !important}.bg-red-active,.modal-danger .modal-header,.modal-danger .modal-footer{background-color:#d33724 !important}.bg-yellow-active,.modal-warning .modal-header,.modal-warning .modal-footer{background-color:#db8b0b !important}.bg-aqua-active,.modal-info .modal-header,.modal-info .modal-footer{background-color:#00a7d0 !important}.bg-blue-active{background-color:#005384 !important}.bg-light-blue-active,.modal-primary .modal-header,.modal-primary .modal-footer{background-color:#357ca5 !important}.bg-green-active,.modal-success .modal-header,.modal-success .modal-footer{background-color:#008d4c !important}.bg-navy-active{background-color:#001a35 !important}.bg-teal-active{background-color:#30bbbb !important}.bg-olive-active{background-color:#368763 !important}.bg-lime-active{background-color:#00e765 !important}.bg-orange-active{background-color:#ff7701 !important}.bg-fuchsia-active{background-color:#db0ead !important}.bg-purple-active{background-color:#555299 !important}.bg-maroon-active{background-color:#ca195a !important}[class^="bg-"].disabled{opacity:.65;filter:alpha(opacity=65)}.text-red{color:#dd4b39 !important}.text-yellow{color:#f39c12 !important}.text-aqua{color:#00c0ef !important}.text-blue{color:#0073b7 !important}.text-black{color:#111 !important}.text-light-blue{color:#3c8dbc !important}.text-green{color:#00a65a !important}.text-gray{color:#d2d6de !important}.text-navy{color:#001f3f !important}.text-teal{color:#39cccc !important}.text-olive{color:#3d9970 !important}.text-lime{color:#01ff70 !important}.text-orange{color:#ff851b !important}.text-fuchsia{color:#f012be !important}.text-purple{color:#605ca8 !important}.text-maroon{color:#d81b60 !important}.link-muted{color:#7a869d}.link-muted:hover,.link-muted:focus{color:#606c84}.link-black{color:#666}.link-black:hover,.link-black:focus{color:#999}.hide{display:none !important}.no-border{border:0 !important}.no-padding{padding:0 !important}.no-margin{margin:0 !important}.no-shadow{box-shadow:none!important}.list-unstyled,.chart-legend,.contacts-list,.users-list,.mailbox-attachments{list-style:none;margin:0;padding:0}.list-group-unbordered>.list-group-item{border-left:0;border-right:0;border-radius:0;padding-left:0;padding-right:0}.flat{border-radius:0 !important}.text-bold,.text-bold.table td,.text-bold.table th{font-weight:700}.text-sm{font-size:12px}.jqstooltip{padding:5px!important;width:auto!important;height:auto!important}.bg-teal-gradient{background:#39cccc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important;background:-ms-linear-gradient(bottom, #39cccc, #7adddd) !important;background:-moz-linear-gradient(center bottom, #39cccc 0, #7adddd 100%) !important;background:-o-linear-gradient(#7adddd, #39cccc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important;color:#fff}.bg-light-blue-gradient{background:#3c8dbc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;background:-ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;background:-moz-linear-gradient(center bottom, #3c8dbc 0, #67a8ce 100%) !important;background:-o-linear-gradient(#67a8ce, #3c8dbc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;color:#fff}.bg-blue-gradient{background:#0073b7 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;background:-ms-linear-gradient(bottom, #0073b7, #0089db) !important;background:-moz-linear-gradient(center bottom, #0073b7 0, #0089db 100%) !important;background:-o-linear-gradient(#0089db, #0073b7) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;color:#fff}.bg-aqua-gradient{background:#00c0ef !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;background:-ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;background:-moz-linear-gradient(center bottom, #00c0ef 0, #14d1ff 100%) !important;background:-o-linear-gradient(#14d1ff, #00c0ef) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;color:#fff}.bg-yellow-gradient{background:#f39c12 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;background:-ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;background:-moz-linear-gradient(center bottom, #f39c12 0, #f7bc60 100%) !important;background:-o-linear-gradient(#f7bc60, #f39c12) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;color:#fff}.bg-purple-gradient{background:#605ca8 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important;background:-ms-linear-gradient(bottom, #605ca8, #9491c4) !important;background:-moz-linear-gradient(center bottom, #605ca8 0, #9491c4 100%) !important;background:-o-linear-gradient(#9491c4, #605ca8) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important;color:#fff}.bg-green-gradient{background:#00a65a !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;background:-ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;background:-moz-linear-gradient(center bottom, #00a65a 0, #00ca6d 100%) !important;background:-o-linear-gradient(#00ca6d, #00a65a) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;color:#fff}.bg-red-gradient{background:#dd4b39 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important;background:-ms-linear-gradient(bottom, #dd4b39, #e47365) !important;background:-moz-linear-gradient(center bottom, #dd4b39 0, #e47365 100%) !important;background:-o-linear-gradient(#e47365, #dd4b39) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important;color:#fff}.bg-black-gradient{background:#111 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #111), color-stop(1, #2b2b2b)) !important;background:-ms-linear-gradient(bottom, #111, #2b2b2b) !important;background:-moz-linear-gradient(center bottom, #111 0, #2b2b2b 100%) !important;background:-o-linear-gradient(#2b2b2b, #111) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111111', GradientType=0) !important;color:#fff}.bg-maroon-gradient{background:#d81b60 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #d81b60), color-stop(1, #e73f7c)) !important;background:-ms-linear-gradient(bottom, #d81b60, #e73f7c) !important;background:-moz-linear-gradient(center bottom, #d81b60 0, #e73f7c 100%) !important;background:-o-linear-gradient(#e73f7c, #d81b60) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#d81b60', GradientType=0) !important;color:#fff}.description-block .description-icon{font-size:16px}.no-pad-top{padding-top:0}.position-static{position:static!important}.list-header{font-size:15px;padding:10px 4px;font-weight:bold;color:#666}.list-seperator{height:1px;background:#f4f4f4;margin:15px 0 9px 0}.list-link>a{padding:4px;color:#777}.list-link>a:hover{color:#222}.font-light{font-weight:300}.user-block:before,.user-block:after{content:" ";display:table}.user-block:after{clear:both}.user-block img{width:40px;height:40px;float:left}.user-block .username,.user-block .description,.user-block .comment{display:block;margin-left:50px}.user-block .username{font-size:16px;font-weight:600}.user-block .description{color:#999;font-size:13px}.user-block.user-block-sm .username,.user-block.user-block-sm .description,.user-block.user-block-sm .comment{margin-left:40px}.user-block.user-block-sm .username{font-size:14px}.img-sm,.img-md,.img-lg,.box-comments .box-comment img,.user-block.user-block-sm img{float:left}.img-sm,.box-comments .box-comment img,.user-block.user-block-sm img{width:30px!important;height:30px!important}.img-sm+.img-push{margin-left:40px}.img-md{width:60px;height:60px}.img-md+.img-push{margin-left:70px}.img-lg{width:100px;height:100px}.img-lg+.img-push{margin-left:110px}.img-bordered{border:3px solid #d2d6de;padding:3px}.img-bordered-sm{border:2px solid #d2d6de;padding:2px}.attachment-block{border:1px solid #f4f4f4;padding:5px;margin-bottom:10px;background:#f7f7f7}.attachment-block .attachment-img{max-width:100px;max-height:100px;height:auto;float:left}.attachment-block .attachment-pushed{margin-left:110px}.attachment-block .attachment-heading{margin:0}.attachment-block .attachment-text{color:#555}.connectedSortable{min-height:100px}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sort-highlight{background:#f4f4f4;border:1px dashed #ddd;margin-bottom:10px}.full-opacity-hover{opacity:.65;filter:alpha(opacity=65)}.full-opacity-hover:hover{opacity:1;filter:alpha(opacity=100)}.chart{position:relative;overflow:hidden;width:100%}.chart svg,.chart canvas{width:100%!important}@media print{.no-print,.main-sidebar,.left-side,.main-header,.content-header{display:none!important}.content-wrapper,.right-side,.main-footer{margin-left:0!important;min-height:0!important;-webkit-transform:translate(0, 0) !important;-ms-transform:translate(0, 0) !important;-o-transform:translate(0, 0) !important;transform:translate(0, 0) !important}.fixed .content-wrapper,.fixed .right-side{padding-top:0!important}.invoice{width:100%;border:0;margin:0;padding:0}.invoice-col{float:left;width:33.3333333%}.table-responsive{overflow:auto}.table-responsive>.table tr th,.table-responsive>.table tr td{white-space:normal!important}}
\ No newline at end of file
--- /dev/null
+.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
\ No newline at end of file
--- /dev/null
+/*! AdminLTE app.js
+ * ================
+ * Main JS application file for AdminLTE v2. This file
+ * should be included in all pages. It controls some layout
+ * options and implements exclusive AdminLTE plugins.
+ *
+ * @Author Almsaeed Studio
+ * @Support <http://www.almsaeedstudio.com>
+ * @Email <support@almsaeedstudio.com>
+ * @version 2.3.0
+ * @license MIT <http://opensource.org/licenses/MIT>
+ */
+function _init(){"use strict";$.AdminLTE.layout={activate:function(){var a=this;a.fix(),a.fixSidebar(),$(window,".wrapper").resize(function(){a.fix(),a.fixSidebar()})},fix:function(){var a=$(".main-header").outerHeight()+$(".main-footer").outerHeight(),b=$(window).height(),c=$(".sidebar").height();if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",b-$(".main-footer").outerHeight());else{var d;b>=c?($(".content-wrapper, .right-side").css("min-height",b-a),d=b-a):($(".content-wrapper, .right-side").css("min-height",c),d=c);var e=$($.AdminLTE.options.controlSidebarOptions.selector);"undefined"!=typeof e&&e.height()>d&&$(".content-wrapper, .right-side").css("min-height",e.height())}},fixSidebar:function(){return $("body").hasClass("fixed")?("undefined"==typeof $.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),void($.AdminLTE.options.sidebarSlimScroll&&"undefined"!=typeof $.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimscroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"})))):void("undefined"!=typeof $.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"))}},$.AdminLTE.pushMenu={activate:function(a){var b=$.AdminLTE.options.screenSizes;$(a).on("click",function(a){a.preventDefault(),$(window).width()>b.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=b.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var a=this,b=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>b&&a.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>b&&a.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(a){var b=this,c=$.AdminLTE.options.animationSpeed;$(document).on("click",a+" li a",function(a){var d=$(this),e=d.next();if(e.is(".treeview-menu")&&e.is(":visible"))e.slideUp(c,function(){e.removeClass("menu-open")}),e.parent("li").removeClass("active");else if(e.is(".treeview-menu")&&!e.is(":visible")){var f=d.parents("ul").first(),g=f.find("ul:visible").slideUp(c);g.removeClass("menu-open");var h=d.parent("li");e.slideDown(c,function(){e.addClass("menu-open"),f.find("li.active").removeClass("active"),h.addClass("active"),b.layout.fix()})}e.is(".treeview-menu")&&a.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var a=this,b=$.AdminLTE.options.controlSidebarOptions,c=$(b.selector),d=$(b.toggleBtnSelector);d.on("click",function(d){d.preventDefault(),c.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?a.close(c,b.slide):a.open(c,b.slide)});var e=$(".control-sidebar-bg");a._fix(e),$("body").hasClass("fixed")?a._fixForFixed(c):$(".content-wrapper, .right-side").height()<c.height()&&a._fixForContent(c)},open:function(a,b){b?a.addClass("control-sidebar-open"):$("body").addClass("control-sidebar-open")},close:function(a,b){b?a.removeClass("control-sidebar-open"):$("body").removeClass("control-sidebar-open")},_fix:function(a){var b=this;$("body").hasClass("layout-boxed")?(a.css("position","absolute"),a.height($(".wrapper").height()),$(window).resize(function(){b._fix(a)})):a.css({position:"fixed",height:"auto"})},_fixForFixed:function(a){a.css({position:"fixed","max-height":"100%",overflow:"auto","padding-bottom":"50px"})},_fixForContent:function(a){$(".content-wrapper, .right-side").css("min-height",a.height())}},$.AdminLTE.boxWidget={selectors:$.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors,icons:$.AdminLTE.options.boxWidgetOptions.boxWidgetIcons,animationSpeed:$.AdminLTE.options.animationSpeed,activate:function(a){var b=this;a||(a=document),$(a).on("click",b.selectors.collapse,function(a){a.preventDefault(),b.collapse($(this))}),$(a).on("click",b.selectors.remove,function(a){a.preventDefault(),b.remove($(this))})},collapse:function(a){var b=this,c=a.parents(".box").first(),d=c.find("> .box-body, > .box-footer, > form >.box-body, > form > .box-footer");c.hasClass("collapsed-box")?(a.children(":first").removeClass(b.icons.open).addClass(b.icons.collapse),d.slideDown(b.animationSpeed,function(){c.removeClass("collapsed-box")})):(a.children(":first").removeClass(b.icons.collapse).addClass(b.icons.open),d.slideUp(b.animationSpeed,function(){c.addClass("collapsed-box")}))},remove:function(a){var b=a.parents(".box").first();b.slideUp(this.animationSpeed)}}}if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");$.AdminLTE={},$.AdminLTE.options={navbarMenuSlimscroll:!0,navbarMenuSlimscrollWidth:"3px",navbarMenuHeight:"200px",animationSpeed:500,sidebarToggleSelector:"[data-toggle='offcanvas']",sidebarPushMenu:!0,sidebarSlimScroll:!0,sidebarExpandOnHover:!1,enableBoxRefresh:!0,enableBSToppltip:!0,BSTooltipSelector:"[data-toggle='tooltip']",enableFastclick:!0,enableControlSidebar:!0,controlSidebarOptions:{toggleBtnSelector:"[data-toggle='control-sidebar']",selector:".control-sidebar",slide:!0},enableBoxWidget:!0,boxWidgetOptions:{boxWidgetIcons:{collapse:"fa-minus",open:"fa-plus",remove:"fa-times"},boxWidgetSelectors:{remove:'[data-widget="remove"]',collapse:'[data-widget="collapse"]'}},directChat:{enable:!0,contactToggleSelector:'[data-widget="chat-pane-toggle"]'},colors:{lightBlue:"#3c8dbc",red:"#f56954",green:"#00a65a",aqua:"#00c0ef",yellow:"#f39c12",blue:"#0073b7",navy:"#001F3F",teal:"#39CCCC",olive:"#3D9970",lime:"#01FF70",orange:"#FF851B",fuchsia:"#F012BE",purple:"#8E24AA",maroon:"#D81B60",black:"#222222",gray:"#d2d6de"},screenSizes:{xs:480,sm:768,md:992,lg:1200}},$(function(){"use strict";$("body").removeClass("hold-transition"),"undefined"!=typeof AdminLTEOptions&&$.extend(!0,$.AdminLTE.options,AdminLTEOptions);var a=$.AdminLTE.options;_init(),$.AdminLTE.layout.activate(),$.AdminLTE.tree(".sidebar"),a.enableControlSidebar&&$.AdminLTE.controlSidebar.activate(),a.navbarMenuSlimscroll&&"undefined"!=typeof $.fn.slimscroll&&$(".navbar .menu").slimscroll({height:a.navbarMenuHeight,alwaysVisible:!1,size:a.navbarMenuSlimscrollWidth}).css("width","100%"),a.sidebarPushMenu&&$.AdminLTE.pushMenu.activate(a.sidebarToggleSelector),a.enableBSToppltip&&$("body").tooltip({selector:a.BSTooltipSelector}),a.enableBoxWidget&&$.AdminLTE.boxWidget.activate(),a.enableFastclick&&"undefined"!=typeof FastClick&&FastClick.attach(document.body),a.directChat.enable&&$(document).on("click",a.directChat.contactToggleSelector,function(){var a=$(this).parents(".direct-chat").first();a.toggleClass("direct-chat-contacts-open")}),$('.btn-group[data-toggle="btn-toggle"]').each(function(){var a=$(this);$(this).find(".btn").on("click",function(b){a.find(".btn.active").removeClass("active"),$(this).addClass("active"),b.preventDefault()})})}),function(a){"use strict";a.fn.boxRefresh=function(b){function c(a){a.append(f),e.onLoadStart.call(a)}function d(a){a.find(f).remove(),e.onLoadDone.call(a)}var e=a.extend({trigger:".refresh-btn",source:"",onLoadStart:function(a){return a},onLoadDone:function(a){return a}},b),f=a('<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>');return this.each(function(){if(""===e.source)return void(window.console&&window.console.log("Please specify a source first - boxRefresh()"));var b=a(this),f=b.find(e.trigger).first();f.on("click",function(a){a.preventDefault(),c(b),b.find(".box-body").load(e.source,function(){d(b)})})})}}(jQuery),function(a){"use strict";a.fn.activateBox=function(){a.AdminLTE.boxWidget.activate(this)}}(jQuery),function(a){"use strict";a.fn.todolist=function(b){var c=a.extend({onCheck:function(a){return a},onUncheck:function(a){return a}},b);return this.each(function(){"undefined"!=typeof a.fn.iCheck?(a("input",this).on("ifChecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onCheck.call(b)}),a("input",this).on("ifUnchecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onUncheck.call(b)})):a("input",this).on("change",function(){var b=a(this).parents("li").first();b.toggleClass("done"),a("input",b).is(":checked")?c.onCheck.call(b):c.onUncheck.call(b)})})}}(jQuery);
\ No newline at end of file
--- /dev/null
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
--- /dev/null
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px;font-size:14px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode=" " />
+<glyph unicode="¥" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode=" " horiz-adv-x="650" />
+<glyph unicode=" " horiz-adv-x="1300" />
+<glyph unicode=" " horiz-adv-x="650" />
+<glyph unicode=" " horiz-adv-x="1300" />
+<glyph unicode=" " horiz-adv-x="433" />
+<glyph unicode=" " horiz-adv-x="325" />
+<glyph unicode=" " horiz-adv-x="216" />
+<glyph unicode=" " horiz-adv-x="216" />
+<glyph unicode=" " horiz-adv-x="162" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="72" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="325" />
+<glyph unicode="€" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="₽" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="−" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="⌛" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="☁" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="⛺" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="✉" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="✏" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="🔑" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="🚪" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg>
\ No newline at end of file
--- /dev/null
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),
+d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
--- /dev/null
+/*!\r
+ * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome\r
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\r
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}\r
--- /dev/null
+@charset "UTF-8";/*!\r
+ Ionicons, v2.0.1\r
+ Created by Ben Sperry for the Ionic Framework, http://ionicons.com/\r
+ https://twitter.com/benjsperry https://twitter.com/ionicframework\r
+ MIT License: https://github.com/driftyco/ionicons\r
+\r
+ Android-style icons originally built by Google’s\r
+ Material Design Icons: https://github.com/google/material-design-icons\r
+ used under CC BY http://creativecommons.org/licenses/by/4.0/\r
+ Modified icons to fit ionicon’s grid from original.\r
+*/@font-face{font-family:"Ionicons";src:url("../fonts/ionicons.eot?v=2.0.1");src:url("../fonts/ionicons.eot?v=2.0.1#iefix") format("embedded-opentype"),url("../fonts/ionicons.ttf?v=2.0.1") format("truetype"),url("../fonts/ionicons.woff?v=2.0.1") format("woff"),url("../fonts/ionicons.svg?v=2.0.1#Ionicons") format("svg");font-weight:normal;font-style:normal}.ion,.ionicons,.ion-alert:before,.ion-alert-circled:before,.ion-android-add:before,.ion-android-add-circle:before,.ion-android-alarm-clock:before,.ion-android-alert:before,.ion-android-apps:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down:before,.ion-android-arrow-dropdown:before,.ion-android-arrow-dropdown-circle:before,.ion-android-arrow-dropleft:before,.ion-android-arrow-dropleft-circle:before,.ion-android-arrow-dropright:before,.ion-android-arrow-dropright-circle:before,.ion-android-arrow-dropup:before,.ion-android-arrow-dropup-circle:before,.ion-android-arrow-forward:before,.ion-android-arrow-up:before,.ion-android-attach:before,.ion-android-bar:before,.ion-android-bicycle:before,.ion-android-boat:before,.ion-android-bookmark:before,.ion-android-bulb:before,.ion-android-bus:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-cancel:before,.ion-android-car:before,.ion-android-cart:before,.ion-android-chat:before,.ion-android-checkbox:before,.ion-android-checkbox-blank:before,.ion-android-checkbox-outline:before,.ion-android-checkbox-outline-blank:before,.ion-android-checkmark-circle:before,.ion-android-clipboard:before,.ion-android-close:before,.ion-android-cloud:before,.ion-android-cloud-circle:before,.ion-android-cloud-done:before,.ion-android-cloud-outline:before,.ion-android-color-palette:before,.ion-android-compass:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-contract:before,.ion-android-create:before,.ion-android-delete:before,.ion-android-desktop:before,.ion-android-document:before,.ion-android-done:before,.ion-android-done-all:before,.ion-android-download:before,.ion-android-drafts:before,.ion-android-exit:before,.ion-android-expand:before,.ion-android-favorite:before,.ion-android-favorite-outline:before,.ion-android-film:before,.ion-android-folder:before,.ion-android-folder-open:before,.ion-android-funnel:before,.ion-android-globe:before,.ion-android-hand:before,.ion-android-hangout:before,.ion-android-happy:before,.ion-android-home:before,.ion-android-image:before,.ion-android-laptop:before,.ion-android-list:before,.ion-android-locate:before,.ion-android-lock:before,.ion-android-mail:before,.ion-android-map:before,.ion-android-menu:before,.ion-android-microphone:before,.ion-android-microphone-off:before,.ion-android-more-horizontal:before,.ion-android-more-vertical:before,.ion-android-navigate:before,.ion-android-notifications:before,.ion-android-notifications-none:before,.ion-android-notifications-off:before,.ion-android-open:before,.ion-android-options:before,.ion-android-people:before,.ion-android-person:before,.ion-android-person-add:before,.ion-android-phone-landscape:before,.ion-android-phone-portrait:before,.ion-android-pin:before,.ion-android-plane:before,.ion-android-playstore:before,.ion-android-print:before,.ion-android-radio-button-off:before,.ion-android-radio-button-on:before,.ion-android-refresh:before,.ion-android-remove:before,.ion-android-remove-circle:before,.ion-android-restaurant:before,.ion-android-sad:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share:before,.ion-android-share-alt:before,.ion-android-star:before,.ion-android-star-half:before,.ion-android-star-outline:before,.ion-android-stopwatch:before,.ion-android-subway:before,.ion-android-sunny:before,.ion-android-sync:before,.ion-android-textsms:before,.ion-android-time:before,.ion-android-train:before,.ion-android-unlock:before,.ion-android-upload:before,.ion-android-volume-down:before,.ion-android-volume-mute:before,.ion-android-volume-off:before,.ion-android-volume-up:before,.ion-android-walk:before,.ion-android-warning:before,.ion-android-watch:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-backspace:before,.ion-backspace-outline:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-bowtie:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox:before,.ion-chatbox-working:before,.ion-chatboxes:before,.ion-chatbubble:before,.ion-chatbubble-working:before,.ion-chatbubbles:before,.ion-checkmark:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close:before,.ion-close-circled:before,.ion-close-round:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code:before,.ion-code-download:before,.ion-code-working:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-crop:before,.ion-cube:before,.ion-disc:before,.ion-document:before,.ion-document-text:before,.ion-drag:before,.ion-earth:before,.ion-easel:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email:before,.ion-email-unread:before,.ion-erlenmeyer-flask:before,.ion-erlenmeyer-flask-bubbles:before,.ion-eye:before,.ion-eye-disabled:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash:before,.ion-flash-off:before,.ion-folder:before,.ion-fork:before,.ion-fork-repo:before,.ion-forward:before,.ion-funnel:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy:before,.ion-happy-outline:before,.ion-headphone:before,.ion-heart:before,.ion-heart-broken:before,.ion-help:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-home:before,.ion-icecream:before,.ion-image:before,.ion-images:before,.ion-information:before,.ion-information-circled:before,.ion-ionic:before,.ion-ios-alarm:before,.ion-ios-alarm-outline:before,.ion-ios-albums:before,.ion-ios-albums-outline:before,.ion-ios-americanfootball:before,.ion-ios-americanfootball-outline:before,.ion-ios-analytics:before,.ion-ios-analytics-outline:before,.ion-ios-arrow-back:before,.ion-ios-arrow-down:before,.ion-ios-arrow-forward:before,.ion-ios-arrow-left:before,.ion-ios-arrow-right:before,.ion-ios-arrow-thin-down:before,.ion-ios-arrow-thin-left:before,.ion-ios-arrow-thin-right:before,.ion-ios-arrow-thin-up:before,.ion-ios-arrow-up:before,.ion-ios-at:before,.ion-ios-at-outline:before,.ion-ios-barcode:before,.ion-ios-barcode-outline:before,.ion-ios-baseball:before,.ion-ios-baseball-outline:before,.ion-ios-basketball:before,.ion-ios-basketball-outline:before,.ion-ios-bell:before,.ion-ios-bell-outline:before,.ion-ios-body:before,.ion-ios-body-outline:before,.ion-ios-bolt:before,.ion-ios-bolt-outline:before,.ion-ios-book:before,.ion-ios-book-outline:before,.ion-ios-bookmarks:before,.ion-ios-bookmarks-outline:before,.ion-ios-box:before,.ion-ios-box-outline:before,.ion-ios-briefcase:before,.ion-ios-briefcase-outline:before,.ion-ios-browsers:before,.ion-ios-browsers-outline:before,.ion-ios-calculator:before,.ion-ios-calculator-outline:before,.ion-ios-calendar:before,.ion-ios-calendar-outline:before,.ion-ios-camera:before,.ion-ios-camera-outline:before,.ion-ios-cart:before,.ion-ios-cart-outline:before,.ion-ios-chatboxes:before,.ion-ios-chatboxes-outline:before,.ion-ios-chatbubble:before,.ion-ios-chatbubble-outline:before,.ion-ios-checkmark:before,.ion-ios-checkmark-empty:before,.ion-ios-checkmark-outline:before,.ion-ios-circle-filled:before,.ion-ios-circle-outline:before,.ion-ios-clock:before,.ion-ios-clock-outline:before,.ion-ios-close:before,.ion-ios-close-empty:before,.ion-ios-close-outline:before,.ion-ios-cloud:before,.ion-ios-cloud-download:before,.ion-ios-cloud-download-outline:before,.ion-ios-cloud-outline:before,.ion-ios-cloud-upload:before,.ion-ios-cloud-upload-outline:before,.ion-ios-cloudy:before,.ion-ios-cloudy-night:before,.ion-ios-cloudy-night-outline:before,.ion-ios-cloudy-outline:before,.ion-ios-cog:before,.ion-ios-cog-outline:before,.ion-ios-color-filter:before,.ion-ios-color-filter-outline:before,.ion-ios-color-wand:before,.ion-ios-color-wand-outline:before,.ion-ios-compose:before,.ion-ios-compose-outline:before,.ion-ios-contact:before,.ion-ios-contact-outline:before,.ion-ios-copy:before,.ion-ios-copy-outline:before,.ion-ios-crop:before,.ion-ios-crop-strong:before,.ion-ios-download:before,.ion-ios-download-outline:before,.ion-ios-drag:before,.ion-ios-email:before,.ion-ios-email-outline:before,.ion-ios-eye:before,.ion-ios-eye-outline:before,.ion-ios-fastforward:before,.ion-ios-fastforward-outline:before,.ion-ios-filing:before,.ion-ios-filing-outline:before,.ion-ios-film:before,.ion-ios-film-outline:before,.ion-ios-flag:before,.ion-ios-flag-outline:before,.ion-ios-flame:before,.ion-ios-flame-outline:before,.ion-ios-flask:before,.ion-ios-flask-outline:before,.ion-ios-flower:before,.ion-ios-flower-outline:before,.ion-ios-folder:before,.ion-ios-folder-outline:before,.ion-ios-football:before,.ion-ios-football-outline:before,.ion-ios-game-controller-a:before,.ion-ios-game-controller-a-outline:before,.ion-ios-game-controller-b:before,.ion-ios-game-controller-b-outline:before,.ion-ios-gear:before,.ion-ios-gear-outline:before,.ion-ios-glasses:before,.ion-ios-glasses-outline:before,.ion-ios-grid-view:before,.ion-ios-grid-view-outline:before,.ion-ios-heart:before,.ion-ios-heart-outline:before,.ion-ios-help:before,.ion-ios-help-empty:before,.ion-ios-help-outline:before,.ion-ios-home:before,.ion-ios-home-outline:before,.ion-ios-infinite:before,.ion-ios-infinite-outline:before,.ion-ios-information:before,.ion-ios-information-empty:before,.ion-ios-information-outline:before,.ion-ios-ionic-outline:before,.ion-ios-keypad:before,.ion-ios-keypad-outline:before,.ion-ios-lightbulb:before,.ion-ios-lightbulb-outline:before,.ion-ios-list:before,.ion-ios-list-outline:before,.ion-ios-location:before,.ion-ios-location-outline:before,.ion-ios-locked:before,.ion-ios-locked-outline:before,.ion-ios-loop:before,.ion-ios-loop-strong:before,.ion-ios-medical:before,.ion-ios-medical-outline:before,.ion-ios-medkit:before,.ion-ios-medkit-outline:before,.ion-ios-mic:before,.ion-ios-mic-off:before,.ion-ios-mic-outline:before,.ion-ios-minus:before,.ion-ios-minus-empty:before,.ion-ios-minus-outline:before,.ion-ios-monitor:before,.ion-ios-monitor-outline:before,.ion-ios-moon:before,.ion-ios-moon-outline:before,.ion-ios-more:before,.ion-ios-more-outline:before,.ion-ios-musical-note:before,.ion-ios-musical-notes:before,.ion-ios-navigate:before,.ion-ios-navigate-outline:before,.ion-ios-nutrition:before,.ion-ios-nutrition-outline:before,.ion-ios-paper:before,.ion-ios-paper-outline:before,.ion-ios-paperplane:before,.ion-ios-paperplane-outline:before,.ion-ios-partlysunny:before,.ion-ios-partlysunny-outline:before,.ion-ios-pause:before,.ion-ios-pause-outline:before,.ion-ios-paw:before,.ion-ios-paw-outline:before,.ion-ios-people:before,.ion-ios-people-outline:before,.ion-ios-person:before,.ion-ios-person-outline:before,.ion-ios-personadd:before,.ion-ios-personadd-outline:before,.ion-ios-photos:before,.ion-ios-photos-outline:before,.ion-ios-pie:before,.ion-ios-pie-outline:before,.ion-ios-pint:before,.ion-ios-pint-outline:before,.ion-ios-play:before,.ion-ios-play-outline:before,.ion-ios-plus:before,.ion-ios-plus-empty:before,.ion-ios-plus-outline:before,.ion-ios-pricetag:before,.ion-ios-pricetag-outline:before,.ion-ios-pricetags:before,.ion-ios-pricetags-outline:before,.ion-ios-printer:before,.ion-ios-printer-outline:before,.ion-ios-pulse:before,.ion-ios-pulse-strong:before,.ion-ios-rainy:before,.ion-ios-rainy-outline:before,.ion-ios-recording:before,.ion-ios-recording-outline:before,.ion-ios-redo:before,.ion-ios-redo-outline:before,.ion-ios-refresh:before,.ion-ios-refresh-empty:before,.ion-ios-refresh-outline:before,.ion-ios-reload:before,.ion-ios-reverse-camera:before,.ion-ios-reverse-camera-outline:before,.ion-ios-rewind:before,.ion-ios-rewind-outline:before,.ion-ios-rose:before,.ion-ios-rose-outline:before,.ion-ios-search:before,.ion-ios-search-strong:before,.ion-ios-settings:before,.ion-ios-settings-strong:before,.ion-ios-shuffle:before,.ion-ios-shuffle-strong:before,.ion-ios-skipbackward:before,.ion-ios-skipbackward-outline:before,.ion-ios-skipforward:before,.ion-ios-skipforward-outline:before,.ion-ios-snowy:before,.ion-ios-speedometer:before,.ion-ios-speedometer-outline:before,.ion-ios-star:before,.ion-ios-star-half:before,.ion-ios-star-outline:before,.ion-ios-stopwatch:before,.ion-ios-stopwatch-outline:before,.ion-ios-sunny:before,.ion-ios-sunny-outline:before,.ion-ios-telephone:before,.ion-ios-telephone-outline:before,.ion-ios-tennisball:before,.ion-ios-tennisball-outline:before,.ion-ios-thunderstorm:before,.ion-ios-thunderstorm-outline:before,.ion-ios-time:before,.ion-ios-time-outline:before,.ion-ios-timer:before,.ion-ios-timer-outline:before,.ion-ios-toggle:before,.ion-ios-toggle-outline:before,.ion-ios-trash:before,.ion-ios-trash-outline:before,.ion-ios-undo:before,.ion-ios-undo-outline:before,.ion-ios-unlocked:before,.ion-ios-unlocked-outline:before,.ion-ios-upload:before,.ion-ios-upload-outline:before,.ion-ios-videocam:before,.ion-ios-videocam-outline:before,.ion-ios-volume-high:before,.ion-ios-volume-low:before,.ion-ios-wineglass:before,.ion-ios-wineglass-outline:before,.ion-ios-world:before,.ion-ios-world-outline:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-lock-combination:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon:before,.ion-navicon-round:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paintbrush:before,.ion-paintbucket:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person:before,.ion-person-add:before,.ion-person-stalker:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply:before,.ion-reply-all:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad:before,.ion-sad-outline:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android:before,.ion-social-android-outline:before,.ion-social-angular:before,.ion-social-angular-outline:before,.ion-social-apple:before,.ion-social-apple-outline:before,.ion-social-bitcoin:before,.ion-social-bitcoin-outline:before,.ion-social-buffer:before,.ion-social-buffer-outline:before,.ion-social-chrome:before,.ion-social-chrome-outline:before,.ion-social-codepen:before,.ion-social-codepen-outline:before,.ion-social-css3:before,.ion-social-css3-outline:before,.ion-social-designernews:before,.ion-social-designernews-outline:before,.ion-social-dribbble:before,.ion-social-dribbble-outline:before,.ion-social-dropbox:before,.ion-social-dropbox-outline:before,.ion-social-euro:before,.ion-social-euro-outline:before,.ion-social-facebook:before,.ion-social-facebook-outline:before,.ion-social-foursquare:before,.ion-social-foursquare-outline:before,.ion-social-freebsd-devil:before,.ion-social-github:before,.ion-social-github-outline:before,.ion-social-google:before,.ion-social-google-outline:before,.ion-social-googleplus:before,.ion-social-googleplus-outline:before,.ion-social-hackernews:before,.ion-social-hackernews-outline:before,.ion-social-html5:before,.ion-social-html5-outline:before,.ion-social-instagram:before,.ion-social-instagram-outline:before,.ion-social-javascript:before,.ion-social-javascript-outline:before,.ion-social-linkedin:before,.ion-social-linkedin-outline:before,.ion-social-markdown:before,.ion-social-nodejs:before,.ion-social-octocat:before,.ion-social-pinterest:before,.ion-social-pinterest-outline:before,.ion-social-python:before,.ion-social-reddit:before,.ion-social-reddit-outline:before,.ion-social-rss:before,.ion-social-rss-outline:before,.ion-social-sass:before,.ion-social-skype:before,.ion-social-skype-outline:before,.ion-social-snapchat:before,.ion-social-snapchat-outline:before,.ion-social-tumblr:before,.ion-social-tumblr-outline:before,.ion-social-tux:before,.ion-social-twitch:before,.ion-social-twitch-outline:before,.ion-social-twitter:before,.ion-social-twitter-outline:before,.ion-social-usd:before,.ion-social-usd-outline:before,.ion-social-vimeo:before,.ion-social-vimeo-outline:before,.ion-social-whatsapp:before,.ion-social-whatsapp-outline:before,.ion-social-windows:before,.ion-social-windows-outline:before,.ion-social-wordpress:before,.ion-social-wordpress-outline:before,.ion-social-yahoo:before,.ion-social-yahoo-outline:before,.ion-social-yen:before,.ion-social-yen-outline:before,.ion-social-youtube:before,.ion-social-youtube-outline:before,.ion-soup-can:before,.ion-soup-can-outline:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle:before,.ion-toggle-filled:before,.ion-transgender:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-tshirt:before,.ion-tshirt-outline:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before{display:inline-block;font-family:"Ionicons";speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-alert:before{content:"\f101"}.ion-alert-circled:before{content:"\f100"}.ion-android-add:before{content:"\f2c7"}.ion-android-add-circle:before{content:"\f359"}.ion-android-alarm-clock:before{content:"\f35a"}.ion-android-alert:before{content:"\f35b"}.ion-android-apps:before{content:"\f35c"}.ion-android-archive:before{content:"\f2c9"}.ion-android-arrow-back:before{content:"\f2ca"}.ion-android-arrow-down:before{content:"\f35d"}.ion-android-arrow-dropdown:before{content:"\f35f"}.ion-android-arrow-dropdown-circle:before{content:"\f35e"}.ion-android-arrow-dropleft:before{content:"\f361"}.ion-android-arrow-dropleft-circle:before{content:"\f360"}.ion-android-arrow-dropright:before{content:"\f363"}.ion-android-arrow-dropright-circle:before{content:"\f362"}.ion-android-arrow-dropup:before{content:"\f365"}.ion-android-arrow-dropup-circle:before{content:"\f364"}.ion-android-arrow-forward:before{content:"\f30f"}.ion-android-arrow-up:before{content:"\f366"}.ion-android-attach:before{content:"\f367"}.ion-android-bar:before{content:"\f368"}.ion-android-bicycle:before{content:"\f369"}.ion-android-boat:before{content:"\f36a"}.ion-android-bookmark:before{content:"\f36b"}.ion-android-bulb:before{content:"\f36c"}.ion-android-bus:before{content:"\f36d"}.ion-android-calendar:before{content:"\f2d1"}.ion-android-call:before{content:"\f2d2"}.ion-android-camera:before{content:"\f2d3"}.ion-android-cancel:before{content:"\f36e"}.ion-android-car:before{content:"\f36f"}.ion-android-cart:before{content:"\f370"}.ion-android-chat:before{content:"\f2d4"}.ion-android-checkbox:before{content:"\f374"}.ion-android-checkbox-blank:before{content:"\f371"}.ion-android-checkbox-outline:before{content:"\f373"}.ion-android-checkbox-outline-blank:before{content:"\f372"}.ion-android-checkmark-circle:before{content:"\f375"}.ion-android-clipboard:before{content:"\f376"}.ion-android-close:before{content:"\f2d7"}.ion-android-cloud:before{content:"\f37a"}.ion-android-cloud-circle:before{content:"\f377"}.ion-android-cloud-done:before{content:"\f378"}.ion-android-cloud-outline:before{content:"\f379"}.ion-android-color-palette:before{content:"\f37b"}.ion-android-compass:before{content:"\f37c"}.ion-android-contact:before{content:"\f2d8"}.ion-android-contacts:before{content:"\f2d9"}.ion-android-contract:before{content:"\f37d"}.ion-android-create:before{content:"\f37e"}.ion-android-delete:before{content:"\f37f"}.ion-android-desktop:before{content:"\f380"}.ion-android-document:before{content:"\f381"}.ion-android-done:before{content:"\f383"}.ion-android-done-all:before{content:"\f382"}.ion-android-download:before{content:"\f2dd"}.ion-android-drafts:before{content:"\f384"}.ion-android-exit:before{content:"\f385"}.ion-android-expand:before{content:"\f386"}.ion-android-favorite:before{content:"\f388"}.ion-android-favorite-outline:before{content:"\f387"}.ion-android-film:before{content:"\f389"}.ion-android-folder:before{content:"\f2e0"}.ion-android-folder-open:before{content:"\f38a"}.ion-android-funnel:before{content:"\f38b"}.ion-android-globe:before{content:"\f38c"}.ion-android-hand:before{content:"\f2e3"}.ion-android-hangout:before{content:"\f38d"}.ion-android-happy:before{content:"\f38e"}.ion-android-home:before{content:"\f38f"}.ion-android-image:before{content:"\f2e4"}.ion-android-laptop:before{content:"\f390"}.ion-android-list:before{content:"\f391"}.ion-android-locate:before{content:"\f2e9"}.ion-android-lock:before{content:"\f392"}.ion-android-mail:before{content:"\f2eb"}.ion-android-map:before{content:"\f393"}.ion-android-menu:before{content:"\f394"}.ion-android-microphone:before{content:"\f2ec"}.ion-android-microphone-off:before{content:"\f395"}.ion-android-more-horizontal:before{content:"\f396"}.ion-android-more-vertical:before{content:"\f397"}.ion-android-navigate:before{content:"\f398"}.ion-android-notifications:before{content:"\f39b"}.ion-android-notifications-none:before{content:"\f399"}.ion-android-notifications-off:before{content:"\f39a"}.ion-android-open:before{content:"\f39c"}.ion-android-options:before{content:"\f39d"}.ion-android-people:before{content:"\f39e"}.ion-android-person:before{content:"\f3a0"}.ion-android-person-add:before{content:"\f39f"}.ion-android-phone-landscape:before{content:"\f3a1"}.ion-android-phone-portrait:before{content:"\f3a2"}.ion-android-pin:before{content:"\f3a3"}.ion-android-plane:before{content:"\f3a4"}.ion-android-playstore:before{content:"\f2f0"}.ion-android-print:before{content:"\f3a5"}.ion-android-radio-button-off:before{content:"\f3a6"}.ion-android-radio-button-on:before{content:"\f3a7"}.ion-android-refresh:before{content:"\f3a8"}.ion-android-remove:before{content:"\f2f4"}.ion-android-remove-circle:before{content:"\f3a9"}.ion-android-restaurant:before{content:"\f3aa"}.ion-android-sad:before{content:"\f3ab"}.ion-android-search:before{content:"\f2f5"}.ion-android-send:before{content:"\f2f6"}.ion-android-settings:before{content:"\f2f7"}.ion-android-share:before{content:"\f2f8"}.ion-android-share-alt:before{content:"\f3ac"}.ion-android-star:before{content:"\f2fc"}.ion-android-star-half:before{content:"\f3ad"}.ion-android-star-outline:before{content:"\f3ae"}.ion-android-stopwatch:before{content:"\f2fd"}.ion-android-subway:before{content:"\f3af"}.ion-android-sunny:before{content:"\f3b0"}.ion-android-sync:before{content:"\f3b1"}.ion-android-textsms:before{content:"\f3b2"}.ion-android-time:before{content:"\f3b3"}.ion-android-train:before{content:"\f3b4"}.ion-android-unlock:before{content:"\f3b5"}.ion-android-upload:before{content:"\f3b6"}.ion-android-volume-down:before{content:"\f3b7"}.ion-android-volume-mute:before{content:"\f3b8"}.ion-android-volume-off:before{content:"\f3b9"}.ion-android-volume-up:before{content:"\f3ba"}.ion-android-walk:before{content:"\f3bb"}.ion-android-warning:before{content:"\f3bc"}.ion-android-watch:before{content:"\f3bd"}.ion-android-wifi:before{content:"\f305"}.ion-aperture:before{content:"\f313"}.ion-archive:before{content:"\f102"}.ion-arrow-down-a:before{content:"\f103"}.ion-arrow-down-b:before{content:"\f104"}.ion-arrow-down-c:before{content:"\f105"}.ion-arrow-expand:before{content:"\f25e"}.ion-arrow-graph-down-left:before{content:"\f25f"}.ion-arrow-graph-down-right:before{content:"\f260"}.ion-arrow-graph-up-left:before{content:"\f261"}.ion-arrow-graph-up-right:before{content:"\f262"}.ion-arrow-left-a:before{content:"\f106"}.ion-arrow-left-b:before{content:"\f107"}.ion-arrow-left-c:before{content:"\f108"}.ion-arrow-move:before{content:"\f263"}.ion-arrow-resize:before{content:"\f264"}.ion-arrow-return-left:before{content:"\f265"}.ion-arrow-return-right:before{content:"\f266"}.ion-arrow-right-a:before{content:"\f109"}.ion-arrow-right-b:before{content:"\f10a"}.ion-arrow-right-c:before{content:"\f10b"}.ion-arrow-shrink:before{content:"\f267"}.ion-arrow-swap:before{content:"\f268"}.ion-arrow-up-a:before{content:"\f10c"}.ion-arrow-up-b:before{content:"\f10d"}.ion-arrow-up-c:before{content:"\f10e"}.ion-asterisk:before{content:"\f314"}.ion-at:before{content:"\f10f"}.ion-backspace:before{content:"\f3bf"}.ion-backspace-outline:before{content:"\f3be"}.ion-bag:before{content:"\f110"}.ion-battery-charging:before{content:"\f111"}.ion-battery-empty:before{content:"\f112"}.ion-battery-full:before{content:"\f113"}.ion-battery-half:before{content:"\f114"}.ion-battery-low:before{content:"\f115"}.ion-beaker:before{content:"\f269"}.ion-beer:before{content:"\f26a"}.ion-bluetooth:before{content:"\f116"}.ion-bonfire:before{content:"\f315"}.ion-bookmark:before{content:"\f26b"}.ion-bowtie:before{content:"\f3c0"}.ion-briefcase:before{content:"\f26c"}.ion-bug:before{content:"\f2be"}.ion-calculator:before{content:"\f26d"}.ion-calendar:before{content:"\f117"}.ion-camera:before{content:"\f118"}.ion-card:before{content:"\f119"}.ion-cash:before{content:"\f316"}.ion-chatbox:before{content:"\f11b"}.ion-chatbox-working:before{content:"\f11a"}.ion-chatboxes:before{content:"\f11c"}.ion-chatbubble:before{content:"\f11e"}.ion-chatbubble-working:before{content:"\f11d"}.ion-chatbubbles:before{content:"\f11f"}.ion-checkmark:before{content:"\f122"}.ion-checkmark-circled:before{content:"\f120"}.ion-checkmark-round:before{content:"\f121"}.ion-chevron-down:before{content:"\f123"}.ion-chevron-left:before{content:"\f124"}.ion-chevron-right:before{content:"\f125"}.ion-chevron-up:before{content:"\f126"}.ion-clipboard:before{content:"\f127"}.ion-clock:before{content:"\f26e"}.ion-close:before{content:"\f12a"}.ion-close-circled:before{content:"\f128"}.ion-close-round:before{content:"\f129"}.ion-closed-captioning:before{content:"\f317"}.ion-cloud:before{content:"\f12b"}.ion-code:before{content:"\f271"}.ion-code-download:before{content:"\f26f"}.ion-code-working:before{content:"\f270"}.ion-coffee:before{content:"\f272"}.ion-compass:before{content:"\f273"}.ion-compose:before{content:"\f12c"}.ion-connection-bars:before{content:"\f274"}.ion-contrast:before{content:"\f275"}.ion-crop:before{content:"\f3c1"}.ion-cube:before{content:"\f318"}.ion-disc:before{content:"\f12d"}.ion-document:before{content:"\f12f"}.ion-document-text:before{content:"\f12e"}.ion-drag:before{content:"\f130"}.ion-earth:before{content:"\f276"}.ion-easel:before{content:"\f3c2"}.ion-edit:before{content:"\f2bf"}.ion-egg:before{content:"\f277"}.ion-eject:before{content:"\f131"}.ion-email:before{content:"\f132"}.ion-email-unread:before{content:"\f3c3"}.ion-erlenmeyer-flask:before{content:"\f3c5"}.ion-erlenmeyer-flask-bubbles:before{content:"\f3c4"}.ion-eye:before{content:"\f133"}.ion-eye-disabled:before{content:"\f306"}.ion-female:before{content:"\f278"}.ion-filing:before{content:"\f134"}.ion-film-marker:before{content:"\f135"}.ion-fireball:before{content:"\f319"}.ion-flag:before{content:"\f279"}.ion-flame:before{content:"\f31a"}.ion-flash:before{content:"\f137"}.ion-flash-off:before{content:"\f136"}.ion-folder:before{content:"\f139"}.ion-fork:before{content:"\f27a"}.ion-fork-repo:before{content:"\f2c0"}.ion-forward:before{content:"\f13a"}.ion-funnel:before{content:"\f31b"}.ion-gear-a:before{content:"\f13d"}.ion-gear-b:before{content:"\f13e"}.ion-grid:before{content:"\f13f"}.ion-hammer:before{content:"\f27b"}.ion-happy:before{content:"\f31c"}.ion-happy-outline:before{content:"\f3c6"}.ion-headphone:before{content:"\f140"}.ion-heart:before{content:"\f141"}.ion-heart-broken:before{content:"\f31d"}.ion-help:before{content:"\f143"}.ion-help-buoy:before{content:"\f27c"}.ion-help-circled:before{content:"\f142"}.ion-home:before{content:"\f144"}.ion-icecream:before{content:"\f27d"}.ion-image:before{content:"\f147"}.ion-images:before{content:"\f148"}.ion-information:before{content:"\f14a"}.ion-information-circled:before{content:"\f149"}.ion-ionic:before{content:"\f14b"}.ion-ios-alarm:before{content:"\f3c8"}.ion-ios-alarm-outline:before{content:"\f3c7"}.ion-ios-albums:before{content:"\f3ca"}.ion-ios-albums-outline:before{content:"\f3c9"}.ion-ios-americanfootball:before{content:"\f3cc"}.ion-ios-americanfootball-outline:before{content:"\f3cb"}.ion-ios-analytics:before{content:"\f3ce"}.ion-ios-analytics-outline:before{content:"\f3cd"}.ion-ios-arrow-back:before{content:"\f3cf"}.ion-ios-arrow-down:before{content:"\f3d0"}.ion-ios-arrow-forward:before{content:"\f3d1"}.ion-ios-arrow-left:before{content:"\f3d2"}.ion-ios-arrow-right:before{content:"\f3d3"}.ion-ios-arrow-thin-down:before{content:"\f3d4"}.ion-ios-arrow-thin-left:before{content:"\f3d5"}.ion-ios-arrow-thin-right:before{content:"\f3d6"}.ion-ios-arrow-thin-up:before{content:"\f3d7"}.ion-ios-arrow-up:before{content:"\f3d8"}.ion-ios-at:before{content:"\f3da"}.ion-ios-at-outline:before{content:"\f3d9"}.ion-ios-barcode:before{content:"\f3dc"}.ion-ios-barcode-outline:before{content:"\f3db"}.ion-ios-baseball:before{content:"\f3de"}.ion-ios-baseball-outline:before{content:"\f3dd"}.ion-ios-basketball:before{content:"\f3e0"}.ion-ios-basketball-outline:before{content:"\f3df"}.ion-ios-bell:before{content:"\f3e2"}.ion-ios-bell-outline:before{content:"\f3e1"}.ion-ios-body:before{content:"\f3e4"}.ion-ios-body-outline:before{content:"\f3e3"}.ion-ios-bolt:before{content:"\f3e6"}.ion-ios-bolt-outline:before{content:"\f3e5"}.ion-ios-book:before{content:"\f3e8"}.ion-ios-book-outline:before{content:"\f3e7"}.ion-ios-bookmarks:before{content:"\f3ea"}.ion-ios-bookmarks-outline:before{content:"\f3e9"}.ion-ios-box:before{content:"\f3ec"}.ion-ios-box-outline:before{content:"\f3eb"}.ion-ios-briefcase:before{content:"\f3ee"}.ion-ios-briefcase-outline:before{content:"\f3ed"}.ion-ios-browsers:before{content:"\f3f0"}.ion-ios-browsers-outline:before{content:"\f3ef"}.ion-ios-calculator:before{content:"\f3f2"}.ion-ios-calculator-outline:before{content:"\f3f1"}.ion-ios-calendar:before{content:"\f3f4"}.ion-ios-calendar-outline:before{content:"\f3f3"}.ion-ios-camera:before{content:"\f3f6"}.ion-ios-camera-outline:before{content:"\f3f5"}.ion-ios-cart:before{content:"\f3f8"}.ion-ios-cart-outline:before{content:"\f3f7"}.ion-ios-chatboxes:before{content:"\f3fa"}.ion-ios-chatboxes-outline:before{content:"\f3f9"}.ion-ios-chatbubble:before{content:"\f3fc"}.ion-ios-chatbubble-outline:before{content:"\f3fb"}.ion-ios-checkmark:before{content:"\f3ff"}.ion-ios-checkmark-empty:before{content:"\f3fd"}.ion-ios-checkmark-outline:before{content:"\f3fe"}.ion-ios-circle-filled:before{content:"\f400"}.ion-ios-circle-outline:before{content:"\f401"}.ion-ios-clock:before{content:"\f403"}.ion-ios-clock-outline:before{content:"\f402"}.ion-ios-close:before{content:"\f406"}.ion-ios-close-empty:before{content:"\f404"}.ion-ios-close-outline:before{content:"\f405"}.ion-ios-cloud:before{content:"\f40c"}.ion-ios-cloud-download:before{content:"\f408"}.ion-ios-cloud-download-outline:before{content:"\f407"}.ion-ios-cloud-outline:before{content:"\f409"}.ion-ios-cloud-upload:before{content:"\f40b"}.ion-ios-cloud-upload-outline:before{content:"\f40a"}.ion-ios-cloudy:before{content:"\f410"}.ion-ios-cloudy-night:before{content:"\f40e"}.ion-ios-cloudy-night-outline:before{content:"\f40d"}.ion-ios-cloudy-outline:before{content:"\f40f"}.ion-ios-cog:before{content:"\f412"}.ion-ios-cog-outline:before{content:"\f411"}.ion-ios-color-filter:before{content:"\f414"}.ion-ios-color-filter-outline:before{content:"\f413"}.ion-ios-color-wand:before{content:"\f416"}.ion-ios-color-wand-outline:before{content:"\f415"}.ion-ios-compose:before{content:"\f418"}.ion-ios-compose-outline:before{content:"\f417"}.ion-ios-contact:before{content:"\f41a"}.ion-ios-contact-outline:before{content:"\f419"}.ion-ios-copy:before{content:"\f41c"}.ion-ios-copy-outline:before{content:"\f41b"}.ion-ios-crop:before{content:"\f41e"}.ion-ios-crop-strong:before{content:"\f41d"}.ion-ios-download:before{content:"\f420"}.ion-ios-download-outline:before{content:"\f41f"}.ion-ios-drag:before{content:"\f421"}.ion-ios-email:before{content:"\f423"}.ion-ios-email-outline:before{content:"\f422"}.ion-ios-eye:before{content:"\f425"}.ion-ios-eye-outline:before{content:"\f424"}.ion-ios-fastforward:before{content:"\f427"}.ion-ios-fastforward-outline:before{content:"\f426"}.ion-ios-filing:before{content:"\f429"}.ion-ios-filing-outline:before{content:"\f428"}.ion-ios-film:before{content:"\f42b"}.ion-ios-film-outline:before{content:"\f42a"}.ion-ios-flag:before{content:"\f42d"}.ion-ios-flag-outline:before{content:"\f42c"}.ion-ios-flame:before{content:"\f42f"}.ion-ios-flame-outline:before{content:"\f42e"}.ion-ios-flask:before{content:"\f431"}.ion-ios-flask-outline:before{content:"\f430"}.ion-ios-flower:before{content:"\f433"}.ion-ios-flower-outline:before{content:"\f432"}.ion-ios-folder:before{content:"\f435"}.ion-ios-folder-outline:before{content:"\f434"}.ion-ios-football:before{content:"\f437"}.ion-ios-football-outline:before{content:"\f436"}.ion-ios-game-controller-a:before{content:"\f439"}.ion-ios-game-controller-a-outline:before{content:"\f438"}.ion-ios-game-controller-b:before{content:"\f43b"}.ion-ios-game-controller-b-outline:before{content:"\f43a"}.ion-ios-gear:before{content:"\f43d"}.ion-ios-gear-outline:before{content:"\f43c"}.ion-ios-glasses:before{content:"\f43f"}.ion-ios-glasses-outline:before{content:"\f43e"}.ion-ios-grid-view:before{content:"\f441"}.ion-ios-grid-view-outline:before{content:"\f440"}.ion-ios-heart:before{content:"\f443"}.ion-ios-heart-outline:before{content:"\f442"}.ion-ios-help:before{content:"\f446"}.ion-ios-help-empty:before{content:"\f444"}.ion-ios-help-outline:before{content:"\f445"}.ion-ios-home:before{content:"\f448"}.ion-ios-home-outline:before{content:"\f447"}.ion-ios-infinite:before{content:"\f44a"}.ion-ios-infinite-outline:before{content:"\f449"}.ion-ios-information:before{content:"\f44d"}.ion-ios-information-empty:before{content:"\f44b"}.ion-ios-information-outline:before{content:"\f44c"}.ion-ios-ionic-outline:before{content:"\f44e"}.ion-ios-keypad:before{content:"\f450"}.ion-ios-keypad-outline:before{content:"\f44f"}.ion-ios-lightbulb:before{content:"\f452"}.ion-ios-lightbulb-outline:before{content:"\f451"}.ion-ios-list:before{content:"\f454"}.ion-ios-list-outline:before{content:"\f453"}.ion-ios-location:before{content:"\f456"}.ion-ios-location-outline:before{content:"\f455"}.ion-ios-locked:before{content:"\f458"}.ion-ios-locked-outline:before{content:"\f457"}.ion-ios-loop:before{content:"\f45a"}.ion-ios-loop-strong:before{content:"\f459"}.ion-ios-medical:before{content:"\f45c"}.ion-ios-medical-outline:before{content:"\f45b"}.ion-ios-medkit:before{content:"\f45e"}.ion-ios-medkit-outline:before{content:"\f45d"}.ion-ios-mic:before{content:"\f461"}.ion-ios-mic-off:before{content:"\f45f"}.ion-ios-mic-outline:before{content:"\f460"}.ion-ios-minus:before{content:"\f464"}.ion-ios-minus-empty:before{content:"\f462"}.ion-ios-minus-outline:before{content:"\f463"}.ion-ios-monitor:before{content:"\f466"}.ion-ios-monitor-outline:before{content:"\f465"}.ion-ios-moon:before{content:"\f468"}.ion-ios-moon-outline:before{content:"\f467"}.ion-ios-more:before{content:"\f46a"}.ion-ios-more-outline:before{content:"\f469"}.ion-ios-musical-note:before{content:"\f46b"}.ion-ios-musical-notes:before{content:"\f46c"}.ion-ios-navigate:before{content:"\f46e"}.ion-ios-navigate-outline:before{content:"\f46d"}.ion-ios-nutrition:before{content:"\f470"}.ion-ios-nutrition-outline:before{content:"\f46f"}.ion-ios-paper:before{content:"\f472"}.ion-ios-paper-outline:before{content:"\f471"}.ion-ios-paperplane:before{content:"\f474"}.ion-ios-paperplane-outline:before{content:"\f473"}.ion-ios-partlysunny:before{content:"\f476"}.ion-ios-partlysunny-outline:before{content:"\f475"}.ion-ios-pause:before{content:"\f478"}.ion-ios-pause-outline:before{content:"\f477"}.ion-ios-paw:before{content:"\f47a"}.ion-ios-paw-outline:before{content:"\f479"}.ion-ios-people:before{content:"\f47c"}.ion-ios-people-outline:before{content:"\f47b"}.ion-ios-person:before{content:"\f47e"}.ion-ios-person-outline:before{content:"\f47d"}.ion-ios-personadd:before{content:"\f480"}.ion-ios-personadd-outline:before{content:"\f47f"}.ion-ios-photos:before{content:"\f482"}.ion-ios-photos-outline:before{content:"\f481"}.ion-ios-pie:before{content:"\f484"}.ion-ios-pie-outline:before{content:"\f483"}.ion-ios-pint:before{content:"\f486"}.ion-ios-pint-outline:before{content:"\f485"}.ion-ios-play:before{content:"\f488"}.ion-ios-play-outline:before{content:"\f487"}.ion-ios-plus:before{content:"\f48b"}.ion-ios-plus-empty:before{content:"\f489"}.ion-ios-plus-outline:before{content:"\f48a"}.ion-ios-pricetag:before{content:"\f48d"}.ion-ios-pricetag-outline:before{content:"\f48c"}.ion-ios-pricetags:before{content:"\f48f"}.ion-ios-pricetags-outline:before{content:"\f48e"}.ion-ios-printer:before{content:"\f491"}.ion-ios-printer-outline:before{content:"\f490"}.ion-ios-pulse:before{content:"\f493"}.ion-ios-pulse-strong:before{content:"\f492"}.ion-ios-rainy:before{content:"\f495"}.ion-ios-rainy-outline:before{content:"\f494"}.ion-ios-recording:before{content:"\f497"}.ion-ios-recording-outline:before{content:"\f496"}.ion-ios-redo:before{content:"\f499"}.ion-ios-redo-outline:before{content:"\f498"}.ion-ios-refresh:before{content:"\f49c"}.ion-ios-refresh-empty:before{content:"\f49a"}.ion-ios-refresh-outline:before{content:"\f49b"}.ion-ios-reload:before{content:"\f49d"}.ion-ios-reverse-camera:before{content:"\f49f"}.ion-ios-reverse-camera-outline:before{content:"\f49e"}.ion-ios-rewind:before{content:"\f4a1"}.ion-ios-rewind-outline:before{content:"\f4a0"}.ion-ios-rose:before{content:"\f4a3"}.ion-ios-rose-outline:before{content:"\f4a2"}.ion-ios-search:before{content:"\f4a5"}.ion-ios-search-strong:before{content:"\f4a4"}.ion-ios-settings:before{content:"\f4a7"}.ion-ios-settings-strong:before{content:"\f4a6"}.ion-ios-shuffle:before{content:"\f4a9"}.ion-ios-shuffle-strong:before{content:"\f4a8"}.ion-ios-skipbackward:before{content:"\f4ab"}.ion-ios-skipbackward-outline:before{content:"\f4aa"}.ion-ios-skipforward:before{content:"\f4ad"}.ion-ios-skipforward-outline:before{content:"\f4ac"}.ion-ios-snowy:before{content:"\f4ae"}.ion-ios-speedometer:before{content:"\f4b0"}.ion-ios-speedometer-outline:before{content:"\f4af"}.ion-ios-star:before{content:"\f4b3"}.ion-ios-star-half:before{content:"\f4b1"}.ion-ios-star-outline:before{content:"\f4b2"}.ion-ios-stopwatch:before{content:"\f4b5"}.ion-ios-stopwatch-outline:before{content:"\f4b4"}.ion-ios-sunny:before{content:"\f4b7"}.ion-ios-sunny-outline:before{content:"\f4b6"}.ion-ios-telephone:before{content:"\f4b9"}.ion-ios-telephone-outline:before{content:"\f4b8"}.ion-ios-tennisball:before{content:"\f4bb"}.ion-ios-tennisball-outline:before{content:"\f4ba"}.ion-ios-thunderstorm:before{content:"\f4bd"}.ion-ios-thunderstorm-outline:before{content:"\f4bc"}.ion-ios-time:before{content:"\f4bf"}.ion-ios-time-outline:before{content:"\f4be"}.ion-ios-timer:before{content:"\f4c1"}.ion-ios-timer-outline:before{content:"\f4c0"}.ion-ios-toggle:before{content:"\f4c3"}.ion-ios-toggle-outline:before{content:"\f4c2"}.ion-ios-trash:before{content:"\f4c5"}.ion-ios-trash-outline:before{content:"\f4c4"}.ion-ios-undo:before{content:"\f4c7"}.ion-ios-undo-outline:before{content:"\f4c6"}.ion-ios-unlocked:before{content:"\f4c9"}.ion-ios-unlocked-outline:before{content:"\f4c8"}.ion-ios-upload:before{content:"\f4cb"}.ion-ios-upload-outline:before{content:"\f4ca"}.ion-ios-videocam:before{content:"\f4cd"}.ion-ios-videocam-outline:before{content:"\f4cc"}.ion-ios-volume-high:before{content:"\f4ce"}.ion-ios-volume-low:before{content:"\f4cf"}.ion-ios-wineglass:before{content:"\f4d1"}.ion-ios-wineglass-outline:before{content:"\f4d0"}.ion-ios-world:before{content:"\f4d3"}.ion-ios-world-outline:before{content:"\f4d2"}.ion-ipad:before{content:"\f1f9"}.ion-iphone:before{content:"\f1fa"}.ion-ipod:before{content:"\f1fb"}.ion-jet:before{content:"\f295"}.ion-key:before{content:"\f296"}.ion-knife:before{content:"\f297"}.ion-laptop:before{content:"\f1fc"}.ion-leaf:before{content:"\f1fd"}.ion-levels:before{content:"\f298"}.ion-lightbulb:before{content:"\f299"}.ion-link:before{content:"\f1fe"}.ion-load-a:before{content:"\f29a"}.ion-load-b:before{content:"\f29b"}.ion-load-c:before{content:"\f29c"}.ion-load-d:before{content:"\f29d"}.ion-location:before{content:"\f1ff"}.ion-lock-combination:before{content:"\f4d4"}.ion-locked:before{content:"\f200"}.ion-log-in:before{content:"\f29e"}.ion-log-out:before{content:"\f29f"}.ion-loop:before{content:"\f201"}.ion-magnet:before{content:"\f2a0"}.ion-male:before{content:"\f2a1"}.ion-man:before{content:"\f202"}.ion-map:before{content:"\f203"}.ion-medkit:before{content:"\f2a2"}.ion-merge:before{content:"\f33f"}.ion-mic-a:before{content:"\f204"}.ion-mic-b:before{content:"\f205"}.ion-mic-c:before{content:"\f206"}.ion-minus:before{content:"\f209"}.ion-minus-circled:before{content:"\f207"}.ion-minus-round:before{content:"\f208"}.ion-model-s:before{content:"\f2c1"}.ion-monitor:before{content:"\f20a"}.ion-more:before{content:"\f20b"}.ion-mouse:before{content:"\f340"}.ion-music-note:before{content:"\f20c"}.ion-navicon:before{content:"\f20e"}.ion-navicon-round:before{content:"\f20d"}.ion-navigate:before{content:"\f2a3"}.ion-network:before{content:"\f341"}.ion-no-smoking:before{content:"\f2c2"}.ion-nuclear:before{content:"\f2a4"}.ion-outlet:before{content:"\f342"}.ion-paintbrush:before{content:"\f4d5"}.ion-paintbucket:before{content:"\f4d6"}.ion-paper-airplane:before{content:"\f2c3"}.ion-paperclip:before{content:"\f20f"}.ion-pause:before{content:"\f210"}.ion-person:before{content:"\f213"}.ion-person-add:before{content:"\f211"}.ion-person-stalker:before{content:"\f212"}.ion-pie-graph:before{content:"\f2a5"}.ion-pin:before{content:"\f2a6"}.ion-pinpoint:before{content:"\f2a7"}.ion-pizza:before{content:"\f2a8"}.ion-plane:before{content:"\f214"}.ion-planet:before{content:"\f343"}.ion-play:before{content:"\f215"}.ion-playstation:before{content:"\f30a"}.ion-plus:before{content:"\f218"}.ion-plus-circled:before{content:"\f216"}.ion-plus-round:before{content:"\f217"}.ion-podium:before{content:"\f344"}.ion-pound:before{content:"\f219"}.ion-power:before{content:"\f2a9"}.ion-pricetag:before{content:"\f2aa"}.ion-pricetags:before{content:"\f2ab"}.ion-printer:before{content:"\f21a"}.ion-pull-request:before{content:"\f345"}.ion-qr-scanner:before{content:"\f346"}.ion-quote:before{content:"\f347"}.ion-radio-waves:before{content:"\f2ac"}.ion-record:before{content:"\f21b"}.ion-refresh:before{content:"\f21c"}.ion-reply:before{content:"\f21e"}.ion-reply-all:before{content:"\f21d"}.ion-ribbon-a:before{content:"\f348"}.ion-ribbon-b:before{content:"\f349"}.ion-sad:before{content:"\f34a"}.ion-sad-outline:before{content:"\f4d7"}.ion-scissors:before{content:"\f34b"}.ion-search:before{content:"\f21f"}.ion-settings:before{content:"\f2ad"}.ion-share:before{content:"\f220"}.ion-shuffle:before{content:"\f221"}.ion-skip-backward:before{content:"\f222"}.ion-skip-forward:before{content:"\f223"}.ion-social-android:before{content:"\f225"}.ion-social-android-outline:before{content:"\f224"}.ion-social-angular:before{content:"\f4d9"}.ion-social-angular-outline:before{content:"\f4d8"}.ion-social-apple:before{content:"\f227"}.ion-social-apple-outline:before{content:"\f226"}.ion-social-bitcoin:before{content:"\f2af"}.ion-social-bitcoin-outline:before{content:"\f2ae"}.ion-social-buffer:before{content:"\f229"}.ion-social-buffer-outline:before{content:"\f228"}.ion-social-chrome:before{content:"\f4db"}.ion-social-chrome-outline:before{content:"\f4da"}.ion-social-codepen:before{content:"\f4dd"}.ion-social-codepen-outline:before{content:"\f4dc"}.ion-social-css3:before{content:"\f4df"}.ion-social-css3-outline:before{content:"\f4de"}.ion-social-designernews:before{content:"\f22b"}.ion-social-designernews-outline:before{content:"\f22a"}.ion-social-dribbble:before{content:"\f22d"}.ion-social-dribbble-outline:before{content:"\f22c"}.ion-social-dropbox:before{content:"\f22f"}.ion-social-dropbox-outline:before{content:"\f22e"}.ion-social-euro:before{content:"\f4e1"}.ion-social-euro-outline:before{content:"\f4e0"}.ion-social-facebook:before{content:"\f231"}.ion-social-facebook-outline:before{content:"\f230"}.ion-social-foursquare:before{content:"\f34d"}.ion-social-foursquare-outline:before{content:"\f34c"}.ion-social-freebsd-devil:before{content:"\f2c4"}.ion-social-github:before{content:"\f233"}.ion-social-github-outline:before{content:"\f232"}.ion-social-google:before{content:"\f34f"}.ion-social-google-outline:before{content:"\f34e"}.ion-social-googleplus:before{content:"\f235"}.ion-social-googleplus-outline:before{content:"\f234"}.ion-social-hackernews:before{content:"\f237"}.ion-social-hackernews-outline:before{content:"\f236"}.ion-social-html5:before{content:"\f4e3"}.ion-social-html5-outline:before{content:"\f4e2"}.ion-social-instagram:before{content:"\f351"}.ion-social-instagram-outline:before{content:"\f350"}.ion-social-javascript:before{content:"\f4e5"}.ion-social-javascript-outline:before{content:"\f4e4"}.ion-social-linkedin:before{content:"\f239"}.ion-social-linkedin-outline:before{content:"\f238"}.ion-social-markdown:before{content:"\f4e6"}.ion-social-nodejs:before{content:"\f4e7"}.ion-social-octocat:before{content:"\f4e8"}.ion-social-pinterest:before{content:"\f2b1"}.ion-social-pinterest-outline:before{content:"\f2b0"}.ion-social-python:before{content:"\f4e9"}.ion-social-reddit:before{content:"\f23b"}.ion-social-reddit-outline:before{content:"\f23a"}.ion-social-rss:before{content:"\f23d"}.ion-social-rss-outline:before{content:"\f23c"}.ion-social-sass:before{content:"\f4ea"}.ion-social-skype:before{content:"\f23f"}.ion-social-skype-outline:before{content:"\f23e"}.ion-social-snapchat:before{content:"\f4ec"}.ion-social-snapchat-outline:before{content:"\f4eb"}.ion-social-tumblr:before{content:"\f241"}.ion-social-tumblr-outline:before{content:"\f240"}.ion-social-tux:before{content:"\f2c5"}.ion-social-twitch:before{content:"\f4ee"}.ion-social-twitch-outline:before{content:"\f4ed"}.ion-social-twitter:before{content:"\f243"}.ion-social-twitter-outline:before{content:"\f242"}.ion-social-usd:before{content:"\f353"}.ion-social-usd-outline:before{content:"\f352"}.ion-social-vimeo:before{content:"\f245"}.ion-social-vimeo-outline:before{content:"\f244"}.ion-social-whatsapp:before{content:"\f4f0"}.ion-social-whatsapp-outline:before{content:"\f4ef"}.ion-social-windows:before{content:"\f247"}.ion-social-windows-outline:before{content:"\f246"}.ion-social-wordpress:before{content:"\f249"}.ion-social-wordpress-outline:before{content:"\f248"}.ion-social-yahoo:before{content:"\f24b"}.ion-social-yahoo-outline:before{content:"\f24a"}.ion-social-yen:before{content:"\f4f2"}.ion-social-yen-outline:before{content:"\f4f1"}.ion-social-youtube:before{content:"\f24d"}.ion-social-youtube-outline:before{content:"\f24c"}.ion-soup-can:before{content:"\f4f4"}.ion-soup-can-outline:before{content:"\f4f3"}.ion-speakerphone:before{content:"\f2b2"}.ion-speedometer:before{content:"\f2b3"}.ion-spoon:before{content:"\f2b4"}.ion-star:before{content:"\f24e"}.ion-stats-bars:before{content:"\f2b5"}.ion-steam:before{content:"\f30b"}.ion-stop:before{content:"\f24f"}.ion-thermometer:before{content:"\f2b6"}.ion-thumbsdown:before{content:"\f250"}.ion-thumbsup:before{content:"\f251"}.ion-toggle:before{content:"\f355"}.ion-toggle-filled:before{content:"\f354"}.ion-transgender:before{content:"\f4f5"}.ion-trash-a:before{content:"\f252"}.ion-trash-b:before{content:"\f253"}.ion-trophy:before{content:"\f356"}.ion-tshirt:before{content:"\f4f7"}.ion-tshirt-outline:before{content:"\f4f6"}.ion-umbrella:before{content:"\f2b7"}.ion-university:before{content:"\f357"}.ion-unlocked:before{content:"\f254"}.ion-upload:before{content:"\f255"}.ion-usb:before{content:"\f2b8"}.ion-videocamera:before{content:"\f256"}.ion-volume-high:before{content:"\f257"}.ion-volume-low:before{content:"\f258"}.ion-volume-medium:before{content:"\f259"}.ion-volume-mute:before{content:"\f25a"}.ion-wand:before{content:"\f358"}.ion-waterdrop:before{content:"\f25b"}.ion-wifi:before{content:"\f25c"}.ion-wineglass:before{content:"\f2b9"}.ion-woman:before{content:"\f25d"}.ion-wrench:before{content:"\f2ba"}.ion-xbox:before{content:"\f30c"}\r
--- /dev/null
+/*loading ¶¯»*/\r
+.loading {\r
+ width: 100%;\r
+ height: 100%;\r
+ position: fixed;\r
+ top:0;\r
+ left:0;\r
+ z-index: 999999;\r
+ background-color: rgba(0, 0, 0, 0);\r
+}\r
+.loading .title {\r
+ width: 300px;\r
+ text-align: center;\r
+ margin: 250px auto 0px;\r
+ color: #2F7095;\r
+ font-weight: 900;\r
+ font-size: 24px;\r
+}\r
+.loading .rectbox {\r
+ width: 150px;\r
+ height: 40px;\r
+ margin: 10px auto;\r
+}\r
+.loading .rectbox .title {\r
+ font-size: 14px;\r
+ font-family: 'Microsoft YaHei';\r
+ font-weight: 900;\r
+ text-align: center;\r
+ color: rgba(68, 149, 57, 1);\r
+}\r
+.loading .rectbox .rect {\r
+ width: 25px;\r
+ height: 25px;\r
+ background-color: rgba(68, 149, 57, 1);\r
+ margin: 0 2px auto 3px;\r
+ float: left;\r
+ -webkit-animation: loading 0.48s infinite ease-in-out;\r
+ -o-animation: loading 0.48s infinite ease-in-out;\r
+ animation: loading 0.48s infinite ease-in-out;\r
+}\r
+.loading .rectbox .rect1 {\r
+ -webkit-animation-delay: 0s;\r
+ -moz-animation-delay: 0s;\r
+ -o-animation-delay: 0s;\r
+ animation-delay: 0s;\r
+}\r
+.loading .rectbox .rect2 {\r
+ -webkit-animation-delay: 0.12s;\r
+ -moz-animation-delay: 0.12s;\r
+ -o-animation-delay: 0.12s;\r
+ animation-delay: 0.12s;\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+}\r
+.loading .rectbox .rect3 {\r
+ -webkit-animation-delay: 0.24s;\r
+ -moz-animation-delay: 0.24s;\r
+ -o-animation-delay: 0.24s;\r
+ animation-delay: 0.24s;\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+}\r
+.loading .rectbox .rect4 {\r
+ -webkit-animation-delay: 0.36s;\r
+ -moz-animation-delay: 0.36s;\r
+ -o-animation-delay: 0.36s;\r
+ animation-delay: 0.36s;\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+}\r
+.loading .rectbox .rect5 {\r
+ -webkit-animation-delay: 0.48s;\r
+ -moz-animation-delay: 0.48s;\r
+ -o-animation-delay: 0.48s;\r
+ animation-delay: 0.48s;\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+}\r
+@keyframes loading {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-moz-keyframes loading {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-ms-keyframes loading {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-webkit-keyframes loading {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+\r
+/*vtoy server is running*/\r
+#vtoy-main .loading {\r
+ width: 100%;\r
+ position: relative;\r
+ top:0;\r
+ left:0;\r
+ background-color: rgba(0, 0, 0, 0);\r
+}\r
+#vtoy-main .loading .title {\r
+ width: 300px;\r
+ text-align: center;\r
+ margin: 250px auto 0px;\r
+ color: #449539;\r
+ font-weight: 900;\r
+ font-size: 24px;\r
+}\r
+#vtoy-main .loading .rectbox {\r
+ width: 120px;\r
+ height: 40px;\r
+ margin: 10px auto;\r
+}\r
+#vtoy-main .loading .rectbox .title {\r
+ font-size: 14px;\r
+ font-family: 'Microsoft YaHei';\r
+ font-weight: 900;\r
+ text-align: center;\r
+ color: rgba(68, 149, 57, 1);\r
+}\r
+#vtoy-main .loading .rectbox .rect {\r
+ width: 25px;\r
+ height: 25px;\r
+ background-color: rgba(68, 149, 57, 1);\r
+ margin: 0 2px auto 3px;\r
+ float: left;\r
+ -webkit-animation: loading 1.44s infinite ease-in-out;\r
+ -o-animation: loading 0.48s infinite ease-in-out;\r
+ animation: loading 0.48s infinite ease-in-out;\r
+}\r
+#vtoy-main .loading .rectbox .rect1 {\r
+ -webkit-animation-delay: 0s;\r
+ -moz-animation-delay: 0s;\r
+ -o-animation-delay: 0s;\r
+ animation-delay: 0s;\r
+}\r
+#vtoy-main .loading .rectbox .rect2 {\r
+ -webkit-animation-delay: 0.36s;\r
+ -moz-animation-delay: 0.12s;\r
+ -o-animation-delay: 0.12s;\r
+ animation-delay: 0.12s;\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+}\r
+#vtoy-main .loading .rectbox .rect3 {\r
+ -webkit-animation-delay: 0.72s;\r
+ -moz-animation-delay: 0.24s;\r
+ -o-animation-delay: 0.24s;\r
+ animation-delay: 0.24s;\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+}\r
+#vtoy-main .loading .rectbox .rect4 {\r
+ -webkit-animation-delay: 1.08s;\r
+ -moz-animation-delay: 0.36s;\r
+ -o-animation-delay: 0.36s;\r
+ animation-delay: 0.36s;\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+}\r
+#vtoy-main .loading .rectbox .rect5 {\r
+ -webkit-animation-delay: 1.44s;\r
+ -moz-animation-delay: 0.48s;\r
+ -o-animation-delay: 0.48s;\r
+ animation-delay: 0.48s;\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+}\r
+@keyframes running {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-moz-keyframes running {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-ms-keyframes running {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+@-webkit-keyframes running {\r
+ 0% {\r
+ background-color: rgba(68, 149, 57, 1);\r
+ }\r
+ 25% {\r
+ background-color: rgba(68, 149, 57, 0.8);\r
+ }\r
+ 50% {\r
+ background-color: rgba(68, 149, 57, 0.6);\r
+ }\r
+ 75% {\r
+ background-color: rgba(68, 149, 57, 0.4);\r
+ }\r
+ 100% {\r
+ background-color: rgba(68, 149, 57, 0.2);\r
+ }\r
+}\r
+\r
+\r
+\r
+.loadEffect{\r
+ width: 110px;\r
+ height: 110px;\r
+ position: relative;\r
+ margin: 0 auto;\r
+ margin-top:0 auto;\r
+}\r
+.loadEffect span{\r
+ display: inline-block;\r
+ width: 20px;\r
+ height: 20px;\r
+ border-radius: 50%;\r
+ background: lightgreen;\r
+ position: absolute;\r
+ -webkit-animation: load 1.04s ease infinite;\r
+}\r
+@-webkit-keyframes load{\r
+ 0%{\r
+ opacity: 1;\r
+ }\r
+ 100%{\r
+ opacity: 0.2;\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+.loadEffect span:nth-child(1){\r
+ left: 40%;\r
+ top: -130%;\r
+ -webkit-animation-delay:0.13s;\r
+}\r
+.loadEffect span:nth-child(2){\r
+ left: 90%;\r
+ top: 8%;\r
+ margin-top:-120%;\r
+ -webkit-animation-delay:0.26s;\r
+}\r
+.loadEffect span:nth-child(3){\r
+ left: 110%;\r
+ top: -80%;\r
+ margin-left: %-100;\r
+ -webkit-animation-delay:0.39s;\r
+}\r
+.loadEffect span:nth-child(4){\r
+ top: -40%;\r
+ left:110%;\r
+ -webkit-animation-delay:0.52s;\r
+}\r
+.loadEffect span:nth-child(5){\r
+ left: 40%;\r
+ top: 0;\r
+ margin-top:10%;\r
+ -webkit-animation-delay:0.65s;\r
+}\r
+.loadEffect span:nth-child(6){\r
+ left: -20%;\r
+ bottom:120%;\r
+ -webkit-animation-delay:0.78s;\r
+}\r
+.loadEffect span:nth-child(7){\r
+ bottom: 160%;\r
+ left: -20%;\r
+ -webkit-animation-delay:0.91s;\r
+}\r
+.loadEffect span:nth-child(8){\r
+ bottom: 200%;\r
+ left: -10%;\r
+ -webkit-animation-delay:1.04s;\r
+}\r
--- /dev/null
+/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function qa(){}qa.prototype=d.filters=d.pseudos,d.setFilters=new qa,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function ra(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){
+return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ia={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qa[0].contentDocument,b.write(),b.close(),c=sa(a,b),qa.detach()),ra[a]=c),c}var ua=/^margin/,va=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wa=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xa(a,b,c){var d,e,f,g,h=a.style;return c=c||wa(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),va.test(g)&&ua.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function ya(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var za=/^(none|table(?!-c[ea]).+)/,Aa=new RegExp("^("+Q+")(.*)$","i"),Ba=new RegExp("^([+-])=("+Q+")","i"),Ca={position:"absolute",visibility:"hidden",display:"block"},Da={letterSpacing:"0",fontWeight:"400"},Ea=["Webkit","O","Moz","ms"];function Fa(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Ea.length;while(e--)if(b=Ea[e]+c,b in a)return b;return d}function Ga(a,b,c){var d=Aa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Ha(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ia(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wa(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xa(a,b,f),(0>e||null==e)&&(e=a.style[b]),va.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Ha(a,b,c||(g?"border":"content"),d,f)+"px"}function Ja(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",ta(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xa(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fa(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Ba.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fa(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xa(a,b,d)),"normal"===e&&b in Da&&(e=Da[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?za.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Ca,function(){return Ia(a,b,d)}):Ia(a,b,d):void 0},set:function(a,c,d){var e=d&&wa(a);return Ga(a,c,d?Ha(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=ya(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xa,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ua.test(a)||(n.cssHooks[a+b].set=Ga)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wa(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Ja(this,!0)},hide:function(){return Ja(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Ka(a,b,c,d,e){return new Ka.prototype.init(a,b,c,d,e)}n.Tween=Ka,Ka.prototype={constructor:Ka,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Ka.propHooks[this.prop];return a&&a.get?a.get(this):Ka.propHooks._default.get(this)},run:function(a){var b,c=Ka.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ka.propHooks._default.set(this),this}},Ka.prototype.init.prototype=Ka.prototype,Ka.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Ka.propHooks.scrollTop=Ka.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Ka.prototype.init,n.fx.step={};var La,Ma,Na=/^(?:toggle|show|hide)$/,Oa=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pa=/queueHooks$/,Qa=[Va],Ra={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Oa.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Oa.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sa(){return setTimeout(function(){La=void 0}),La=n.now()}function Ta(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ua(a,b,c){for(var d,e=(Ra[b]||[]).concat(Ra["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Va(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||ta(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Na.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?ta(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ua(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wa(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xa(a,b,c){var d,e,f=0,g=Qa.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=La||Sa(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:La||Sa(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wa(k,j.opts.specialEasing);g>f;f++)if(d=Qa[f].call(j,a,k,j.opts))return d;return n.map(k,Ua,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xa,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Ra[c]=Ra[c]||[],Ra[c].unshift(b)},prefilter:function(a,b){b?Qa.unshift(a):Qa.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xa(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pa.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Ta(b,!0),a,d,e)}}),n.each({slideDown:Ta("show"),slideUp:Ta("hide"),slideToggle:Ta("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(La=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),La=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Ma||(Ma=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Ma),Ma=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Ya,Za,$a=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Za:Ya)),
+void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Za={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$a[b]||n.find.attr;$a[b]=function(a,b,d){var e,f;return d||(f=$a[b],$a[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$a[b]=f),e}});var _a=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_a.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ab=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ab," ").indexOf(b)>=0)return!0;return!1}});var bb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cb=n.now(),db=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var eb=/#.*$/,fb=/([?&])_=[^&]*/,gb=/^(.*?):[ \t]*([^\r\n]*)$/gm,hb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ib=/^(?:GET|HEAD)$/,jb=/^\/\//,kb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lb={},mb={},nb="*/".concat("*"),ob=a.location.href,pb=kb.exec(ob.toLowerCase())||[];function qb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rb(a,b,c,d){var e={},f=a===mb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function ub(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ob,type:"GET",isLocal:hb.test(pb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sb(sb(a,n.ajaxSettings),b):sb(n.ajaxSettings,a)},ajaxPrefilter:qb(lb),ajaxTransport:qb(mb),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gb.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||ob)+"").replace(eb,"").replace(jb,pb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kb.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pb[1]&&h[2]===pb[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pb[3]||("http:"===pb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rb(lb,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ib.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(db.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fb.test(d)?d.replace(fb,"$1_="+cb++):d+(db.test(d)?"&":"?")+"_="+cb++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nb+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rb(mb,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tb(k,v,f)),u=ub(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vb=/%20/g,wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&").replace(vb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bb=0,Cb={},Db={0:200,1223:204},Eb=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cb)Cb[a]()}),k.cors=!!Eb&&"withCredentials"in Eb,k.ajax=Eb=!!Eb,n.ajaxTransport(function(a){var b;return k.cors||Eb&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cb[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Db[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cb[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Fb=[],Gb=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Fb.pop()||n.expando+"_"+cb++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Gb.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Gb.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Gb,"$1"+e):b.jsonp!==!1&&(b.url+=(db.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Fb.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Hb=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Hb)return Hb.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Ib=a.document.documentElement;function Jb(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Jb(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Ib;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Ib})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Jb(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=ya(k.pixelPosition,function(a,c){return c?(c=xa(a,b),va.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Kb=a.jQuery,Lb=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Lb),b&&a.jQuery===n&&(a.jQuery=Kb),n},typeof b===U&&(a.jQuery=a.$=n),n});
--- /dev/null
+/*! jQuery Validation Plugin - v1.14.0 - 6/30/2015
+ * http://jqueryvalidation.org/
+ * Copyright (c) 2015 Jörn Zaefferer; Licensed MIT */
+!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.settings.submitHandler&&(c.submitButton=b.target),a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.settings.submitHandler?(c.submitButton&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),e=c.settings.submitHandler.call(c,c.currentForm,b),c.submitButton&&d.remove(),void 0!==e?e:!1):!0}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,d=d.concat(c.errorList)}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(b,c){i[c]=f[c],delete f[c],"required"===c&&a(j).removeAttr("aria-required")}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g),a(j).attr("aria-required","true")),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}),a.extend(a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){return!!a.trim(""+a(b).val())},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||-1!==a.inArray(c.keyCode,d)||(b.name in this.submitted||b===this.lastElement)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date ( ISO ).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler),a(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required","true")},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c=this.clean(b),d=this.validationTargetFor(c),e=!0;return this.lastElement=d,void 0===d?delete this.invalid[c.name]:(this.prepareElement(d),this.currentElements=a(d),e=this.check(d)!==!1,e?delete this.invalid[d.name]:this.invalid[d.name]=!0),a(b).attr("aria-invalid",!e),this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),e},showErrors:function(b){if(b){a.extend(this.errorMap,b),this.errorList=[];for(var c in b)this.errorList.push({message:b[c],element:this.findByName(c)[0]});this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors();var b,c=this.elements().removeData("previousValue").removeAttr("aria-invalid");if(this.settings.unhighlight)for(b=0;c[b];b++)this.settings.unhighlight.call(this,c[b],this.settings.errorClass,"");else c.removeClass(this.settings.errorClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){return!this.name&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in c||!b.objectLength(a(this).rules())?!1:(c[this.name]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([]),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d=a(b),e=b.type;return"radio"===e||"checkbox"===e?this.findByName(b.name).filter(":checked").val():"number"===e&&"undefined"!=typeof b.validity?b.validity.badInput?!1:d.val():(c=d.val(),"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f=a(b).rules(),g=a.map(f,function(a,b){return b}).length,h=!1,i=this.elementValue(b);for(d in f){e={method:d,parameters:f[d]};try{if(c=a.validator.methods[d].call(this,i,b,e.parameters),"dependency-mismatch"===c&&1===g){h=!0;continue}if(h=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(j){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",j),j instanceof TypeError&&(j.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),j}}if(!h)return this.objectLength(f)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a];return void 0},defaultMessage:function(b,c){return this.findDefined(this.customMessage(b.name,c),this.customDataMessage(b,c),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c],"<strong>Warning: No message defined for "+b.name+"</strong>")},formatAndAdd:function(b,c){var d=this.defaultMessage(b,c.method),e=/\$?\{(\d+)\}/g;"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),this.errorList.push({message:d,element:b,method:c.method}),this.errorMap[b.name]=d,this.submitted[b.name]=d},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g=this.errorsFor(b),h=this.idOrName(b),i=a(b).attr("aria-describedby");g.length?(g.removeClass(this.settings.validClass).addClass(this.settings.errorClass),g.html(c)):(g=a("<"+this.settings.errorElement+">").attr("id",h+"-error").addClass(this.settings.errorClass).html(c||""),d=g,this.settings.wrapper&&(d=g.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement(d,a(b)):d.insertAfter(b),g.is("label")?g.attr("for",h):0===g.parents("label[for='"+h+"']").length&&(f=g.attr("id").replace(/(:|\.|\[|\]|\$)/g,"\\$1"),i?i.match(new RegExp("\\b"+f+"\\b"))||(i+=" "+f):i=f,a(b).attr("aria-describedby",i),e=this.groups[b.name],e&&a.each(this.groups,function(b,c){c===e&&a("[name='"+b+"']",this.currentForm).attr("aria-describedby",g.attr("id"))}))),!c&&this.settings.success&&(g.text(""),"string"==typeof this.settings.success?g.addClass(this.settings.success):this.settings.success(g,b)),this.toShow=this.toShow.add(g)},errorsFor:function(b){var c=this.idOrName(b),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+d.replace(/\s+/g,", #")),this.errors().filter(e)},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+b+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return this.dependTypes[typeof a]?this.dependTypes[typeof a](a,b):!0},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(a){this.pending[a.name]||(this.pendingRequest++,this.pending[a.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b){return a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,"remote")})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0!==e.param?e.param:!0:delete b[d]}}),a.each(b,function(d,e){b[d]=a.isFunction(e)?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},creditcard:function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||d>=e},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||c>=a},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.off(".validate-equalTo").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d){if(this.optional(c))return"dependency-mismatch";var e,f,g=this.previousValue(c);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),g.originalMessage=this.settings.messages[c.name].remote,this.settings.messages[c.name].remote=g.message,d="string"==typeof d&&{url:d}||d,g.old===b?g.valid:(g.old=b,e=this,this.startRequest(c),f={},f[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:f,context:e.currentForm,success:function(d){var f,h,i,j=d===!0||"true"===d;e.settings.messages[c.name].remote=g.originalMessage,j?(i=e.formSubmitted,e.prepareElement(c),e.formSubmitted=i,e.successList.push(c),delete e.invalid[c.name],e.showErrors()):(f={},h=d||e.defaultMessage(c,"remote"),f[c.name]=g.message=a.isFunction(h)?h(b):h,e.invalid[c.name]=!0,e.showErrors(f)),g.valid=j,e.stopRequest(c,j)}},d)),"pending")}}});var b,c={};a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)})});
\ No newline at end of file
--- /dev/null
+\r
+/**\r
+ * 用于bootstap框架下提示信息框\r
+ * demo: \r
+ * \r
+ * 模态框\r
+ * Modal.confirm({msg: "是否确定提交?"}).on( function (e) {alert("返回结果:" + e);});\r
+ * Modal.alert({msg:"该记录已删除!"})\r
+ * Modal.process('show'/'hide') 隐藏或显示全屏、进度条\r
+ * \r
+ * 非模态框\r
+ * Message.show({ type : 'S|W|E|I', msg: '提示信息' })\r
+ * Message.success('成功信息')\r
+ * Message.error('错误信息')\r
+ * Message.warn('警告信息')\r
+ * Message.info('提示信息')\r
+ * Message.warn('警告信息',10000) //10000为显示时长\r
+ */\r
+;$(function() {\r
+ \r
+ window.Modal = function() {\r
+ var reg = new RegExp("\\[([^\\[\\]]*?)\\]", 'igm');\r
+ var alr = $("#msgAlertDiv");\r
+ \r
+ if (alr.length == 0) {\r
+ alr = $('<div id="msgAlertDiv" class="modal fade"></div>')\r
+ $("body").append(alr);\r
+ }\r
+ \r
+ var ahtml = ' <div class="modal-dialog">'\r
+ + '<div class="modal-content">'\r
+ + '<div class="modal-header">'\r
+ + '<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>'\r
+ + '<h3 class="modal-title"><B style="font-weight: 400;"> [Title]</B></h3>'\r
+ + '</div>'\r
+ + '<div class="modal-body " style="word-break: break-all;display: block;">'\r
+ + '[Message]'\r
+ + '</div>'\r
+ + '<div class="modal-footer" >'\r
+ + '<button type="button" class="btn btn-success ok" data-dismiss="modal">[BtnOk]</button>'\r
+ + '[UserDefined]'\r
+ + '<button type="button" class="btn cancel" data-dismiss="modal">[BtnCancel]</button>'\r
+ + '</div>' + '</div>' + '</div>';\r
+\r
+ var _alert = function(options) {\r
+ alr.html(ahtml); // 复原\r
+ alr.find('.ok').removeClass('btn-success').addClass('btn-primary');\r
+ \r
+ if (vtoy_chrome_app_mode == 0) {\r
+ if (document.body.clientHeight > 400) {\r
+ alr.find('.modal-dialog').css("top",((document.body.clientHeight - 400) / 2)); \r
+ }\r
+ }\r
+ \r
+ alr.find('.cancel').hide();\r
+ _dialog(options);\r
+\r
+ return {\r
+ on : function(callback) {\r
+ if (callback && callback instanceof Function) {\r
+ alr.find('.ok').click(function() {\r
+ callback(true)\r
+ });\r
+ }\r
+ }\r
+ };\r
+ };\r
+\r
+ var _confirm = function(options) {\r
+ alr.html(ahtml);\r
+ alr.find('.ok').removeClass('btn-primary').addClass('btn-success');\r
+ alr.find('.cancel').show();\r
+ if (vtoy_chrome_app_mode == 0) {\r
+ if (document.body.clientHeight > 400) {\r
+ alr.find('.modal-dialog').css("top",((document.body.clientHeight - 400) / 2)); \r
+ }\r
+ }\r
+ \r
+ _dialog(options);\r
+\r
+ return {\r
+ on : function(callback) {\r
+ if (callback && callback instanceof Function) {\r
+ alr.find('.ok').click(function() {\r
+ callback(true);\r
+ });\r
+ alr.find('.cancel').click(function() {\r
+ callback(false);\r
+ });\r
+ alr.find('.userDefiend').click(function() {\r
+ callback("userDefiend");\r
+ });\r
+ }\r
+ }\r
+ };\r
+ };\r
+\r
+ var _dialog = function(options) {\r
+ var ops = {\r
+ msg : "提示内容",\r
+ title : "操作提示",\r
+ btnok : "确定",\r
+ btncl : "取消",\r
+ userDefined : ""\r
+ };\r
+\r
+ $.extend(ops, options);\r
+\r
+ var html = alr.html().replace(reg, function(node, key) {\r
+ return {\r
+ Title : ops.title,\r
+ Message : ops.msg,\r
+ BtnOk : ops.btnok,\r
+ BtnCancel : ops.btncl,\r
+ UserDefined : ops.userDefined\r
+ }[key];\r
+ });\r
+\r
+ alr.html(html);\r
+ alr.modal({\r
+ width : 500,\r
+ backdrop : 'static'\r
+ });\r
+ }\r
+\r
+ var _process = function(showOrHide,time) {\r
+ var defaultTime = 100;\r
+ if($.isNumeric(time)) {\r
+ defaultTime = time;\r
+ }\r
+ var $proc;\r
+ $proc = $("#vtoy_proc");\r
+ if('hide' === showOrHide) {\r
+ $proc.remove();\r
+ } else if ('show' === showOrHide) {\r
+ if($proc.length == 1) {\r
+ return;\r
+ }\r
+ $(document).find(":focus").blur();\r
+ $proc = $('<div id="vtoy_proc" class="loading"></div>');\r
+ $("body").append($proc);\r
+ setTimeout(function() {\r
+ $proc.replaceWith('<div id="vtoy_proc" class="loading" style="background-color: rgba(0, 0, 0, 0.2);"><div class="rectbox"><div class="title">DATA</div><div class="rect rect1"></div><div class="rect rect2"></div><div class="rect rect3"></div><div class="rect rect4"></div><div class="rect rect5"></div></div></div>');\r
+ }, $.isNumeric(time) ? time : 100);\r
+ } else {\r
+ alert("Modal.process参数必须为show/hide");\r
+ }\r
+ }\r
+ \r
+ return {\r
+ alert : _alert,\r
+ confirm : _confirm,\r
+ process : _process\r
+ }\r
+\r
+ }();\r
+ \r
+ window.Message = function() {\r
+ var _showMsg = function(type, msg, time) {\r
+ var o = {type : type, msg : msg };\r
+ if(time) {\r
+ o.time = time;\r
+ }\r
+ _show(o);\r
+ }\r
+ \r
+ var _show = function(options) {\r
+ var ops = {\r
+ msg : "提示内容",\r
+ type: 'S',\r
+ time: 3000\r
+ };\r
+ $.extend(ops, options);\r
+\r
+ var msg_class = 'alert-success';\r
+ if('S' === ops.type || 's' === ops.type) {\r
+ msg_class = 'alert-success';\r
+ } else if ('E' === ops.type || 'e' === ops.type) {\r
+ msg_class = 'alert-danger';\r
+ } else if ('W' === ops.type || 'w' === ops.type) {\r
+ msg_class = 'alert-warning';\r
+ } else if ('I' === ops.type || 'i' === ops.type) {\r
+ msg_class = 'alert-info';\r
+ } else {\r
+ alert("未知的类型,请使用: w-警告;s-成功;e-失败;i-提示");\r
+ return;\r
+ }\r
+ var $messageContainer = $("#fcss_message");\r
+ if($messageContainer.length === 0) {\r
+ $messageContainer = $('<div id="fcss_message" style="position:fixed; left: 20%; right: 20%; top:0px; z-index:99999999"></div>');\r
+ $messageContainer.appendTo($('body'));\r
+ }\r
+ var $div = $('<div class="alert ' + msg_class + ' alert-dismissible fade in" role="alert" style="margin-bottom: 0; padding-top:10px; padding-bottom: 10px;"></div>');\r
+ var $btn = $('<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>');\r
+ $div.append($btn).append(ops.msg).appendTo($messageContainer);\r
+ setTimeout(function() {\r
+ $div.remove();\r
+ }, ops.time);\r
+ }\r
+ \r
+ var _success = function(msg, time) {\r
+ _showMsg('s', msg, time);\r
+ }\r
+ var _error = function(msg, time) {\r
+ _showMsg('e', msg, time || 6000);\r
+ }\r
+ var _warn = function(msg, time) {\r
+ _showMsg('w', msg, time);\r
+ }\r
+ var _info = function(msg, time) {\r
+ _showMsg('i', msg, time);\r
+ }\r
+ \r
+ return {\r
+ success : _success,\r
+ error : _error,\r
+ warn : _warn,\r
+ info : _info,\r
+ show : _show\r
+ }\r
+ }();\r
+ \r
+});
\ No newline at end of file
--- /dev/null
+\r
+// 包装ajax请求\r
+function callVtoy(p1, p2, p3) {\r
+ var url = '/vtoy/json';\r
+ var data = {};\r
+ var func = function(data) {};\r
+\r
+ if (typeof(p1) === 'string') {\r
+ url = p1;\r
+ } else if (typeof(p1) === 'object') {\r
+ data = p1;\r
+ }\r
+ if (typeof(p2) === 'object') {\r
+ data = p2;\r
+ } else if (typeof(p2) === 'function') {\r
+ func = p2;\r
+ }\r
+ if (typeof(p3) === 'function') {\r
+ func = p3;\r
+ }\r
+\r
+ //vtoy.debug('callVtoy:\t\t\t\t' + JSON.stringify(data));\r
+ $.ajax({\r
+ url: url,\r
+ type: 'post',\r
+ cache: false,\r
+ dataType: 'json',\r
+ data: JSON.stringify(data),\r
+ success: func,\r
+ error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+\r
+ if(undefined === errorThrown)\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_REMOTE_ABNORMAL);\r
+ }\r
+ else if(undefined === errorThrown.length)\r
+ {\r
+ \r
+ }\r
+ else if('' == errorThrown.trim())\r
+ {\r
+ }\r
+ else\r
+ {\r
+ switch(errorThrown)\r
+ {\r
+ case 'timeout':\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_REQUEST_TIMEOUT);\r
+ break;\r
+ }\r
+ case 'Service Unavailable':\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_SERVICE_UNAVAILABLE);\r
+ break;\r
+ }\r
+ case 'abort':\r
+ {\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_COMMUNICATION_ERR + errorThrown);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ },\r
+ complete: function(data) {\r
+ //vtoy.debug('callVtoy\'s resp:\t\t' + data.responseText);\r
+ }\r
+ });\r
+}\r
+\r
+function callVtoyASyncTimeout(time, data, func) {\r
+ $.ajax({\r
+ url: '/vtoy/json',\r
+ type: 'post',\r
+ cache: false,\r
+ dataType: 'json',\r
+ async: true,\r
+ timeout: time,\r
+ data: JSON.stringify(data),\r
+ success: func,\r
+ error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+ if(undefined === errorThrown)\r
+ { \r
+ }\r
+ else if(undefined === errorThrown.length)\r
+ {\r
+ \r
+ }\r
+ else if('' == errorThrown.trim())\r
+ {\r
+ }\r
+ else\r
+ {\r
+ switch(errorThrown)\r
+ {\r
+ case 'timeout':\r
+ {\r
+ callVtoyASyncTimeout(time, data, func);\r
+ break;\r
+ }\r
+ case 'Service Unavailable':\r
+ {\r
+ break;\r
+ }\r
+ case 'abort':\r
+ {\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ },\r
+ complete: function(data) {\r
+ //vtoy.debug('callVtoyASyncTimeout\'s resp:\t' + JSON.stringify(data));\r
+ }\r
+ });\r
+}\r
+\r
+function callVtoySync(data, func) {\r
+ //vtoy.debug('callVtoySync:\t\t\t' + JSON.stringify(data));\r
+ $.ajax({\r
+ url: '/vtoy/json',\r
+ type: 'post',\r
+ cache: false,\r
+ dataType: 'json',\r
+ async: false,\r
+ data: JSON.stringify(data),\r
+ success: function VtoyCallFuncWrapper(data) { \r
+ if (data.result === 'tokenerror') {\r
+ var titlestr = '<span class="fa fa-minus-circle" style="color:#dd4b39; font-weight:bold;"> ' + vtoy_cur_language.STR_ERROR + '</span>';\r
+ var msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_WEB_TOKEN_MISMATCH + '</span>'; \r
+ \r
+ Modal.alert({title:titlestr, msg:msgstr, btnok:vtoy_cur_language.STR_BTN_OK }).on(function(e) {\r
+ window.location.reload(true);\r
+ });\r
+ } \r
+ else if (data.result === 'busy') {\r
+ var titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+ var msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_WEB_SERVICE_BUSY + '</span>';\r
+ Modal.alert({title:titlestr, msg:msgstr, btnok:vtoy_cur_language.STR_BTN_OK }); \r
+ }else {\r
+ func(data);\r
+ }\r
+ },\r
+ error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+ if(undefined === errorThrown)\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_REMOTE_ABNORMAL);\r
+ }\r
+ else if(undefined === errorThrown.length)\r
+ {\r
+ \r
+ }\r
+ else if('' == errorThrown.trim())\r
+ {\r
+ }\r
+ else\r
+ {\r
+ switch(errorThrown)\r
+ {\r
+ case 'timeout':\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_REQUEST_TIMEOUT);\r
+ break;\r
+ }\r
+ case 'Service Unavailable':\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_SERVICE_UNAVAILABLE);\r
+ break;\r
+ }\r
+ case 'abort':\r
+ {\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ Message.error(vtoy_cur_language.STR_WEB_COMMUNICATION_ERR + errorThrown);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ },\r
+ complete: function(data) {\r
+ //vtoy.debug('callVtoySync\'s resp:\t' + JSON.stringify(data));\r
+ }\r
+ });\r
+}\r
+\r
+var vtoy = { \r
+ baseurl : '',\r
+ status: '', \r
+ scan: {\r
+ time: 3,\r
+ ret: []\r
+ }\r
+}\r
+\r
+// \r
+String.prototype.endsWith = function(str) {\r
+ if (str == null || str == "" || this.length == 0 || str.length > this.length)\r
+ return false;\r
+ if (this.substring(this.length - str.length) == str)\r
+ return true;\r
+ else\r
+ return false;\r
+}\r
+\r
+window.Message = function() {\r
+ var _showMsg = function(type, msg, time) {\r
+ var o = {type : type, msg : msg };\r
+ if(time) {\r
+ o.time = time;\r
+ }\r
+ _show(o);\r
+ }\r
+ \r
+ var _show = function(options) {\r
+ var ops = {\r
+ msg : "提示内容",\r
+ type: 'S',\r
+ time: 3000\r
+ };\r
+ $.extend(ops, options);\r
+\r
+ var msg_class = 'alert-success';\r
+ if('S' === ops.type || 's' === ops.type) {\r
+ msg_class = 'alert-success';\r
+ } else if ('E' === ops.type || 'e' === ops.type) {\r
+ msg_class = 'alert-danger';\r
+ } else if ('W' === ops.type || 'w' === ops.type) {\r
+ msg_class = 'alert-warning';\r
+ } else if ('I' === ops.type || 'i' === ops.type) {\r
+ msg_class = 'alert-info';\r
+ } else {\r
+ alert("未知的类型,请使用: w-警告;s-成功;e-失败;i-提示");\r
+ return;\r
+ }\r
+ var $messageContainer = $("#fcss_message");\r
+ if($messageContainer.length === 0) {\r
+ $messageContainer = $('<div id="fcss_message" style="position:fixed; left: 20%; right: 20%; top:0px; z-index:99999999"></div>');\r
+ $messageContainer.appendTo($('body'));\r
+ }\r
+ var $div = $('<div class="alert ' + msg_class + ' alert-dismissible fade in" role="alert" style="margin-bottom: 0; padding-top:10px; padding-bottom: 10px;"></div>');\r
+ var $btn = $('<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>');\r
+ $div.append($btn).append(ops.msg).appendTo($messageContainer);\r
+ setTimeout(function() {\r
+ $div.remove();\r
+ }, ops.time);\r
+ }\r
+ \r
+ var _success = function(msg, time) {\r
+ _showMsg('s', msg, time);\r
+ }\r
+ var _error = function(msg, time) { \r
+ _showMsg('e', msg, time || 5000);\r
+ }\r
+ var _warn = function(msg, time) {\r
+ _showMsg('w', msg, time);\r
+ }\r
+ var _info = function(msg, time) {\r
+ _showMsg('i', msg, time);\r
+ }\r
+ \r
+ return {\r
+ success : _success,\r
+ error : _error,\r
+ warn : _warn,\r
+ info : _info,\r
+ show : _show\r
+ }\r
+}();\r
+\r
--- /dev/null
+#!/bin/bash
+
+build_func() {
+ libsuffix=$2
+ toolDir=$3
+
+ XXFLAG='-std=gnu99 -D_FILE_OFFSET_BITS=64'
+ XXLIB="./Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_${libsuffix}.a"
+
+ echo "CC=$1 libsuffix=$libsuffix"
+
+ $1 $XXFLAG -c -Wall -Wextra -Wshadow -Wformat-security -Winit-self \
+ -Wmissing-prototypes -O2 -DLINUX \
+ -I./Ventoy2Disk/Lib/libhttp/include \
+ -DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED \
+ -DUSE_STACK_SIZE=102400 -DNDEBUG -fPIC \
+ ./Ventoy2Disk/Lib/libhttp/include/civetweb.c \
+ -o ./civetweb.o
+
+ $1 $XXFLAG -O2 -Wall -Wno-unused-function -DSTATIC=static -DINIT= \
+ -I./Ventoy2Disk \
+ -I./Ventoy2Disk/Core \
+ -I./Ventoy2Disk/Web \
+ -I./Ventoy2Disk/Include \
+ -I./Ventoy2Disk/Lib/libhttp/include \
+ -I./Ventoy2Disk/Lib/fat_io_lib/include \
+ -I./Ventoy2Disk/Lib/xz-embedded/linux/include \
+ -I./Ventoy2Disk/Lib/xz-embedded/linux/include/linux \
+ -I./Ventoy2Disk/Lib/xz-embedded/userspace \
+ -I ./Ventoy2Disk/Lib/exfat/src/libexfat \
+ -I ./Ventoy2Disk/Lib/exfat/src/mkfs \
+ \
+ -L ./Ventoy2Disk/Lib/fat_io_lib/lib \
+ Ventoy2Disk/*.c \
+ Ventoy2Disk/Core/*.c \
+ Ventoy2Disk/Web/*.c \
+ Ventoy2Disk/Lib/xz-embedded/linux/lib/decompress_unxz.c \
+ Ventoy2Disk/Lib/exfat/src/libexfat/*.c \
+ Ventoy2Disk/Lib/exfat/src/mkfs/*.c \
+ $XXLIB \
+ -l pthread \
+ ./civetweb.o \
+ -o V2D$libsuffix
+
+ rm -f *.o
+
+ if [ "$libsuffix" = "aa64" ]; then
+ aarch64-linux-gnu-strip V2D$libsuffix
+ else
+ strip V2D$libsuffix
+ fi
+
+ rm -f ../INSTALL/tool/$toolDir/V2DServer
+ cp -a V2D$libsuffix ../INSTALL/tool/$toolDir/V2DServer
+}
+
+build_func "gcc" '64' 'x86_64'
+build_func "gcc -m32" '32' 'i386'
+build_func "aarch64-linux-gnu-gcc" 'aa64' 'aarch64'
+
+
--- /dev/null
+#!/bin/bash
+
+echo "generating languages.js ..."
+
+iconv -f utf-16 -t utf-8 ../LANGUAGES/languages.ini | egrep -v '=STR|^;' | egrep 'Language-|STR_' > languages.js
+
+dos2unix languages.js
+
+sed 's/\(STR_.*\)=/"\1":/g' -i languages.js
+
+sed "s/: *'/:\"/g" -i languages.js
+
+sed "s/'\s*$/\",/g" -i languages.js
+
+sed 's/\[Language-\(.*\)\].*/"STR_XXX":""},{"name":"\1",/g' -i languages.js
+
+sed "1s/.*\},/var vtoy_language_data = \[/" -i languages.js
+
+sed 's/\("STR_WEB_COMMUNICATION_ERR"[^,]*\)/\1,/g' -i languages.js
+sed 's/,,/,/g' -i languages.js
+
+CNT=$(grep -v -c ',$' languages.js)
+
+if [ $CNT -gt 0 ]; then
+ echo "====== FAILED ========="
+ grep -v -n ',$' languages.js
+ exit 1
+fi
+
+
+echo '"STR_XXX":""}' >> languages.js
+echo '];' >> languages.js
+
+rm -f WebUI/static/js/languages.js
+mv languages.js WebUI/static/js/
+
+echo "====== SUCCESS =========="
\ No newline at end of file