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.
2. Two direct download links for files of format .VMG
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.
- https://git.bernd-paysan.de/bernd/gforth/raw/0.7.9_20181213/vmgen-ex2/mini-inst.vmg
- https://git.bernd-paysan.de/bernd/gforth/raw/0.7.9_20181213/vmgen-ex2/mini-super.vmg
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.
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;
// }