修改为东南天坐标系
This commit is contained in:
181
backend_service/venv/lib/python3.13/site-packages/oxmsg/cli.py
Normal file
181
backend_service/venv/lib/python3.13/site-packages/oxmsg/cli.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# pyright: reportPrivateUsage=false
|
||||
|
||||
"""The command-line interface (CLI) for `python-oxmsg`.
|
||||
|
||||
The CLI provides the command `oxmsg`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Iterator, cast
|
||||
|
||||
import click
|
||||
from olefile import OleFileIO
|
||||
|
||||
from oxmsg.attachment import Attachment
|
||||
from oxmsg.domain import constants as c
|
||||
from oxmsg.domain.encodings import encoding_from_codepage
|
||||
from oxmsg.message import Message
|
||||
from oxmsg.properties import Properties
|
||||
from oxmsg.recipient import Recipient
|
||||
from oxmsg.storage import Storage
|
||||
|
||||
# TODO: add `body` sub-command
|
||||
# TODO: add `detach` sub-command
|
||||
|
||||
|
||||
@click.group()
|
||||
def oxmsg():
|
||||
"""Utility CLI for `python-oxmsg`.
|
||||
|
||||
Provides the subcommands listed below, useful for exploratory or diagnostic purposes.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@oxmsg.command()
|
||||
@click.argument("msg_file_path", type=str)
|
||||
def dump(msg_file_path: str):
|
||||
"""Write a summary of the MSG file's properties to stdout."""
|
||||
msg = Message.load(msg_file_path)
|
||||
print(f"{dump_message_properties(msg)}")
|
||||
|
||||
for r in msg.recipients:
|
||||
print(f"{dump_recipient_properties(r)}")
|
||||
|
||||
for a in msg.attachments:
|
||||
if not a.attached_by_value:
|
||||
print(f"attachment {a.file_name} is not embedded in message, file unavailable")
|
||||
print(f"{dump_attachment_properties(a)}")
|
||||
|
||||
|
||||
@oxmsg.command()
|
||||
@click.argument("msg_file_path", type=str)
|
||||
def storage(msg_file_path: str):
|
||||
"""Summarize low-level "directories and files" structure of MSG."""
|
||||
|
||||
def iter_storage_dump_lines(storage: Storage, prefix: str = "") -> Iterator[str]:
|
||||
yield f"{prefix}{storage.name or 'root'}"
|
||||
for stream in storage.streams:
|
||||
yield f"{prefix} {stream.name}"
|
||||
for s in storage.storages:
|
||||
yield from iter_storage_dump_lines(s, prefix + " ")
|
||||
|
||||
with OleFileIO(msg_file_path) as ole:
|
||||
root_storage = Storage.from_ole(ole)
|
||||
|
||||
print("\n".join(iter_storage_dump_lines(root_storage)))
|
||||
|
||||
|
||||
def dump_message_properties(msg: Message) -> str:
|
||||
"""A summary of this MS-OXMSG object's top-level properties."""
|
||||
string_props_are_unicode = msg.properties.string_props_are_unicode
|
||||
str_prop_encoding = msg.properties._str_prop_encoding
|
||||
internet_code_page = msg.properties.int_prop_value(0x3FDE)
|
||||
internet_encoding = (
|
||||
None if internet_code_page is None else encoding_from_codepage(internet_code_page)
|
||||
)
|
||||
|
||||
def iter_lines() -> Iterator[str]:
|
||||
yield ""
|
||||
yield "------------------"
|
||||
yield "Message Properties"
|
||||
yield "------------------"
|
||||
yield ""
|
||||
yield "header-properties"
|
||||
yield "-----------------"
|
||||
yield f"recipient_count: {msg._header_prop_values[2]}"
|
||||
yield ""
|
||||
yield "distinguished-properties"
|
||||
yield "------------------------"
|
||||
yield f"attachment_count: {msg.attachment_count}"
|
||||
yield f"internet_code_page: {internet_encoding}"
|
||||
yield f"message_class: {msg.message_class}"
|
||||
yield f"sender: {msg.sender}"
|
||||
yield f"sent_date: {msg.sent_date}"
|
||||
yield f"string_props_are_unicode: {string_props_are_unicode}"
|
||||
if not string_props_are_unicode:
|
||||
yield f"string_props_encoding: {str_prop_encoding}"
|
||||
yield f"subject: {repr(msg.subject)}"
|
||||
yield f"message_headers:\n{json.dumps(msg.message_headers, indent=4, sort_keys=True)}"
|
||||
yield ""
|
||||
yield "other properties"
|
||||
yield dump_properties(msg.properties)
|
||||
|
||||
return "\n".join(iter_lines())
|
||||
|
||||
|
||||
def dump_attachment_properties(attachment: Attachment) -> str:
|
||||
"""Report of message properies suitable for writing to the console."""
|
||||
|
||||
def iter_lines() -> Iterator[str]:
|
||||
yield ""
|
||||
yield "---------------------"
|
||||
yield "Attachment Properties"
|
||||
yield "---------------------"
|
||||
yield ""
|
||||
yield "distinguished-properties"
|
||||
yield "------------------------"
|
||||
yield f"attached_by_value: {attachment.attached_by_value}"
|
||||
yield f"file_name: {attachment.file_name}"
|
||||
yield f"last_modified: {attachment.last_modified}"
|
||||
yield f"mime_type: {attachment.mime_type}"
|
||||
yield f"size: {attachment.size:,}"
|
||||
yield ""
|
||||
yield "other properties"
|
||||
yield dump_properties(attachment.properties)
|
||||
|
||||
return "\n".join(iter_lines())
|
||||
|
||||
|
||||
def dump_recipient_properties(recipient: Recipient) -> str:
|
||||
"""Report of message properies suitable for writing to the console."""
|
||||
|
||||
def iter_lines() -> Iterator[str]:
|
||||
yield ""
|
||||
yield "---------------------"
|
||||
yield "Recipient Properties"
|
||||
yield "---------------------"
|
||||
yield ""
|
||||
yield "distinguished-properties"
|
||||
yield "------------------------"
|
||||
yield f"name: {repr(recipient.name)}"
|
||||
yield f"email_address: {recipient.email_address}"
|
||||
yield ""
|
||||
yield "other properties"
|
||||
yield dump_properties(recipient.properties)
|
||||
|
||||
return "\n".join(iter_lines())
|
||||
|
||||
|
||||
def dump_properties(self: Properties) -> str:
|
||||
"""A summary of these properties suitable for printing to the console."""
|
||||
|
||||
def iter_lines() -> Iterator[str]:
|
||||
head_rule = f"{'-'*53}+{'-'*23}+{'-'*70}"
|
||||
yield head_rule
|
||||
yield "property-id" + " " * 42 + "| type" + " " * 18 + "| value"
|
||||
yield head_rule
|
||||
|
||||
for p in self:
|
||||
value = p.value
|
||||
if p.ptyp in (c.PTYP_STRING, c.PTYP_STRING8):
|
||||
value = cast(str, self.str_prop_value(p.pid))
|
||||
value = repr(value)[:64] + "..." if len(value) > 64 else repr(value)
|
||||
elif p.ptyp == c.PTYP_BINARY and p.pid == c.PID_HTML:
|
||||
assert isinstance(value, bytes)
|
||||
value = value[:64]
|
||||
elif isinstance(value, bytes):
|
||||
value = f"{len(value):,} bytes"
|
||||
elif p.ptyp == c.PTYP_INTEGER_32:
|
||||
assert isinstance(value, int)
|
||||
b0 = value & 0xFF
|
||||
b1 = (value & 0xFF00) >> 8
|
||||
b2 = (value & 0xFF0000) >> 16
|
||||
b3 = (value & 0xFF000000) >> 24
|
||||
value = f"{b3:02X} {b2:02X} {b1:02X} {b0:02X}"
|
||||
|
||||
yield f"0x{p.pid:04X} - {p.name:<43} | {p.ptyp_name:<21} | {value}"
|
||||
|
||||
return "\n".join(iter_lines())
|
||||
Reference in New Issue
Block a user