Java tutorial
/* * ***** BEGIN LICENSE BLOCK ***** * Zimbra Collaboration Suite Server * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, * version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program. * If not, see <https://www.gnu.org/licenses/>. * ***** END LICENSE BLOCK ***** */ package com.zimbra.cs.mailbox.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import com.zimbra.common.service.ServiceException; import com.zimbra.common.util.CliUtil; import com.zimbra.common.util.StringUtil; import com.zimbra.cs.db.DbMailItem; import com.zimbra.cs.db.DbPool; import com.zimbra.cs.db.DbPool.DbConnection; import com.zimbra.cs.mailbox.Metadata; import com.zimbra.cs.volume.Volume; import com.zimbra.cs.volume.VolumeManager; public final class MetadataDump { private static final String OPT_MAILBOX_ID = "mailboxId"; private static final String OPT_ITEM_ID = "itemId"; private static final String OPT_DUMPSTER = "dumpster"; private static final String OPT_FILE = "file"; private static final String OPT_HELP = "h"; private static final String OPT_STR = "String"; private static Options sOptions = new Options(); static { sOptions.addOption("m", OPT_MAILBOX_ID, true, "mailbox id or email"); sOptions.addOption("i", OPT_ITEM_ID, true, "item id (required when --" + OPT_MAILBOX_ID + " is used)"); sOptions.addOption(null, OPT_DUMPSTER, false, "Get data from the dumpster"); sOptions.addOption("f", OPT_FILE, true, "Decode metadata value in a file (other options are ignored)"); sOptions.addOption("s", OPT_STR, true, "Decode metadata value from a string (other options are ignored)"); sOptions.addOption(OPT_HELP, "help", false, "Show help (this output)"); } private static void usage(String errmsg) { if (errmsg != null) { System.err.println(errmsg); } System.err.println("Usage: zmmetadump -m <mailbox id/email> -i <item id> [--dumpster]"); System.err.println(" or: zmmetadump -f <file containing encoded metadata>"); System.err.println(" or: zmmetadump -s <encoded string>"); } private static CommandLine parseArgs(String args[]) { CommandLineParser parser = new GnuParser(); CommandLine cl = null; try { cl = parser.parse(sOptions, args); } catch (ParseException pe) { usage(pe.getMessage()); System.exit(1); } return cl; } private static final String METADATA_COLUMN = "metadata"; private static class Row implements Iterable<Entry<String, String>> { private Map<String, String> mMap = new LinkedHashMap<String, String>(); Row() { } void addColumn(String colName, String data) throws ServiceException { String key = colName.toLowerCase(); String value = key.equals(METADATA_COLUMN) ? DbMailItem.decodeMetadata(data) : data; mMap.put(key, value); } @Override public Iterator<Entry<String, String>> iterator() { return mMap.entrySet().iterator(); } String get(String colName) { return mMap.get(colName.toLowerCase()); } void print(PrintStream ps) throws ServiceException { ps.println("[Database Columns]"); for (Entry<String, String> entry : this) { String col = entry.getKey(); if (!col.equalsIgnoreCase(METADATA_COLUMN)) { String val = entry.getValue(); if (col.equalsIgnoreCase("date") || col.equalsIgnoreCase("change_date")) { if (val != null) { long t = Long.parseLong(val) * 1000; val += " (" + getTimestampStr(t) + ")"; } } ps.println(" " + col + ": " + (val != null ? val : "<null>")); } } ps.println(); if (mMap.get("blob_digest") != null) { Volume vol = null; try { short volId = Short.parseShort(mMap.get("locator")); vol = VolumeManager.getInstance().getVolume(volId); } catch (NumberFormatException nfe) { //probably not FileBlobStore } if (vol != null) { int mboxId = Integer.parseInt(mMap.get("mailbox_id")); String itemIdStr = mMap.get("id"); if (itemIdStr == null) itemIdStr = mMap.get("item_id"); int itemId = Integer.parseInt(itemIdStr); String dir = vol.getBlobDir(mboxId, itemId); String modContent = mMap.get("mod_content"); String blobPath = dir + File.separator + itemIdStr + "-" + modContent + ".msg"; ps.println("[Blob Path]"); ps.println(blobPath); ps.println(); } } ps.println("[Metadata]"); Metadata md = new Metadata(mMap.get(METADATA_COLUMN)); ps.println(md.prettyPrint()); } } private static int getMailboxGroup(DbConnection conn, int mboxId) throws SQLException { int gid = 0; PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement("SELECT group_id FROM mailbox WHERE id = ?"); stmt.setInt(1, mboxId); rs = stmt.executeQuery(); if (rs.next()) gid = rs.getInt(1); } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } return gid; } private static int lookupMailboxIdFromEmail(DbConnection conn, String email) throws SQLException, ServiceException { PreparedStatement stmt = null; ResultSet rs = null; try { String sql = "SELECT id FROM mailbox WHERE comment=?"; stmt = conn.prepareStatement(sql); stmt.setString(1, email.toUpperCase()); rs = stmt.executeQuery(); if (!rs.next()) throw ServiceException.INVALID_REQUEST("Account " + email + " not found on this host", null); return rs.getInt(1); } finally { DbPool.closeResults(rs); DbPool.closeStatement(stmt); } } private static Row getItemRow(DbConnection conn, int groupId, int mboxId, int itemId, boolean fromDumpster) throws ServiceException { PreparedStatement stmt = null; ResultSet rs = null; try { String sql = "SELECT * FROM " + DbMailItem.getMailItemTableName(groupId, fromDumpster) + " WHERE mailbox_id = " + mboxId + " AND id = " + itemId; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); if (!rs.next()) throw ServiceException.INVALID_REQUEST("No such item: mbox=" + mboxId + ", item=" + itemId, null); Row row = new Row(); ResultSetMetaData rsMeta = rs.getMetaData(); int cols = rsMeta.getColumnCount(); for (int i = 1; i <= cols; i++) { String colName = rsMeta.getColumnName(i); String colValue = rs.getString(i); if (rs.wasNull()) colValue = null; row.addColumn(colName, colValue); } return row; } catch (SQLException e) { throw ServiceException.INVALID_REQUEST("No such item: mbox=" + mboxId + ", item=" + itemId, e); } finally { DbPool.closeResults(rs); DbPool.closeStatement(stmt); } } private static List<Row> getRevisionRows(DbConnection conn, int groupId, int mboxId, int itemId, boolean fromDumpster) throws ServiceException { PreparedStatement stmt = null; ResultSet rs = null; try { String sql = "SELECT * FROM " + DbMailItem.getRevisionTableName(groupId, fromDumpster) + " WHERE mailbox_id = " + mboxId + " AND item_id = " + itemId + " ORDER BY mailbox_id, item_id, version DESC"; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); List<Row> rows = new ArrayList<Row>(); while (rs.next()) { Row row = new Row(); ResultSetMetaData rsMeta = rs.getMetaData(); int cols = rsMeta.getColumnCount(); for (int i = 1; i <= cols; i++) { String colName = rsMeta.getColumnName(i); String colValue = rs.getString(i); if (rs.wasNull()) colValue = null; row.addColumn(colName, colValue); } rows.add(row); } return rows; } catch (SQLException e) { throw ServiceException.INVALID_REQUEST("No such item: mbox=" + mboxId + ", item=" + itemId, e); } finally { DbPool.closeResults(rs); DbPool.closeStatement(stmt); } } private static String loadFromFile(File file) throws ServiceException { try { FileInputStream fis = null; try { fis = new FileInputStream(file); Scanner scan = new Scanner(fis); StringBuilder builder = new StringBuilder(); while (scan.hasNextLine()) { builder.append(scan.nextLine()); } return builder.toString(); } finally { if (fis != null) fis.close(); } } catch (IOException e) { throw ServiceException.FAILURE("IOException while reading from " + file.getAbsolutePath(), e); } } private static void printBanner(PrintStream out, String title) { out.println("******************** " + title + " ********************"); } static String getTimestampStr(long time) { DateFormat fmt = new SimpleDateFormat("EEE yyyy/MM/dd HH:mm:ss z"); return fmt.format(time); } public static void doDump(DbConnection conn, int mboxId, int itemId, boolean fromDumpster, PrintStream out) throws ServiceException { try { int groupId = getMailboxGroup(conn, mboxId); boolean first = true; Row item = getItemRow(conn, groupId, mboxId, itemId, fromDumpster); List<Row> revs = getRevisionRows(conn, groupId, mboxId, itemId, fromDumpster); // main item if (!revs.isEmpty()) printBanner(out, "Current Revision"); item.print(out); first = false; // revisions for (Row rev : revs) { String version = rev.get("version"); if (!first) { out.println(); out.println(); } printBanner(out, "Revision " + version); rev.print(out); first = false; } } catch (SQLException e) { throw ServiceException .FAILURE("error while getting metadata for item " + itemId + " in mailbox " + mboxId, e); } } public static void main(String[] args) { try { CliUtil.toolSetup("WARN"); int mboxId = 0; int itemId = 0; PrintStream out = new PrintStream(System.out, true, "utf-8"); CommandLine cl = parseArgs(args); if (cl.hasOption(OPT_HELP)) { usage(null); System.exit(0); } // Get data from file. String infileName = cl.getOptionValue(OPT_FILE); if (infileName != null) { File file = new File(infileName); if (file.exists()) { String encoded = loadFromFile(file); Metadata md = new Metadata(encoded); String pretty = md.prettyPrint(); out.println(pretty); return; } else { System.err.println("File " + infileName + " does not exist"); System.exit(1); } } // Get data from input String. String encoded = cl.getOptionValue(OPT_STR); if (!StringUtil.isNullOrEmpty(encoded)) { Metadata md = new Metadata(encoded); String pretty = md.prettyPrint(); out.println(pretty); return; } // Get data from db. DbPool.startup(); DbConnection conn = null; try { boolean fromDumpster = cl.hasOption(OPT_DUMPSTER); String mboxIdStr = cl.getOptionValue(OPT_MAILBOX_ID); String itemIdStr = cl.getOptionValue(OPT_ITEM_ID); if (mboxIdStr == null || itemIdStr == null) { usage(null); System.exit(1); return; } if (mboxIdStr.matches("\\d+")) { try { mboxId = Integer.parseInt(mboxIdStr); } catch (NumberFormatException e) { System.err.println("Invalid mailbox id " + mboxIdStr); System.exit(1); } } else { conn = DbPool.getConnection(); mboxId = lookupMailboxIdFromEmail(conn, mboxIdStr); } try { itemId = Integer.parseInt(itemIdStr); } catch (NumberFormatException e) { usage(null); System.exit(1); } if (conn == null) conn = DbPool.getConnection(); doDump(conn, mboxId, itemId, fromDumpster, out); } finally { DbPool.quietClose(conn); } } catch (Exception e) { System.err.println(e.getMessage()); System.err.println(); System.err.println(); e.printStackTrace(); System.exit(1); } } }