/* * This is a sample plugin to demonstrate the snapshot management API */ #include #include #include #include //-------------------------------------------------------------------------- class snapman_t : public chooser_t { struct snapdesc_t { snapshot_t *ss; qstring title; qstring date; }; typedef qvector sdlist_t; snapshot_t root; sdlist_t sdlist; static const int widths_[2]; static const char *const header_[2]; static void idaapi done_restore(const char *err_msg, void *) { if ( err_msg != NULL ) warning("ICON ERROR\nError restoring: %s", err_msg); else warning("Restored successfully!"); } void build_tree_list(snapshot_t *n, int level = 0) { if ( n != &root ) { // Insert new description record snapdesc_t &sd = sdlist.push_back(); // Compute title for ( int i=0; i < level; i++ ) sd.title += " "; if ( n->id == root.id ) sd.title += "->"; sd.title += n->desc; // Compute date char ss_date[MAXSTR]; qstrftime64(ss_date, sizeof(ss_date), "%Y-%m-%d %H:%M:%S", n->id); sd.date = ss_date; // Store ss sd.ss = n; } for ( snapshots_t::iterator it=n->children.begin(); it != n->children.end(); ++it ) build_tree_list(*it, level+1); } snapdesc_t *get_item(uint32 n) { return n >= sdlist.size() ? NULL : &sdlist[n]; } const snapdesc_t *get_item(uint32 n) const { return n >= sdlist.size() ? NULL : &sdlist[n]; } public: bool init() override { sdlist.clear(); root.clear(); if ( !build_snapshot_tree(&root) ) { warning("Snapshot tree cannot be built.\nNo snapshots exist?"); return false; } // Convert the tree to a list build_tree_list(&root); if ( sdlist.empty() ) { warning("Snapshot tree empty!"); return false; } return true; } snapman_t() : chooser_t(CH_MODAL | CH_KEEP | CH_CAN_INS | CH_CAN_DEL | CH_CAN_EDIT, qnumber(widths_), widths_, header_, "Simple snapshot manager"), root(), sdlist() {} virtual size_t idaapi get_count() const override { return sdlist.size(); } virtual void idaapi get_row( qstrvec_t *cols_, int *, chooser_item_attrs_t *, size_t n) const override { const snapdesc_t *sd = get_item(n); QASSERT(561, sd != NULL); qstrvec_t &cols = *cols_; cols[0] = sd->date; cols[1] = sd->title; } virtual cbret_t idaapi ins(ssize_t n) override { qstring desc = "snapshot description"; if ( !ask_str(&desc, HIST_CMT, "Enter snapshot description") ) return cbret_t(); // nothing changed qstring err_msg; snapshot_t new_attr; qstrncpy(new_attr.desc, desc.c_str(), sizeof(new_attr.desc)); if ( !take_database_snapshot(&new_attr, &err_msg) ) { warning("Failed to create a snapshot, error: %s", err_msg.c_str()); return cbret_t(); // nothing changed } msg("Created new snapshot: %s\n", new_attr.filename); init(); // we preserve the selection // FIXME use get_item_index() return n; } virtual cbret_t idaapi del(size_t n) override { const snapdesc_t *sd = get_item(n); if ( sd == NULL ) return cbret_t(); // Simply delete the file qunlink(sd->ss->filename); // Rebuild the list init(); return adjust_last_item(n); // take in account deleting of the last item } virtual cbret_t idaapi edit(size_t n) override { snapdesc_t *sd = get_item(n); if ( sd == NULL ) return cbret_t(); qstring desc = sd->ss->desc; if ( !ask_str(&desc, HIST_CMT, "Enter new snapshot description") ) return cbret_t(); // Update the description qstrncpy(sd->ss->desc, desc.c_str(), sizeof(sd->ss->desc)); update_snapshot_attributes(sd->ss->filename, &root, sd->ss, SSUF_DESC); return n; } // calculate the location of the default item only, // `item_data` is a pointer to a snapshot ID virtual ssize_t idaapi get_item_index(const void *item_data) const override { qtime64_t item_id = *(const qtime64_t *)item_data; for ( size_t i = 0; i < sdlist.size(); ++i ) { if ( sdlist[i].ss->id == item_id ) return i; } return NO_SELECTION; } void show() { // now open the window ssize_t n = ::choose(this, &root.id); if ( n >= 0 ) { snapdesc_t *sd = get_item(n); if ( sd != NULL && sd->ss != NULL ) restore_database_snapshot(sd->ss, done_restore, NULL); } } }; DECLARE_TYPE_AS_MOVABLE(snapman_t::snapdesc_t); // column widths const int snapman_t::widths_[2] = { 12, // Date 70, // Description }; // column headers const char *const snapman_t::header_[2] = { "Date", // 0 "Description", // 1 }; //-------------------------------------------------------------------------- struct plugin_ctx_t : public plugmod_t { virtual bool idaapi run(size_t) override; }; //-------------------------------------------------------------------------- bool idaapi plugin_ctx_t::run(size_t) { snapman_t sm; if ( !sm.init() ) return false; sm.show(); return true; } //-------------------------------------------------------------------------- static plugmod_t *idaapi init() { // Display help msg( "Simple snapshot manager loaded!\n" "Press Shift+F8 to toggle the plugin\n" "Inside the snapshots window, press:\n" " - Insert: to take a snapshot\n" " - Delete: to delete\n" " - Edit: to edit the snapshot description\n" "\n" "Click on:\n" " - Ok: to restore the selected snapshot\n" " - Cancel: close without doing anything\n"); // The plugin flags must include PLUGIN_FIX as well return new plugin_ctx_t; } //-------------------------------------------------------------------------- // // PLUGIN DESCRIPTION BLOCK // //-------------------------------------------------------------------------- plugin_t PLUGIN = { IDP_INTERFACE_VERSION, // plugin flags PLUGIN_FIX // Load plugin when IDA starts and keep it in the // memory until IDA stops | PLUGIN_MULTI, // The plugin can work with multiple idbs in parallel init, // initialize nullptr, nullptr, "This is a sample plugin. It displays the list of snapshots", // long comment about the plugin "A snapshot manager sample plugin\n" "\n" "This plugin allows you to list and restore snapshots.\n", // multiline help about the plugin "Simple snapshot manager", // the preferred short name of the plugin "Shift-F8", // the preferred hotkey to run the plugin };