From f8d9b1eb8c7edd7174eb24dbfde1516212fe7a9b Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Sun, 26 Jan 2020 08:36:57 +0000 Subject: [PATCH] MDL-49103 badges: bake badges with iTXt chunk instead of tEXt The final OB 1.0 specification changed the baking badges method from tEXt to iTXt. Besides, the iTXt chunk should be a signed assertion or the raw JSON (instead of the assertion URL). This has been changed in order to make Moodle OB compliant. Yuliya Bozhko, thanks for the patch! :-) --- badges/lib/bakerlib.php | 46 ++++++++++++++++++++++++++++++++++++----- lib/badgeslib.php | 11 ++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/badges/lib/bakerlib.php b/badges/lib/bakerlib.php index 9507bfa44c12c..23fbcc8018677 100644 --- a/badges/lib/bakerlib.php +++ b/badges/lib/bakerlib.php @@ -114,23 +114,59 @@ public function check_chunks($type, $check) { * @param string $value Currently an assertion URL that is added to an image metadata. * * @return string $result File content with a new chunk as a string. Can be used in file_put_contents() to write to a file. + * @throws \moodle_exception when unsupported chunk type is defined. */ public function add_chunks($type, $key, $value) { if (strlen($key) > 79) { debugging('Key is too big'); } - // tEXt Textual data. - // Keyword: 1-79 bytes (character string) - // Null separator: 1 byte - // Text: n bytes (character string) - $data = $key . "\0" . $value; + $dataparts = []; + if ($type === 'iTXt') { + // International textual data (iTXt). + // Keyword: 1-79 bytes (character string). + $dataparts[] = $key; + // Null separator: 1 byte. + $dataparts[] = "\x00"; + // Compression flag: 1 byte + // A value of 0 means no compression. + $dataparts[] = "\x00"; + // Compression method: 1 byte + // If compression is disabled, the method should also be 0. + $dataparts[] = "\x00"; + // Language tag: 0 or more bytes (character string) + // When there is no language specified leave empty. + + // Null separator: 1 byte. + $dataparts[] = "\x00"; + // Translated keyword: 0 or more bytes + // When there is no translation specified, leave empty. + + // Null separator: 1 byte. + $dataparts[] = "\x00"; + // Text: 0 or more bytes. + $dataparts[] = $value; + } else if ($type === 'tEXt') { + // Textual data (tEXt). + // Keyword: 1-79 bytes (character string). + $dataparts[] = $key; + // Null separator: 1 byte. + $dataparts[] = "\0"; + // Text: n bytes (character string). + $dataparts[] = $value; + } else { + throw new \moodle_exception('Unsupported chunk type: ' . $type); + } + + $data = implode($dataparts); + $crc = pack("N", crc32($type . $data)); $len = pack("N", strlen($data)); // Chunk format: length + type + data + CRC. // CRC is a CRC-32 computed over the chunk type and chunk data. $newchunk = $len . $type . $data . $crc; + $this->_chunks[$type] = $data; $result = substr($this->_contents, 0, $this->_size - 12) . $newchunk diff --git a/lib/badgeslib.php b/lib/badgeslib.php index 572a52086ba75..940d567ca972e 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -503,10 +503,13 @@ function badges_bake($hash, $badgeid, $userid = 0, $pathhash = false) { $contents = $file->get_content(); $filehandler = new PNG_MetaDataHandler($contents); - $assertion = new moodle_url('/badges/assertion.php', array('b' => $hash)); - if ($filehandler->check_chunks("tEXt", "openbadges")) { - // Add assertion URL tExt chunk. - $newcontents = $filehandler->add_chunks("tEXt", "openbadges", $assertion->out(false)); + // For now, the site backpack OB version will be used as default. + $obversion = badges_open_badges_backpack_api(); + $assertion = new core_badges_assertion($hash, $obversion); + $assertionjson = json_encode($assertion->get_badge_assertion()); + if ($filehandler->check_chunks("iTXt", "openbadges")) { + // Add assertion URL iTXt chunk. + $newcontents = $filehandler->add_chunks("iTXt", "openbadges", $assertionjson); $fileinfo = array( 'contextid' => $user_context->id, 'component' => 'badges',