Source code for binaryninja.collaboration.snapshot

import ctypes
import datetime
from typing import List, Optional

import binaryninja

from . import _collaboration as core
from . import databasesync, file, project, remote, util


[docs] def nop(*args, **kwargs): return True
[docs] class Snapshot: """ Class representing a remote Snapshot """ def __init__(self, handle): self._handle = core.BNCollaborationSnapshotCopy(handle) def __del__(self): core.BNCollaborationSnapshotFree(self._handle) def __eq__(self, other): if type(other) is not Snapshot: return False return other.id == self.id def __str__(self): return f'<snapshot: {self.name} @ {self.remote.name}.{self.project.name}.{self.file.name}>' def __repr__(self): return f'<snapshot: {self.name} @ {self.remote.name}.{self.project.name}.{self.file.name}>'
[docs] @staticmethod def get_for_local_snapshot(snapshot: 'binaryninja.Snapshot') -> Optional['Snapshot']: """ Get the remote snapshot associated with a local snapshot (if it exists) :param snap: Local snapshot :return: Remote snapshot if it exists, or None if not :raises RuntimeError: If there was an error """ return databasesync.get_remote_snapshot_for_local(snapshot)
@property def file(self) -> 'file.RemoteFile': """ Owning File :return: File object """ value = core.BNCollaborationSnapshotGetFile(self._handle) if value is None: raise RuntimeError(util._last_error()) result = file.RemoteFile(handle=value) core.BNCollaborationFileFree(value) return result @property def project(self) -> 'project.RemoteProject': """ Owning Project :return: Project object """ value = core.BNCollaborationSnapshotGetProject(self._handle) if value is None: raise RuntimeError(util._last_error()) result = project.RemoteProject(handle=value) core.BNCollaborationProjectFree(value) return result @property def remote(self) -> 'remote.Remote': """ Owning Remote :return: Remote object """ value = core.BNCollaborationSnapshotGetRemote(self._handle) if value is None: raise RuntimeError(util._last_error()) result = remote.Remote(handle=value) core.BNCollaborationRemoteFree(value) return result @property def url(self) -> str: """ Web api endpoint url :return: """ return core.BNCollaborationSnapshotGetUrl(self._handle) @property def id(self) -> str: """ Unique id :return: Id string """ return core.BNCollaborationSnapshotGetId(self._handle) @property def name(self) -> str: """ Name of snapshot :return: Name string """ return core.BNCollaborationSnapshotGetName(self._handle) @property def title(self) -> str: """ Get the title of a snapshot: the first line of its name :return: Snapshot title as described """ return core.BNCollaborationSnapshotGetTitle(self._handle) @property def description(self) -> str: """ Get the description of a snapshot: the lines of its name after the first line :return: Snapshot description as described """ return core.BNCollaborationSnapshotGetDescription(self._handle) @property def author(self) -> str: """ Get the user id of the author of a snapshot :return: Snapshot author user id """ return core.BNCollaborationSnapshotGetAuthor(self._handle) @property def author_username(self) -> str: """ Get the username of the author of a snapshot, if possible (vs author which is user id) :return: Snapshot author username """ return core.BNCollaborationSnapshotGetAuthorUsername(self._handle) @property def created(self) -> datetime.datetime: """ Created date of Snapshot :return: Created date """ return datetime.datetime.utcfromtimestamp(core.BNCollaborationSnapshotGetCreated(self._handle)) @property def last_modified(self) -> datetime.datetime: """ Date of last modification to the snapshot :return: Last modified date """ return datetime.datetime.utcfromtimestamp(core.BNCollaborationSnapshotGetLastModified(self._handle)) @property def hash(self) -> str: """ Hash of snapshot data (analysis and markup, etc) No specific hash algorithm is guaranteed :return: Hash string """ return core.BNCollaborationSnapshotGetHash(self._handle) @property def snapshot_file_hash(self) -> str: """ Hash of file contents in snapshot No specific hash algorithm is guaranteed :return: Hash string """ return core.BNCollaborationSnapshotGetSnapshotFileHash(self._handle) @property def has_pulled_undo_entires(self) -> bool: """ If the snapshot has pulled undo entries yet :return: True if they have been pulled """ return core.BNCollaborationSnapshotHasPulledUndoEntries(self._handle) @property def is_finalized(self) -> bool: """ If the snapshot has been finalized on the server and is no longer editable :return: True if finalized """ return core.BNCollaborationSnapshotIsFinalized(self._handle) @property def parent_ids(self) -> List[str]: """ List of ids of all remote parent Snapshots :return: List of id strings :raises: RuntimeError if there was an error """ count = ctypes.c_size_t() value = core.BNCollaborationSnapshotGetParentIds(self._handle, count) if not value: raise RuntimeError(util._last_error()) result = [] for i in range(count.value): result.append(value[i]) core.BNCollaborationFreeIdList(value, count.value) return result @property def child_ids(self) -> List[str]: """ List of ids of all remote child Snapshots :return: List of id strings :raises: RuntimeError if there was an error """ count = ctypes.c_size_t() value = core.BNCollaborationSnapshotGetChildIds(self._handle, count) if not value: raise RuntimeError(util._last_error()) result = [] for i in range(count.value): result.append(value[i]) core.BNCollaborationFreeIdList(value, count.value) return result @property def parents(self) -> List['Snapshot']: """ List of all parent Snapshot objects :return: List of Snapshot objects :raises: RuntimeError if there was an error """ count = ctypes.c_size_t() value = core.BNCollaborationSnapshotGetParents(self._handle, count) if not value: raise RuntimeError(util._last_error()) result = [] for i in range(count.value): result.append(Snapshot(handle=value[i])) core.BNCollaborationSnapshotFreeList(value, count.value) return result @property def children(self) -> List['Snapshot']: """ List of all child Snapshot objects :return: List of Snapshot objects :raises: RuntimeError if there was an error """ count = ctypes.c_size_t() value = core.BNCollaborationSnapshotGetChildren(self._handle, count) if not value: raise RuntimeError(util._last_error()) result = [] for i in range(count.value): result.append(Snapshot(handle=value[i])) core.BNCollaborationSnapshotFreeList(value, count.value) return result @property def undo_entries(self) -> List['UndoEntry']: """ Get the list of undo entries stored in this snapshot. .. note:: If undo entries have not been pulled, they will be pulled upon calling this. :return: List of UndoEntry objects :raises: RuntimeError if there was an error pulling undo entries """ if not self.has_pulled_undo_entires: self.pull_undo_entries() count = ctypes.c_size_t() value = core.BNCollaborationSnapshotGetUndoEntries(self._handle, count) if not value: raise RuntimeError(util._last_error()) result = [] for i in range(count.value): result.append(UndoEntry(handle=value[i])) core.BNCollaborationUndoEntryFreeList(value, count.value) return result
[docs] def get_undo_entry_by_id(self, id: int) -> Optional['UndoEntry']: """ Get a specific Undo Entry in the Snapshot by its id .. note:: If undo entries have not been pulled, they will be pulled upon calling this. :param id: Id of Undo Entry :return: UndoEntry object, if one with that id exists. Else, None :raises: RuntimeError if there was an error pulling undo entries """ if not self.has_pulled_undo_entires: self.pull_undo_entries() value = core.BNCollaborationSnapshotGetUndoEntryById(self._handle, id) if value is None: return None result = UndoEntry(value) core.BNCollaborationUndoEntryFree(value) return result
[docs] def pull_undo_entries(self, progress: 'util.ProgressFuncType' = nop): """ Pull the list of Undo Entries from the Remote. :param progress: Function to call for progress updates :raises: RuntimeError if there was an error pulling undo entries """ if not core.BNCollaborationSnapshotPullUndoEntries(self._handle, util.wrap_progress(progress), None): raise RuntimeError(util._last_error())
[docs] def create_undo_entry(self, parent: Optional[int], data: str) -> 'UndoEntry': """ Create a new Undo Entry in this snapshot. :param parent: Id of parent Undo Entry :param data: Undo Entry contents :return: Created Undo Entry :raises: RuntimeError if there was an error """ value = core.BNCollaborationSnapshotCreateUndoEntry(self._handle, parent is not None, parent if parent is not None else 0, data) if value is None: raise RuntimeError(util._last_error()) result = UndoEntry(value) core.BNCollaborationUndoEntryFree(value) return result
[docs] def finalize(self): """ Mark a snapshot as Finalized, committing it to the Remote, preventing future updates, and allowing snapshots to be children of it. :raises: RuntimeError if there was an error """ if not core.BNCollaborationSnapshotFinalize(self._handle): raise RuntimeError(util._last_error())
[docs] def download_snapshot_file(self, progress: 'util.ProgressFuncType' = nop) -> bytes: """ Download the contents of the file in the Snapshot. :param progress: Function to call for progress updates. Cancels if the function returns False. :return: File contents data :raises: RuntimeError if there was an error or the operation was cancelled """ data = ctypes.POINTER(ctypes.c_uint8)() size = ctypes.c_size_t() if not core.BNCollaborationSnapshotDownloadSnapshotFile(self._handle, util.wrap_progress(progress), None, data, size): raise RuntimeError(util._last_error()) return bytes(ctypes.cast(data, ctypes.POINTER(ctypes.c_uint8 * size.value)).contents)
[docs] def download(self, progress: 'util.ProgressFuncType' = nop) -> bytes: """ Download the snapshot fields blob, compatible with binaryninja.KeyValueStore. :param progress: Function to call for progress updates. Cancels if the function returns False. :return: Snapshot contents data :raises: RuntimeError if there was an error or the operation was cancelled """ data = ctypes.POINTER(ctypes.c_uint8)() size = ctypes.c_size_t() if not core.BNCollaborationSnapshotDownload(self._handle, util.wrap_progress(progress), None, data, size): raise RuntimeError(util._last_error()) return bytes(ctypes.cast(data, ctypes.POINTER(ctypes.c_uint8 * size.value)).contents)
[docs] def download_analysis_cache(self, progress: 'util.ProgressFuncType' = nop) -> bytes: """ Download the analysis cache fields blob, compatible with binaryninja.KeyValueStore. :param progress: Function to call for progress updates. Cancels if the function returns False. :return: Snapshot analysis cache data :raises: RuntimeError if there was an error or the operation was cancelled """ data = ctypes.POINTER(ctypes.c_uint8)() size = ctypes.c_size_t() if not core.BNCollaborationSnapshotDownloadAnalysisCache(self._handle, util.wrap_progress(progress), None, data, size): raise RuntimeError(util._last_error()) return bytes(ctypes.cast(data, ctypes.POINTER(ctypes.c_uint8 * size.value)).contents)
[docs] def get_local_snapshot(self, bv: 'binaryninja.BinaryView') -> Optional['binaryninja.Snapshot']: """ Get the local snapshot associated with a remote snapshot (if it exists) :param bv: BinaryView with database to search :return: Local snapshot, if one exists. Else, None :raises: RuntimeError if there was an error """ if not bv.file.has_database: return None return databasesync.get_local_snapshot_for_remote(self, bv.file.database)
[docs] class UndoEntry: """ Class representing a remote undo entry """ def __init__(self, handle): self._handle = core.BNCollaborationUndoEntryCopy(handle) def __del__(self): core.BNCollaborationUndoEntryFree(self._handle) def __eq__(self, other): if type(other) is not UndoEntry: return False return other.id == self.id @property def snapshot(self) -> 'Snapshot': """ Owning Snapshot :return: Snapshot object """ value = core.BNCollaborationUndoEntryGetSnapshot(self._handle) if value is None: raise RuntimeError(util._last_error()) result = Snapshot(handle=value) core.BNCollaborationSnapshotFree(value) return result @property def file(self) -> 'file.RemoteFile': """ Owning File :return: File object """ value = core.BNCollaborationUndoEntryGetFile(self._handle) if value is None: raise RuntimeError(util._last_error()) result = file.RemoteFile(handle=value) core.BNCollaborationFileFree(value) return result @property def project(self) -> 'project.RemoteProject': """ Owning Project :return: Project object """ value = core.BNCollaborationUndoEntryGetProject(self._handle) if value is None: raise RuntimeError(util._last_error()) result = project.RemoteProject(handle=value) core.BNCollaborationProjectFree(value) return result @property def remote(self) -> 'remote.Remote': """ Owning Remote :return: Remote object """ value = core.BNCollaborationUndoEntryGetRemote(self._handle) if value is None: raise RuntimeError(util._last_error()) result = remote.Remote(handle=value) core.BNCollaborationRemoteFree(value) return result @property def url(self) -> str: """ Web api endpoint url :return: URL String """ return core.BNCollaborationUndoEntryGetUrl(self._handle) @property def id(self) -> int: """ Unique id :return: Id number """ return core.BNCollaborationUndoEntryGetId(self._handle) @property def parent_id(self) -> Optional[int]: """ Id of parent undo entry :return: Parent id number, if there is one, None otherwise """ id = ctypes.c_uint64() if not core.BNCollaborationUndoEntryGetParentId(self._handle, id): return None return id.value @property def data(self) -> str: """ Undo entry contents data :return: Data string """ data = ctypes.c_char_p() if not core.BNCollaborationUndoEntryGetData(self._handle, data): raise RuntimeError(util._last_error()) return str(core.pyNativeStr(data.value)) @property def parent(self) -> Optional['UndoEntry']: """ Parent Undo Entry object :return: Undo Entry object, if there is one, None otherwise """ value = core.BNCollaborationUndoEntryGetParent(self._handle) if value is None: return None result = UndoEntry(handle=value) core.BNCollaborationUndoEntryFree(value) return result