Task 781: .VMG File Format

Task 781: .VMG File Format

1. List of all the properties of this file format intrinsic to its file system

The .VMG file format is primarily associated with vMessage, a structured text format used by Nokia and other vendors for storing SMS messages. It is based on the IrDA IrMC v1.1 specification and uses a vCard-like syntax with nested BEGIN/END blocks. The file is typically encoded in UTF-16LE (each ASCII character followed by a null byte), making it appear binary-like, but it is essentially text. The structure includes headers, sender/recipient details via VCARD, envelopes via VENV, and the body via VBODY.

Based on the specification and sample structures, the intrinsic properties (fields) are:

  • VERSION: The version of the vMessage format (e.g., "1.1").
  • X-IRMC-STATUS: The status of the message (e.g., "UNSENT", "SENT", "READ", "UNREAD", "DRAFT").
  • X-IRMC-BOX: The storage box for the message (e.g., "INBOX", "OUTBOX", "SENT", "DRAFT").
  • Sender Name (from VCARD N): The name of the sender.
  • Sender Telephone (from VCARD TEL): The phone number of the sender.
  • Recipient Name (from inner VCARD N): The name of the recipient (if applicable in nested VENV).
  • Recipient Telephone (from inner VCARD TEL): The phone number of the recipient (if applicable).
  • Date: The date and time the message was sent/received (e.g., "21-12-2005 11:24:50").
  • Message Text (from VBODY): The actual content of the SMS message.
  • From (optional, per spec): Sender identifier.
  • To (optional, per spec): Recipient identifier.
  • Subject (optional, per spec): Message subject (rare in SMS but supported).
  • Message-ID (optional, per spec): Unique identifier for the message.
  • Content-Type (optional, per spec): MIME type of the body (e.g., "text/plain").
  • Priority (optional, per spec): Message priority (e.g., "normal", "high").
  • Attachments (optional, per spec): References to any attached content (rare in basic SMS).

These properties are parsed from the nested structure, and not all may be present in every file.

Note: Direct downloads for Nokia vMessage .VMG files are rare due to privacy concerns with real SMS data. The following are direct links to .VMG files from a public repository, though they are used for a different purpose (VMGen in GForth, a Forth programming tool) rather than vMessage. They are valid .VMG files but follow a different internal structure (text-based VM instructions). No reliable, public direct downloads for Nokia-style vMessage .VMG files were found without risking malware or private data.

3. Ghost blog embedded HTML JavaScript for drag n drop .VMG file dump

This is an HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML-enabled blog). It creates a drag-and-drop area. When a .VMG file is dropped, it reads the file as UTF-16LE, parses the vMessage structure, extracts the properties, and dumps them to the screen in a readable format.

Drag and drop a .VMG file here

4. Python class for .VMG file

This Python class opens a .VMG file, decodes it (using UTF-16LE encoding), parses the properties, prints them to console, and supports writing a new .VMG file with modified properties.

class VMGHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}
        self.read_and_decode()

    def read_and_decode(self):
        with open(self.filepath, 'r', encoding='utf-16le') as f:
            text = f.read()
        lines = text.splitlines()
        self.properties = self.parse_vmg(lines)

    def parse_vmg(self, lines):
        props = {
            'VERSION': '',
            'X-IRMC-STATUS': '',
            'X-IRMC-BOX': '',
            'Sender Name': '',
            'Sender Telephone': '',
            'Recipient Name': '',
            'Recipient Telephone': '',
            'Date': '',
            'Message Text': '',
            'From': '',
            'To': '',
            'Subject': '',
            'Message-ID': '',
            'Content-Type': '',
            'Priority': '',
            'Attachments': ''
        }
        current_section = ''
        in_body = False
        body_text = []
        for line in lines:
            line = line.strip()
            if line.startswith('BEGIN:'):
                current_section = line[6:]
            elif line.startswith('END:'):
                current_section = ''
            elif current_section == 'VMSG' and ':' in line:
                key, value = line.split(':', 1)
                if key in props:
                    props[key] = value.strip()
            elif current_section.startswith('VCARD') and ':' in line:  # First VCARD is sender
                key, value = line.split(':', 1)
                if key == 'N':
                    props['Sender Name'] = value.strip()
                elif key == 'TEL':
                    props['Sender Telephone'] = value.strip()
            elif current_section.startswith('VENV') and ':' in line:
                if 'VCARD' in current_section:
                    key, value = line.split(':', 1)
                    if key == 'N':
                        props['Recipient Name'] = value.strip()
                    elif key == 'TEL':
                        props['Recipient Telephone'] = value.strip()
            elif current_section == 'VBODY':
                if line.startswith('Date:'):
                    props['Date'] = line[5:].strip()
                elif line:
                    body_text.append(line)
        props['Message Text'] = '\n'.join(body_text)
        return props

    def print_properties(self):
        print("VMG Properties:")
        for key, value in self.properties.items():
            if value:
                print(f"{key}: {value}")

    def write(self, new_filepath=None):
        if not new_filepath:
            new_filepath = self.filepath
        content = self.build_vmg_content()
        with open(new_filepath, 'w', encoding='utf-16le') as f:
            f.write(content)

    def build_vmg_content(self):
        content = "BEGIN:VMSG\n"
        if self.properties['VERSION']: content += f"VERSION:{self.properties['VERSION']}\n"
        if self.properties['X-IRMC-STATUS']: content += f"X-IRMC-STATUS:{self.properties['X-IRMC-STATUS']}\n"
        if self.properties['X-IRMC-BOX']: content += f"X-IRMC-BOX:{self.properties['X-IRMC-BOX']}\n"
        content += "BEGIN:VCARD\n"
        if self.properties['VERSION']: content += f"VERSION:{self.properties['VERSION']}\n"
        if self.properties['Sender Name']: content += f"N:{self.properties['Sender Name']}\n"
        if self.properties['Sender Telephone']: content += f"TEL:{self.properties['Sender Telephone']}\n"
        content += "END:VCARD\n"
        content += "BEGIN:VENV\n"
        content += "BEGIN:VCARD\n"
        if self.properties['VERSION']: content += f"VERSION:{self.properties['VERSION']}\n"
        if self.properties['Recipient Name']: content += f"N:{self.properties['Recipient Name']}\n"
        if self.properties['Recipient Telephone']: content += f"TEL:{self.properties['Recipient Telephone']}\n"
        content += "END:VCARD\n"
        content += "BEGIN:VBODY\n"
        if self.properties['Date']: content += f"Date:{self.properties['Date']}\n"
        content += self.properties['Message Text'] + "\n"
        content += "END:VBODY\n"
        content += "END:VENV\n"
        content += "END:VMSG\n"
        return content

# Example usage:
# handler = VMGHandler('example.vmg')
# handler.print_properties()
# handler.write('new.vmg')

5. Java class for .VMG file

This Java class opens a .VMG file, decodes it (reading as UTF-16LE), parses the properties, prints them to console, and supports writing a new .VMG file.

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class VMGHandler {
    private String filepath;
    private Map<String, String> properties = new HashMap<>();

    public VMGHandler(String filepath) {
        this.filepath = filepath;
        readAndDecode();
    }

    private void readAndDecode() {
        StringBuilder text = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filepath), "UTF-16LE"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                text.append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String[] lines = text.toString().split("\n");
        properties = parseVMG(lines);
    }

    private Map<String, String> parseVMG(String[] lines) {
        Map<String, String> props = new HashMap<>();
        props.put("VERSION", "");
        props.put("X-IRMC-STATUS", "");
        props.put("X-IRMC-BOX", "");
        props.put("Sender Name", "");
        props.put("Sender Telephone", "");
        props.put("Recipient Name", "");
        props.put("Recipient Telephone", "");
        props.put("Date", "");
        props.put("Message Text", "");
        props.put("From", "");
        props.put("To", "");
        props.put("Subject", "");
        props.put("Message-ID", "");
        props.put("Content-Type", "");
        props.put("Priority", "");
        props.put("Attachments", "");
        String currentSection = "";
        StringBuilder bodyText = new StringBuilder();
        for (String line : lines) {
            line = line.trim();
            if (line.startsWith("BEGIN:")) {
                currentSection = line.substring(6);
            } else if (line.startsWith("END:")) {
                currentSection = "";
            } else if (currentSection.equals("VMSG") && line.contains(":")) {
                String[] parts = line.split(":", 2);
                String key = parts[0];
                if (props.containsKey(key)) props.put(key, parts[1].trim());
            } else if (currentSection.startsWith("VCARD") && line.contains(":")) {
                String[] parts = line.split(":", 2);
                String key = parts[0];
                if (key.equals("N")) props.put("Sender Name", parts[1].trim());
                if (key.equals("TEL")) props.put("Sender Telephone", parts[1].trim());
            } else if (currentSection.startsWith("VENV") && line.contains(":")) {
                if (currentSection.contains("VCARD")) {
                    String[] parts = line.split(":", 2);
                    String key = parts[0];
                    if (key.equals("N")) props.put("Recipient Name", parts[1].trim());
                    if (key.equals("TEL")) props.put("Recipient Telephone", parts[1].trim());
                }
            } else if (currentSection.equals("VBODY")) {
                if (line.startsWith("Date:")) {
                    props.put("Date", line.substring(5).trim());
                } else if (!line.isEmpty()) {
                    bodyText.append(line).append("\n");
                }
            }
        }
        props.put("Message Text", bodyText.toString().trim());
        return props;
    }

    public void printProperties() {
        System.out.println("VMG Properties:");
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            if (!entry.getValue().isEmpty()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        }
    }

    public void write(String newFilepath) throws IOException {
        if (newFilepath == null) newFilepath = filepath;
        String content = buildVMGContent();
        try (Writer writer = new OutputStreamWriter(new FileOutputStream(newFilepath), StandardCharsets.UTF_16LE)) {
            writer.write(content);
        }
    }

    private String buildVMGContent() {
        StringBuilder content = new StringBuilder();
        content.append("BEGIN:VMSG\n");
        if (!properties.get("VERSION").isEmpty()) content.append("VERSION:").append(properties.get("VERSION")).append("\n");
        if (!properties.get("X-IRMC-STATUS").isEmpty()) content.append("X-IRMC-STATUS:").append(properties.get("X-IRMC-STATUS")).append("\n");
        if (!properties.get("X-IRMC-BOX").isEmpty()) content.append("X-IRMC-BOX:").append(properties.get("X-IRMC-BOX")).append("\n");
        content.append("BEGIN:VCARD\n");
        if (!properties.get("VERSION").isEmpty()) content.append("VERSION:").append(properties.get("VERSION")).append("\n");
        if (!properties.get("Sender Name").isEmpty()) content.append("N:").append(properties.get("Sender Name")).append("\n");
        if (!properties.get("Sender Telephone").isEmpty()) content.append("TEL:").append(properties.get("Sender Telephone")).append("\n");
        content.append("END:VCARD\n");
        content.append("BEGIN:VENV\n");
        content.append("BEGIN:VCARD\n");
        if (!properties.get("VERSION").isEmpty()) content.append("VERSION:").append(properties.get("VERSION")).append("\n");
        if (!properties.get("Recipient Name").isEmpty()) content.append("N:").append(properties.get("Recipient Name")).append("\n");
        if (!properties.get("Recipient Telephone").isEmpty()) content.append("TEL:").append(properties.get("Recipient Telephone")).append("\n");
        content.append("END:VCARD\n");
        content.append("BEGIN:VBODY\n");
        if (!properties.get("Date").isEmpty()) content.append("Date:").append(properties.get("Date")).append("\n");
        content.append(properties.get("Message Text")).append("\n");
        content.append("END:VBODY\n");
        content.append("END:VENV\n");
        content.append("END:VMSG\n");
        return content.toString();
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     VMGHandler handler = new VMGHandler("example.vmg");
    //     handler.printProperties();
    //     handler.write("new.vmg");
    // }
}

6. JavaScript class for .VMG file

This JavaScript class (for Node.js) opens a .VMG file, decodes it, parses the properties, prints them to console, and supports writing a new .VMG file. (Requires 'fs' module.)

const fs = require('fs');

class VMGHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
    this.readAndDecode();
  }

  readAndDecode() {
    const buffer = fs.readFileSync(this.filepath);
    const text = buffer.toString('utf16le');
    const lines = text.split(/\r?\n/);
    this.properties = this.parseVMG(lines);
  }

  parseVMG(lines) {
    const props = {
      VERSION: '',
      'X-IRMC-STATUS': '',
      'X-IRMC-BOX': '',
      'Sender Name': '',
      'Sender Telephone': '',
      'Recipient Name': '',
      'Recipient Telephone': '',
      Date: '',
      'Message Text': '',
      From: '',
      To: '',
      Subject: '',
      'Message-ID': '',
      'Content-Type': '',
      Priority: '',
      Attachments: ''
    };
    let currentSection = '';
    const bodyText = [];
    lines.forEach(line => {
      line = line.trim();
      if (line.startsWith('BEGIN:')) {
        currentSection = line.substring(6);
      } else if (line.startsWith('END:')) {
        currentSection = '';
      } else if (currentSection === 'VMSG' && line.includes(':')) {
        const [key, value] = line.split(':', 2);
        if (key in props) props[key] = value.trim();
      } else if (currentSection.startsWith('VCARD') && line.includes(':')) {
        const [key, value] = line.split(':', 2);
        if (key === 'N') props['Sender Name'] = value.trim();
        if (key === 'TEL') props['Sender Telephone'] = value.trim();
      } else if (currentSection.startsWith('VENV') && line.includes(':')) {
        if (currentSection.includes('VCARD')) {
          const [key, value] = line.split(':', 2);
          if (key === 'N') props['Recipient Name'] = value.trim();
          if (key === 'TEL') props['Recipient Telephone'] = value.trim();
        }
      } else if (currentSection === 'VBODY') {
        if (line.startsWith('Date:')) {
          props.Date = line.substring(5).trim();
        } else if (line) {
          bodyText.push(line);
        }
      }
    });
    props['Message Text'] = bodyText.join('\n');
    return props;
  }

  printProperties() {
    console.log('VMG Properties:');
    for (const [key, value] of Object.entries(this.properties)) {
      if (value) console.log(`${key}: ${value}`);
    }
  }

  write(newFilepath = this.filepath) {
    const content = this.buildVMGContent();
    const buffer = Buffer.from(content, 'utf16le');
    fs.writeFileSync(newFilepath, buffer);
  }

  buildVMGContent() {
    let content = 'BEGIN:VMSG\n';
    if (this.properties.VERSION) content += `VERSION:${this.properties.VERSION}\n`;
    if (this.properties['X-IRMC-STATUS']) content += `X-IRMC-STATUS:${this.properties['X-IRMC-STATUS']}\n`;
    if (this.properties['X-IRMC-BOX']) content += `X-IRMC-BOX:${this.properties['X-IRMC-BOX']}\n`;
    content += 'BEGIN:VCARD\n';
    if (this.properties.VERSION) content += `VERSION:${this.properties.VERSION}\n`;
    if (this.properties['Sender Name']) content += `N:${this.properties['Sender Name']}\n`;
    if (this.properties['Sender Telephone']) content += `TEL:${this.properties['Sender Telephone']}\n`;
    content += 'END:VCARD\n';
    content += 'BEGIN:VENV\n';
    content += 'BEGIN:VCARD\n';
    if (this.properties.VERSION) content += `VERSION:${this.properties.VERSION}\n`;
    if (this.properties['Recipient Name']) content += `N:${this.properties['Recipient Name']}\n`;
    if (this.properties['Recipient Telephone']) content += `TEL:${this.properties['Recipient Telephone']}\n`;
    content += 'END:VCARD\n';
    content += 'BEGIN:VBODY\n';
    if (this.properties.Date) content += `Date:${this.properties.Date}\n`;
    content += `${this.properties['Message Text']}\n`;
    content += 'END:VBODY\n';
    content += 'END:VENV\n';
    content += 'END:VMSG\n';
    return content;
  }
}

// Example usage:
// const handler = new VMGHandler('example.vmg');
// handler.printProperties();
// handler.write('new.vmg');

7. C class for .VMG file

This C++ class opens a .VMG file, decodes it (reading as UTF-16LE via wide chars), parses the properties, prints them to console, and supports writing a new .VMG file.

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <locale>
#include <codecvt>

class VMGHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> properties;

public:
    VMGHandler(const std::string& fp) : filepath(fp) {
        readAndDecode();
    }

    void readAndDecode() {
        std::wifstream file(filepath, std::ios::binary);
        file.imbue(std::locale(file.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>));
        std::vector<std::string> lines;
        std::wstring wline;
        while (std::getline(file, wline)) {
            std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
            lines.push_back(converter.to_bytes(wline));
        }
        properties = parseVMG(lines);
    }

    std::map<std::string, std::string> parseVMG(const std::vector<std::string>& lines) {
        std::map<std::string, std::string> props;
        props["VERSION"] = "";
        props["X-IRMC-STATUS"] = "";
        props["X-IRMC-BOX"] = "";
        props["Sender Name"] = "";
        props["Sender Telephone"] = "";
        props["Recipient Name"] = "";
        props["Recipient Telephone"] = "";
        props["Date"] = "";
        props["Message Text"] = "";
        props["From"] = "";
        props["To"] = "";
        props["Subject"] = "";
        props["Message-ID"] = "";
        props["Content-Type"] = "";
        props["Priority"] = "";
        props["Attachments"] = "";
        std::string currentSection = "";
        std::vector<std::string> bodyText;
        for (const auto& line : lines) {
            std::string trimmed = line;
            // Trim logic (remove leading/trailing spaces)
            size_t start = trimmed.find_first_not_of(" \t");
            if (start == std::string::npos) continue;
            trimmed = trimmed.substr(start);
            size_t end = trimmed.find_last_not_of(" \t");
            trimmed = trimmed.substr(0, end + 1);
            if (trimmed.find("BEGIN:") == 0) {
                currentSection = trimmed.substr(6);
            } else if (trimmed.find("END:") == 0) {
                currentSection = "";
            } else if (currentSection == "VMSG" && trimmed.find(':') != std::string::npos) {
                size_t pos = trimmed.find(':');
                std::string key = trimmed.substr(0, pos);
                std::string value = trimmed.substr(pos + 1);
                if (props.count(key)) props[key] = value;
            } else if (currentSection.find("VCARD") == 0 && trimmed.find(':') != std::string::npos) {
                size_t pos = trimmed.find(':');
                std::string key = trimmed.substr(0, pos);
                std::string value = trimmed.substr(pos + 1);
                if (key == "N") props["Sender Name"] = value;
                if (key == "TEL") props["Sender Telephone"] = value;
            } else if (currentSection.find("VENV") == 0 && trimmed.find(':') != std::string::npos) {
                if (currentSection.find("VCARD") != std::string::npos) {
                    size_t pos = trimmed.find(':');
                    std::string key = trimmed.substr(0, pos);
                    std::string value = trimmed.substr(pos + 1);
                    if (key == "N") props["Recipient Name"] = value;
                    if (key == "TEL") props["Recipient Telephone"] = value;
                }
            } else if (currentSection == "VBODY") {
                if (trimmed.find("Date:") == 0) {
                    props["Date"] = trimmed.substr(5);
                } else if (!trimmed.empty()) {
                    bodyText.push_back(trimmed);
                }
            }
        }
        props["Message Text"] = "";
        for (const auto& bt : bodyText) {
            props["Message Text"] += bt + "\n";
        }
        if (!props["Message Text"].empty()) props["Message Text"].pop_back(); // Remove trailing \n
        return props;
    }

    void printProperties() {
        std::cout << "VMG Properties:" << std::endl;
        for (const auto& pair : properties) {
            if (!pair.second.empty()) {
                std::cout << pair.first << ": " << pair.second << std::endl;
            }
        }
    }

    void write(const std::string& newFilepath = "") {
        std::string target = newFilepath.empty() ? filepath : newFilepath;
        std::wstring content = buildVMGContent();
        std::wofstream file(target, std::ios::binary);
        file.imbue(std::locale(file.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>));
        file << content;
    }

    std::wstring buildVMGContent() {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        std::string narrowContent = "BEGIN:VMSG\n";
        if (!properties["VERSION"].empty()) narrowContent += "VERSION:" + properties["VERSION"] + "\n";
        if (!properties["X-IRMC-STATUS"].empty()) narrowContent += "X-IRMC-STATUS:" + properties["X-IRMC-STATUS"] + "\n";
        if (!properties["X-IRMC-BOX"].empty()) narrowContent += "X-IRMC-BOX:" + properties["X-IRMC-BOX"] + "\n";
        narrowContent += "BEGIN:VCARD\n";
        if (!properties["VERSION"].empty()) narrowContent += "VERSION:" + properties["VERSION"] + "\n";
        if (!properties["Sender Name"].empty()) narrowContent += "N:" + properties["Sender Name"] + "\n";
        if (!properties["Sender Telephone"].empty()) narrowContent += "TEL:" + properties["Sender Telephone"] + "\n";
        narrowContent += "END:VCARD\n";
        narrowContent += "BEGIN:VENV\n";
        narrowContent += "BEGIN:VCARD\n";
        if (!properties["VERSION"].empty()) narrowContent += "VERSION:" + properties["VERSION"] + "\n";
        if (!properties["Recipient Name"].empty()) narrowContent += "N:" + properties["Recipient Name"] + "\n";
        if (!properties["Recipient Telephone"].empty()) narrowContent += "TEL:" + properties["Recipient Telephone"] + "\n";
        narrowContent += "END:VCARD\n";
        narrowContent += "BEGIN:VBODY\n";
        if (!properties["Date"].empty()) narrowContent += "Date:" + properties["Date"] + "\n";
        narrowContent += properties["Message Text"] + "\n";
        narrowContent += "END:VBODY\n";
        narrowContent += "END:VENV\n";
        narrowContent += "END:VMSG\n";
        return converter.from_bytes(narrowContent);
    }
};

// Example usage:
// int main() {
//     VMGHandler handler("example.vmg");
//     handler.printProperties();
//     handler.write("new.vmg");
//     return 0;
// }