Task 850: .XXE File Format

Task 850: .XXE File Format

1. Properties of the .XXE File Format Intrinsic to Its File System

The .XXE file format refers to files encoded using the XXEncoding scheme, an obsolete binary-to-text encoding method designed for transferring binary data over text-based protocols. It is similar to UUEncoding but uses a distinct character set to avoid corruption in certain character translations. Based on the specifications, the properties intrinsic to the format that relate to file system attributes (such as metadata embedded within the encoded file) are as follows:

  • Filename: The original name of the encoded binary file, stored in the header line.
  • Permissions Mode: The Unix-style octal permission code (e.g., 644 or 0755) representing file access rights, also stored in the header line.

These properties are embedded directly in the file structure to preserve basic file system metadata during encoding and decoding. The encoded binary content itself represents the original file data but is not considered a separate "property" in this context.

Due to the obsolete nature of the XXEncoding format, which has been largely replaced by modern encodings such as Base64, MIME, or yEnc, no publicly available direct download links for sample .XXE files were identified through comprehensive web searches. Historical references indicate that .XXE files were primarily used in early email and Usenet transmissions, but archived examples are not readily accessible as direct downloads. If required, sample files can be generated using encoding tools, but no existing links meet the criteria for direct downloads.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .XXE File Processing

The following is an embeddable HTML snippet with JavaScript that can be inserted into a Ghost blog post. It enables users to drag and drop a .XXE file onto a designated area, parses the file, extracts the properties (filename and permissions mode), decodes the binary content, and displays the properties on the screen. The decoded binary data is shown as a hexadecimal dump for readability, as it may not be text-based.

Drag and drop a .XXE file here

4. Python Class for .XXE File Processing

The following Python class can open a .XXE file, decode its content, extract and print the properties, and write a new .XXE file from provided filename, mode, and binary data.

class XXEHandler:
    def __init__(self):
        self.alphabet = '+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
        self.decode_map = {c: i for i, c in enumerate(self.alphabet)}
        self.filename = None
        self.mode = None
        self.binary_data = b''

    def open_and_decode(self, filepath):
        with open(filepath, 'r') as f:
            content = f.read()
        lines = content.splitlines()
        i = 0
        while i < len(lines) and not lines[i].startswith('begin '):
            i += 1
        if i == len(lines):
            raise ValueError('Invalid .XXE file: No begin line found')
        parts = lines[i].split()
        self.mode = parts[1]
        self.filename = parts[2]
        binary_list = []
        i += 1
        while i < len(lines) and lines[i].strip() != 'end':
            line = lines[i].strip()
            if line == '+' or not line:
                i += 1
                continue
            len_char = line[0]
            length = self.decode_map.get(len_char, 0)
            data = line[1:]
            j = 0
            while j < len(data):
                group = data[j:j+4]
                if len(group) < 4:
                    group += ' ' * (4 - len(group))  # Pad if needed, though rare
                bits = [self.decode_map.get(c, 0) for c in group]
                byte1 = (bits[0] << 2) | (bits[1] >> 4)
                byte2 = ((bits[1] & 0x0F) << 4) | (bits[2] >> 2)
                byte3 = ((bits[2] & 0x03) << 6) | bits[3]
                bytes_group = [byte1, byte2, byte3]
                remaining = min(3, length - (j // 4 * 3))
                binary_list.extend(bytes_group[:remaining])
                j += 4
            i += 1
        self.binary_data = bytes(binary_list)

    def print_properties(self):
        print(f'Filename: {self.filename}')
        print(f'Permissions Mode: {self.mode}')

    def write(self, output_filepath, filename, mode, binary_data):
        self.filename = filename
        self.mode = mode
        self.binary_data = binary_data
        with open(output_filepath, 'w') as f:
            f.write(f'begin {mode} {filename}\n')
            i = 0
            while i < len(binary_data):
                chunk_size = min(45, len(binary_data) - i)
                len_char = self.alphabet[chunk_size]
                f.write(len_char)
                for j in range(0, chunk_size, 3):
                    group = binary_data[i+j:i+j+3]
                    if len(group) < 3:
                        group += b'\x00' * (3 - len(group))
                    byte1, byte2, byte3 = group
                    out1 = (byte1 >> 2)
                    out2 = ((byte1 & 0x03) << 4) | (byte2 >> 4)
                    out3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6)
                    out4 = byte3 & 0x3F
                    f.write(self.alphabet[out1] + self.alphabet[out2] + self.alphabet[out3] + self.alphabet[out4])
                f.write('\n')
                i += chunk_size
            f.write('+\n')
            f.write('end\n')

# Example usage:
# handler = XXEHandler()
# handler.open_and_decode('example.xxe')
# handler.print_properties()
# handler.write('new.xxe', 'newfile.bin', '644', b'Some binary data')

5. Java Class for .XXE File Processing

The following Java class performs similar operations: opening and decoding a .XXE file, extracting and printing properties, and writing a new .XXE file.

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class XXEHandler {
    private String alphabet = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private Map<Character, Integer> decodeMap = new HashMap<>();
    private String filename;
    private String mode;
    private byte[] binaryData;

    public XXEHandler() {
        for (int i = 0; i < alphabet.length(); i++) {
            decodeMap.put(alphabet.charAt(i), i);
        }
    }

    public void openAndDecode(String filepath) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(filepath));
        String line;
        while ((line = reader.readLine()) != null && !line.startsWith("begin ")) {}
        if (line == null) throw new IOException("Invalid .XXE file: No begin line found");
        String[] parts = line.split("\\s+");
        mode = parts[1];
        filename = parts[2];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ((line = reader.readLine()) != null && !line.trim().equals("end")) {
            line = line.trim();
            if (line.equals("+") || line.isEmpty()) continue;
            char lenChar = line.charAt(0);
            int length = decodeMap.getOrDefault(lenChar, 0);
            String data = line.substring(1);
            for (int j = 0; j < data.length(); j += 4) {
                String group = data.substring(j, Math.min(j + 4, data.length()));
                while (group.length() < 4) group += " ";  // Pad if needed
                int[] bits = new int[4];
                for (int k = 0; k < 4; k++) bits[k] = decodeMap.getOrDefault(group.charAt(k), 0);
                int byte1 = (bits[0] << 2) | (bits[1] >> 4);
                int byte2 = ((bits[1] & 0x0F) << 4) | (bits[2] >> 2);
                int byte3 = ((bits[2] & 0x03) << 6) | bits[3];
                int remaining = Math.min(3, length - (j / 4 * 3));
                if (remaining > 0) baos.write(byte1);
                if (remaining > 1) baos.write(byte2);
                if (remaining > 2) baos.write(byte3);
            }
        }
        reader.close();
        binaryData = baos.toByteArray();
    }

    public void printProperties() {
        System.out.println("Filename: " + filename);
        System.out.println("Permissions Mode: " + mode);
    }

    public void write(String outputFilepath, String newFilename, String newMode, byte[] newBinaryData) throws IOException {
        filename = newFilename;
        mode = newMode;
        binaryData = newBinaryData;
        BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilepath));
        writer.write("begin " + mode + " " + filename + "\n");
        int i = 0;
        while (i < binaryData.length) {
            int chunkSize = Math.min(45, binaryData.length - i);
            char lenChar = alphabet.charAt(chunkSize);
            writer.write(lenChar);
            for (int j = 0; j < chunkSize; j += 3) {
                int b1 = i + j < binaryData.length ? Byte.toUnsignedInt(binaryData[i + j]) : 0;
                int b2 = i + j + 1 < binaryData.length ? Byte.toUnsignedInt(binaryData[i + j + 1]) : 0;
                int b3 = i + j + 2 < binaryData.length ? Byte.toUnsignedInt(binaryData[i + j + 2]) : 0;
                int out1 = (b1 >> 2);
                int out2 = ((b1 & 0x03) << 4) | (b2 >> 4);
                int out3 = ((b2 & 0x0F) << 2) | (b3 >> 6);
                int out4 = b3 & 0x3F;
                writer.write(alphabet.charAt(out1));
                writer.write(alphabet.charAt(out2));
                writer.write(alphabet.charAt(out3));
                writer.write(alphabet.charAt(out4));
            }
            writer.write("\n");
            i += chunkSize;
        }
        writer.write("+\n");
        writer.write("end\n");
        writer.close();
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     XXEHandler handler = new XXEHandler();
    //     handler.openAndDecode("example.xxe");
    //     handler.printProperties();
    //     handler.write("new.xxe", "newfile.bin", "644", new byte[]{/* binary data */});
    // }
}

6. JavaScript Class for .XXE File Processing

The following JavaScript class (ES6) can be used in a Node.js environment (requiring 'fs' module) to open, decode, print properties, and write .XXE files.

const fs = require('fs');

class XXEHandler {
  constructor() {
    this.alphabet = '+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    this.decodeMap = new Map(this.alphabet.split('').map((c, i) => [c, i]));
    this.filename = null;
    this.mode = null;
    this.binaryData = Buffer.alloc(0);
  }

  openAndDecode(filepath) {
    const content = fs.readFileSync(filepath, 'utf8');
    const lines = content.split(/\r?\n/);
    let i = 0;
    while (i < lines.length && !lines[i].startsWith('begin ')) i++;
    if (i === lines.length) throw new Error('Invalid .XXE file: No begin line found');
    const parts = lines[i].trim().split(/\s+/);
    this.mode = parts[1];
    this.filename = parts[2];
    const binaryList = [];
    i++;
    while (i < lines.length && lines[i].trim() !== 'end') {
      const line = lines[i].trim();
      if (line === '+' || line === '') { i++; continue; }
      const lenChar = line[0];
      const length = this.decodeMap.get(lenChar) || 0;
      const data = line.slice(1);
      for (let j = 0; j < data.length; j += 4) {
        let group = data.slice(j, j + 4);
        while (group.length < 4) group += ' ';
        const bits = group.split('').map(c => this.decodeMap.get(c) || 0);
        const byte1 = (bits[0] << 2) | (bits[1] >> 4);
        const byte2 = ((bits[1] & 0x0F) << 4) | (bits[2] >> 2);
        const byte3 = ((bits[2] & 0x03) << 6) | bits[3];
        const remaining = Math.min(3, length - (j / 4 * 3));
        if (remaining > 0) binaryList.push(byte1);
        if (remaining > 1) binaryList.push(byte2);
        if (remaining > 2) binaryList.push(byte3);
      }
      i++;
    }
    this.binaryData = Buffer.from(binaryList);
  }

  printProperties() {
    console.log(`Filename: ${this.filename}`);
    console.log(`Permissions Mode: ${this.mode}`);
  }

  write(outputFilepath, newFilename, newMode, newBinaryData) {
    this.filename = newFilename;
    this.mode = newMode;
    this.binaryData = newBinaryData;
    let output = `begin ${this.mode} ${this.filename}\n`;
    let i = 0;
    while (i < newBinaryData.length) {
      const chunkSize = Math.min(45, newBinaryData.length - i);
      const lenChar = this.alphabet[chunkSize];
      output += lenChar;
      for (let j = 0; j < chunkSize; j += 3) {
        const b1 = newBinaryData[i + j] || 0;
        const b2 = newBinaryData[i + j + 1] || 0;
        const b3 = newBinaryData[i + j + 2] || 0;
        const out1 = (b1 >> 2);
        const out2 = ((b1 & 0x03) << 4) | (b2 >> 4);
        const out3 = ((b2 & 0x0F) << 2) | (b3 >> 6);
        const out4 = b3 & 0x3F;
        output += this.alphabet[out1] + this.alphabet[out2] + this.alphabet[out3] + this.alphabet[out4];
      }
      output += '\n';
      i += chunkSize;
    }
    output += '+\nend\n';
    fs.writeFileSync(outputFilepath, output);
  }
}

// Example usage:
// const handler = new XXEHandler();
// handler.openAndDecode('example.xxe');
// handler.printProperties();
// handler.write('new.xxe', 'newfile.bin', '644', Buffer.from('Some binary data'));

7. C Implementation for .XXE File Processing

Since C does not support classes natively, the following implementation uses a struct with associated functions to achieve equivalent functionality: opening and decoding a .XXE file, extracting and printing properties, and writing a new .XXE file.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ALPHABET "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

typedef struct {
    char *filename;
    char *mode;
    unsigned char *binary_data;
    size_t data_size;
} XXEHandler;

int decode_map[256];

void init_decode_map() {
    memset(decode_map, -1, sizeof(decode_map));
    for (int i = 0; i < 64; i++) {
        decode_map[(unsigned char)ALPHABET[i]] = i;
    }
}

XXEHandler* create_xxe_handler() {
    XXEHandler* handler = malloc(sizeof(XXEHandler));
    handler->filename = NULL;
    handler->mode = NULL;
    handler->binary_data = NULL;
    handler->data_size = 0;
    init_decode_map();
    return handler;
}

void destroy_xxe_handler(XXEHandler* handler) {
    free(handler->filename);
    free(handler->mode);
    free(handler->binary_data);
    free(handler);
}

int open_and_decode(XXEHandler* handler, const char* filepath) {
    FILE* file = fopen(filepath, "r");
    if (!file) return -1;
    char line[1024];
    while (fgets(line, sizeof(line), file) && !strstr(line, "begin ")) {}
    if (feof(file)) { fclose(file); return -1; }
    char* token = strtok(line + 6, " \n");  // Skip "begin "
    handler->mode = strdup(token);
    token = strtok(NULL, " \n");
    handler->filename = strdup(token);
    unsigned char* data = NULL;
    size_t capacity = 0;
    size_t size = 0;
    while (fgets(line, sizeof(line), file)) {
        char* trim = strtok(line, "\n");
        if (strcmp(trim, "end") == 0) break;
        if (strcmp(trim, "+") == 0 || strlen(trim) == 0) continue;
        char len_char = trim[0];
        int length = decode_map[(unsigned char)len_char];
        if (length <= 0) continue;
        char* enc_data = trim + 1;
        int enc_len = strlen(enc_data);
        for (int j = 0; j < enc_len; j += 4) {
            char group[5] = {0};
            strncpy(group, enc_data + j, 4);
            int bits[4] = {0};
            for (int k = 0; k < 4; k++) bits[k] = decode_map[(unsigned char)group[k]];
            unsigned char byte1 = (bits[0] << 2) | (bits[1] >> 4);
            unsigned char byte2 = ((bits[1] & 0x0F) << 4) | (bits[2] >> 2);
            unsigned char byte3 = ((bits[2] & 0x03) << 6) | bits[3];
            int remaining = (length - (j / 4 * 3) > 3) ? 3 : length - (j / 4 * 3);
            if (size + remaining > capacity) {
                capacity = capacity ? capacity * 2 : 1024;
                data = realloc(data, capacity);
            }
            if (remaining > 0) data[size++] = byte1;
            if (remaining > 1) data[size++] = byte2;
            if (remaining > 2) data[size++] = byte3;
        }
    }
    fclose(file);
    handler->binary_data = data;
    handler->data_size = size;
    return 0;
}

void print_properties(XXEHandler* handler) {
    printf("Filename: %s\n", handler->filename);
    printf("Permissions Mode: %s\n", handler->mode);
}

int write_xxe(XXEHandler* handler, const char* output_filepath, const char* new_filename, const char* new_mode, const unsigned char* new_binary_data, size_t new_data_size) {
    free(handler->filename);
    free(handler->mode);
    free(handler->binary_data);
    handler->filename = strdup(new_filename);
    handler->mode = strdup(new_mode);
    handler->binary_data = malloc(new_data_size);
    memcpy(handler->binary_data, new_binary_data, new_data_size);
    handler->data_size = new_data_size;
    FILE* file = fopen(output_filepath, "w");
    if (!file) return -1;
    fprintf(file, "begin %s %s\n", handler->mode, handler->filename);
    size_t i = 0;
    while (i < new_data_size) {
        size_t chunk_size = (new_data_size - i < 45) ? new_data_size - i : 45;
        char len_char = ALPHABET[chunk_size];
        fputc(len_char, file);
        size_t j;
        for (j = 0; j < chunk_size; j += 3) {
            unsigned char b1 = (i + j < new_data_size) ? new_binary_data[i + j] : 0;
            unsigned char b2 = (i + j + 1 < new_data_size) ? new_binary_data[i + j + 1] : 0;
            unsigned char b3 = (i + j + 2 < new_data_size) ? new_binary_data[i + j + 2] : 0;
            int out1 = (b1 >> 2);
            int out2 = ((b1 & 0x03) << 4) | (b2 >> 4);
            int out3 = ((b2 & 0x0F) << 2) | (b3 >> 6);
            int out4 = b3 & 0x3F;
            fputc(ALPHABET[out1], file);
            fputc(ALPHABET[out2], file);
            fputc(ALPHABET[out3], file);
            fputc(ALPHABET[out4], file);
        }
        fputc('\n', file);
        i += chunk_size;
    }
    fprintf(file, "+\nend\n");
    fclose(file);
    return 0;
}

// Example usage:
// int main() {
//     XXEHandler* handler = create_xxe_handler();
//     if (open_and_decode(handler, "example.xxe") == 0) {
//         print_properties(handler);
//     }
//     unsigned char data[] = {/* binary data */};
//     write_xxe(handler, "new.xxe", "newfile.bin", "644", data, sizeof(data));
//     destroy_xxe_handler(handler);
//     return 0;
// }