/** * Decodes filenames given in the content-disposition header according * to RFC5987, such as filename*=utf-8''filename.png. Note that the * language sub-component is defined in RFC5646, and that the filename * is URL encoded (in the charset specified) */ function decodeRfc5987($filename) { $match = array(); if (preg_match("/([\\w!#\$%&+^_`{}~-]+)'([\\w-]*)'(.*)\$/", $filename, $match)) { // XXX: Currently we don't care about the language component. // The encoding hint is sufficient. return Charset::utf8(urldecode($match[3]), $match[1]); } else { return $filename; } }
/** * Perform simple operations to make data consistent between JSON and * XML data types */ function fixup($current) { global $cfg; if ($current['ticket']) { $current = $current['ticket']; } if (!is_array($current)) { return $current; } foreach ($current as $key => &$value) { if ($key == "phone" && is_array($value)) { $value = $value[":text"]; } else { if ($key == "alert") { $value = (bool) (strtolower($value) === 'false' ? false : $value); } else { if ($key == "autorespond") { $value = (bool) (strtolower($value) === 'false' ? false : $value); } else { if ($key == "message") { if (!is_array($value)) { $value = array("body" => $value, "type" => "text/plain"); } else { $value["body"] = $value[":text"]; unset($value[":text"]); } if (isset($value['encoding'])) { $value['body'] = Charset::utf8($value['body'], $value['encoding']); } if (!strcasecmp($value['type'], 'text/html')) { $value = new HtmlThreadBody($value['body']); } else { $value = new TextThreadBody($value['body']); } } else { if ($key == "attachments") { if (!isset($value['file'][':text'])) { $value = $value['file']; } if ($value && is_array($value)) { foreach ($value as &$info) { $info["data"] = $info[":text"]; unset($info[":text"]); } unset($info); } } else { if (is_array($value)) { $value = $this->fixup($value); } } } } } } } unset($value); return $current; }
/** * Read a single typed value from the current input stream. The type is * a 16-bit number receved as an argument which should be one of the * Type* constants defined in this class. * * According to the TNEF spec, all types regardless of their actual * size, must be rounded up in size to the next multiple of four (4) * bytes. Therefore 16-bit values and a strings will need to have extra * padding consumed to keep the stream on track. */ protected function readPhpValue($type) { switch ($type) { case self::TypeUnspecified: case self::TypeNull: case self::TypeError: return null; case self::TypeInt16: // Signed 16-bit value = INT16. $int16 = unpack('v', $this->_getx(4)); $sign = $int16 & 0x8000; if ($sign) { // Use two's compliment $int16 = -((~$int16 & 0xffff) + 1); } return $int16; case self::TypeInt32: // Signed 32-bit value = INT32. // FIXME: Convert to signed value return $this->_geti(32); case self::TypeBoolean: // 16-bit Boolean (non-zero = TRUE) list($bool) = unpack('v', $this->_getx(4)); return 0 != $bool; case self::TypeFlt32: // Signed 32-bit floating point= FLOAT. list($f) = unpack('f', $this->_getx(4)); return $f; case self::TypeFlt64: // 64-bit floating point= DOUBLE. list($d) = unpack('d', $this->_getx(8)); return $d; case self::TypeCurency: // Signed 64-bit int = OLE CURRENCY type. // FIXME: Convert to PHP double return $this->_getx(8); case self::TypeInt64: // 8-byte signed integer= INT64. $x = $this->_getx(8); if (phpversion() >= '5.6.3') { list($x) = unpack('P', $x); } return $x; case self::TypeAppTime: list($d) = unpack('d', $this->_getx(8)); // Application time= OLE DATE type. // Thanks, http://stackoverflow.com/a/10443946/1025836 // Convert to UNIX timestamp, UTC timezone is assumed return ($d - 25569) * 86400; case self::TypeSystime: $a = unpack('Vl/Vh', $this->_getx(8)); // return FileTimeToU64(f) / 10000000 - 11644473600 $ft = $a['l'] / 10000000.0 + $a['h'] * 429.4967296; return $ft - 11644473600; case self::TypeString8: case self::TypeUnicode: case self::TypeBinary: $length = $this->_geti(32); /* Pad to next 4 byte boundary. */ $datalen = $length + (4 - $length % 4) % 4; // Chomp null terminator if ($type == self::TypeString8) { --$length; } elseif ($type == self::TypeUnicode) { $length -= 2; } /* Read and truncate to length. */ $text = substr($this->_getx($datalen), 0, $length); if ($type == self::TypeUnicode) { // TNEF spec says encoding is UTF-16LE $text = Charset::utf8($text, 'UTF-16LE'); } return $text; case self::TypeObject: $length = $this->_geti(32); $oid = $this->_getx(16); if (bin2hex($text) == "0703020000000000c000000000000046") { // TODO: Create stream parser for embedded TNEF stream } $this->skip($length - 16); $this->skip((4 - $length % 4) % 4); return null; case self::TypeCLSID: return $this->_getx(16); default: throw new TnefException(sprintf('0x%04x: Bad data type', $type)); } }
static function buildHashFile($mofile, $outfile = false, $return = false) { if (!$outfile) { $stream = fopen('php://stdout', 'w'); } elseif (is_string($outfile)) { $stream = fopen($outfile, 'w'); } elseif (is_resource($outfile)) { $stream = $outfile; } if (!$stream) { throw new InvalidArgumentException('Expected a filename or valid resource'); } if (!$mofile instanceof FileReader) { $mofile = new FileReader($mofile); } $reader = new parent($mofile, true); if ($reader->short_circuit || $reader->error) { throw new Exception('Unable to initialize MO input file'); } $reader->load_tables(); // Get basic table if (!($table = $reader->cache_translations)) { throw new Exception('Unable to read translations from file'); } // Transcode the table to UTF-8 $header = $table[""]; $info = array(); preg_match('/^content-type: (.*)$/im', $header, $info); $charset = false; if ($content_type = $info[1]) { // Find the charset property $settings = explode(';', $content_type); foreach ($settings as $v) { @(list($prop, $value) = explode('=', trim($v), 2)); if (strtolower($prop) == 'charset') { $charset = trim($value); break; } } } if ($charset && strcasecmp($charset, 'utf-8') !== 0) { foreach ($table as $orig => $trans) { $table[Charset::utf8($orig, $charset)] = Charset::utf8($trans, $charset); unset($table[$orig]); } } // Add in some meta-data $table[self::META_HEADER] = array('Revision' => $reader->revision, 'Total-Strings' => $reader->total, 'Table-Size' => count($table), 'Build-Timestamp' => gmdate(DATE_RFC822), 'Format-Version' => 'A', 'Encoding' => 'UTF-8'); // Serialize the PHP array and write to output $contents = sprintf('<?php return %s;', var_export($table, true)); if ($return) { return $contents; } else { fwrite($stream, $contents); } }