Zimbra (CLI) - Search & Delete messages from users

Post date: Sep 26, 2018 11:43:18 AM

This is a simple script that allows you to search either delete a message from a single person OR all users from the command line. A full help with examples is displayed if no or an invalid arguments are given.

Most of the script is really about making the existing Zimbra cli tools more user friendly. By converting  things like "(to:spam@nomadcf.com OR cc:spam@nomadcf.com)"  to a simpler "-t spam@nomadcf.com". 

CODE

#!/bin/bash

## Written by Chris Franklin

##  CFranklin@NomadCF.com

## Vars

MSG_LIST_MAX=999

LOG=/var/log/zimbra_remove_messages.$(date +%s).log

## Nothing to edit passed here

function _help() {

 cat <<EOF

 $1

 

 -a : Finds emails with any attachment

      $0 -a 

 -A : attachment filename 

      $0 -A bob.txt

      finds emai lwith wav attachments

      $0 -A .wav

 -c : Contains this word/phrase (very picky)

      $0 -c BPMCLASSROOM

      !! Won't find BPMCLASSROOM !!

      $0 -c BPMCLASS

 -C : Condition ("unread", "read", "flagged", "unflagged", "sent", "draft", "received", "replied", "unreplied", "forwarded", unforwarded", "anywhere", "remote" (in a shared folder), "local", "sent", "invite", "solo" (no other messages in conversation), "tome", "fromme", "ccme", "tofromme". "fromccme", "tofromccme" (to, from cc me, including my aliases)

      $0 -C unread

     

 -d : recived on date  (must be as fallows mm/dd/yyyy)

      $0 -d 09/09/2017

 -D : today

      $0 -D 

 -e : Before this date (must be as fallows mm/dd/yyyy)

      $0 -e 09/09/2018

 -E : After this date  (must be as fallows mm/dd/yyyy)

      $0 -E 09/09/2017

 -f : From address (Display name,emailaddress or domain) (Always Wild card searches!!) 

      $0 -f cfranklin

      $0 -f "Chris Franklin"

      $0 -f @berea.k12.oh.us

      $0 -f cfranklin@berea.k12.oh.us

 -h : Header exists (Any value is * (wildcard))

      $0 -h "X-Spammy"

 -H : Header Value (Requires -h)

      $0 -H "75"

 -i : Search only the inbox (Default is to search all folders OR -l messages)

      $0 -i

 -I : folder to search in (Default is to search all folders OR -l messages)

      $0 -I Sent

 -l : Limit List to this number newest messages

      $0 -l 5

  

 -L : List user messages (max 999)

      $0 -L

 -m : Move Message to trash (Default is to show messages)

      $0 -m

 -s : Subject

      $0 -s "Taxes are due"

 -S : Messages total size including attachments is BIGGER than (in megabytes)

      $0 -S 5

 -t : Message for (to or cc)

      $0 -t "bob@aol.com"

 -u : Username to check (Default is all usernames)

      $0 -u "cfranklin"

 -v : Verbose 

 

 -Z : Deletes the matching messages

       

--------------

Examples:

  Find emails with the fallowing 

   1. delivered "TODAY"

   2. HEADER field called "Return-Path" WITH value of "geofdupo@savba.sk" 

   3. FROM a display name containing "BCSD"

   4. Message Contains the phrase "BCSD Account will be De-activated"

   5. Subject Contains "Technology Help Desk"

   6. Search in the inbox

   7. Search only the mailbox for USER cfranklin 

   $0 -D -h "Return-Path" -H "geofdupo@savba.sk" -f "BCSD" -c "BCSD Account will be De-activated" -s "Technology Help Desk" -i -u cfranklin

  As as above, but now MOVE the email to the TRASH

 

   $0 -D -h "Return-Path" -H "geofdupo@savba.sk" -f "BCSD" -c "BCSD Account will be De-activated" -s "Technology Help Desk" -i -u cfranklin -m

 -------------

 $1

EOF

exit 1

}

REQ=0

MSG_LIST=0

SEARCH=0

MOVE=0

DELETE=0

DEBUG=0

while getopts ":A:c:C:d:e:E:f:h:l:H:I:N:s:S:t:u:miLaDZv" opt; do

 case ${opt} in

  a)

   EMAIL_ATTACHMENT=1;

   SEARCH=1;

   REQ=1;

   ;;

  A)

   EMAIL_ATTACHMENT_NAME="${OPTARG}";

   SEARCH=1;

   REQ=1;

   ;;

  t)

   SEARCH=1;

   EMAIL_TO="${OPTARG}";

   REQ=1;

   ;;

  c)

   SEARCH=1;

   EMAIL_CONTAINS="${OPTARG}";

   REQ=1;

   ;;

  C)

   SEARCH=1;

   EMAIL_CONDITION="${OPTARG}";

   REQ=1;

   ;;

  l) 

   MSG_LIST_MAX=${OPTARG};

   ;;

  d)

   SEARCH=1;

   EMAIL_DATE="${OPTARG}";

   REQ=1;

   ;;

  D)

   SEARCH=1;

   EMAIL_DATE="$(date +%m/%d/%Y)";

   REQ=1;

   ;;

  e)

   SEARCH=1;

   EMAIL_DATE_BEFORE="${OPTARG}";

   REQ=1;

   ;;

  E)

   SEARCH=1;

   EMAIL_DATE_AFTER="${OPTARG}";

   REQ=1;

   ;;

  f)

   SEARCH=1;

   EMAIL_FROM="${OPTARG}";

   REQ=1;

   ;;

  h)

   SEARCH=1;

   EMAIL_HEADER="#${OPTARG}";

   REQ=1;

   ;;

  H)

   SEARCH=1;

   EMAIL_HEADER_VALUE="${OPTARG}";

   REQ=1;

   ;;

  i)

   SEARCH_FOLDER="inbox";

   REQ=1;

   ;;

  L)

   MSG_LIST=1;

   REQ=1;

   ;;

  I)

   SEARCH_FOLDER="${OPTARG}";

   REQ=1;

   ;;

  m)

   MOVE=1;

   ;;

  s)

   SEARCH=1;

   EMAIL_SUBJECT="${OPTARG}";

   REQ=1;

   ;;

  S)

   SEARCH=1;

   EMAIL_SIZE_BIGGER=">${OPTARG}mb";

   REQ=1;

  ;;

  u)

   EMAIL_USER="${OPTARG}";

   ;;

  v)

   DEBUG=1

   ;;

  Z)

   DELETE=1

   ;;

  \?)

   _help $opt

   ;;

  :)

   _help $opt

   ;;

  *) 

   _help $opt

   ;;

 esac

done

shift $((OPTIND - 1))

if [ $REQ -eq 0 ] && [ ! -z "${EMAIL_USER}" ]; then

 MSG_LIST=1

 REQ=1

fi

if [ $REQ -eq 0 ]; then

 _help

fi

if [ -z "${EMAIL_USER}" ]; then

 EMAIL_USER="*"

fi

if [ ! -z "${EMAIL_DATE}" ]; then

 if [ ! -z "${EMAIL_DATE_BEFORE}" ] || [ ! -z "${EMAIL_DATE_AFTER}" ]; then

  _help "ERROR: -d CAN'T be used with -D OR -E "

 fi

fi

if [ $DELETE -eq 1 ] && [ $MOVE -eq 1 ]; then

 _help "ERROR: -Z CAN'T be used with -m"

fi

#convert LIST anything to listing with a search

if [ $MSG_LIST -eq 1 ] && [ $SEARCH -eq 1 ]; then

 MSG_LIST=2

elif [ $MOVE -eq 0 ] &&  [ $DELETE -eq 0 ] && [ $MSG_LIST -eq 0 ]; then

 MSG_LIST=2 

fi

if [ $MSG_LIST -gt 0 ] && [ $MOVE -eq 1 ]; then

 _help "ERROR: -m and -L can NOT be used at the same time."

fi

if [ $MSG_LIST -gt 0 ] && [ $DELETE -eq 1 ]; then

 _help "ERROR: -Z and -L can NOT be used at the same time."

fi

if [ ! -z "${EMAIL_HEADER_VALUE}" ] && [ -z "${EMAIL_HEADER}" ]; then

 _help "ERROR: -H requires -h"

fi

if [ ! -z "${EMAIL_ATTACHMENT_NAME}" ] && [ ! -z "${EMAIL_ATTACHMENT}" ]; then

 _help "ERROR: -A Can't BOTH be used -a"

fi

START_DATE="$(date)"

START_SECS=$(date +%s)

## Lookup zmlocalconfig

ZMCONFIG=$(whereis zmlocalconfig | cut -d ' ' -f2)

if [ ! -e "${ZMCONFIG}" ]; then

 echo "Could not find: zmlocalconfig"

 exit

fi

## lookup ldapsearch

LDAPSEARCH=$(whereis ldapsearch | cut -d ' ' -f2)

if [ ! -e "${LDAPSEARCH}" ]; then

 echo "Could not find: ldapsearch"

 exit

fi

MAIL=$(which mail)

if [ ! -e "${MAIL}" ]; then

 echo "Could not find: mail"

 exit

fi

EMAIL_SEARCH=""

if [ ! -z "${EMAIL_FROM}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}from:${EMAIL_FROM} "

fi

if [ ! -z "${EMAIL_SUBJECT}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}subject:\"${EMAIL_SUBJECT}\" "

fi

if [ ! -z "${EMAIL_DATE}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}date:${EMAIL_DATE} "

fi

if [ ! -z "${EMAIL_DATE_BEFORE}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}before:${EMAIL_DATE_BEFORE} "

fi

if [ ! -z "${EMAIL_DATE_AFTER}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}after:${EMAIL_DATE_AFTER} "

fi

if [ ! -z "${SEARCH_FOLDER}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}in:${SEARCH_FOLDER} "

fi

if [ ! -z "${EMAIL_HEADER_VALUE}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH} ${EMAIL_HEADER}:\"${EMAIL_HEADER_VALUE}\" "

fi

if [ ! -z "${EMAIL_TO}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH} (to:${EMAIL_TO} OR cc:${EMAIL_TO}) "

fi

if [ ! -z "${EMAIL_ATTACHMENT}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}has:attachment "

fi

if [ ! -z "${EMAIL_ATTACHMENT_NAME}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}filename:\"${EMAIL_ATTACHMENT_NAME}\" "

fi

if [ ! -z "${EMAIL_CONTAINS}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}content:\"${EMAIL_CONTAINS}\" "

fi

if [ ! -z "${EMAIL_SIZE_BIGGER}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}larger:${EMAIL_SIZE_BIGGER} "

fi

if [ ! -z "${EMAIL_CONDITION}" ]; then

 EMAIL_SEARCH="${EMAIL_SEARCH}is:${EMAIL_CONDITION} "

fi

## This is the default ldap lookup query

LDAP_SEARCH="${LDAPSEARCH} -x -h $($ZMCONFIG ldap_host | cut -d '=' -f2) -D $($ZMCONFIG zimbra_ldap_userdn | awk '{print $3}') -w$($ZMCONFIG -s zimbra_ldap_password | cut -d ' ' -f3) -LLL -o ldif-wrap=no "

echo ""

if [ $DELETE -eq 1 ] || [ $MOVE -eq 1 ]; then

 echo "Log file: ${LOG}"

fi

echo ""

$LDAP_SEARCH "(&(objectClass=zimbraAccount)(mail=${EMAIL_USER}@berea.k12.oh.us))" mail | while IFS=": " read TRASH EMAIL; do

 if [ ! -z "${EMAIL}" ] && [ "${TRASH}" == "mail" ]; then

  # MNUM  = Message number in printed out list

  # MID   = Message ID in zimbra

  # TRASH = Everything else about the email

  if [ $DELETE -eq 1 ] || [ $MOVE -eq 1 ]; then

   echo "${EMAIL}" |  tee -a "${LOG}"

  else

   echo "${EMAIL}"

  fi

  if [ $MSG_LIST -eq 1 ]; then

   if [ "${DEBUG}" -eq 1 ]; then

    echo  "/opt/zimbra/bin/zmmailbox -z -m '${EMAIL}' s -l ${MSG_LIST_MAX} -T" >> "${LOG}"

   fi

   /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" s -l ${MSG_LIST_MAX} -T  | tee -a "${LOG}"

  elif [ $MSG_LIST -eq 2 ]; then

   if [ "${DEBUG}" -eq 1 ]; then

    echo "/opt/zimbra/bin/zmmailbox -z -m '${EMAIL}' s -l ${MSG_LIST_MAX} -t message '${EMAIL_SEARCH}'" >> "${LOG}"

   fi

   /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" s -l ${MSG_LIST_MAX} -t message "${EMAIL_SEARCH}" | tee -a "${LOG}"

  elif [ $MOVE -eq 1 ] || [ $DELETE -eq 1 ]; then

   if [ "${DEBUG}" -eq 1 ]; then

    echo "/opt/zimbra/bin/zmmailbox -z -m '${EMAIL}' s -l ${MSG_LIST_MAX} -t message '${EMAIL_SEARCH}'" | tee -a "${LOG}" 

   fi

   /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" s -l ${MSG_LIST_MAX} -t message "${EMAIL_SEARCH}" | while IFS=" " read MNUM MID TRASH; do

    if [ ! -z "${MNUM}" ] && [ ! -z "${MID}" ]; then

     if [ "${MNUM//./}" != "${MNUM}" ]; then

      if [ $DELETE -eq 1 ]; then

       echo "#Deleting Match: ${EMAIL} => ${MNUM} => ${MID} => ${TRASH} " >> "${LOG}"

       echo "/opt/zimbra/bin/zmmailbox -z -m '${EMAIL}' deleteMessage '${MID}'" >> "${LOG}"

       echo "Deleting ${MID} for ${EMAIL}"

       /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" deleteMessage "${MID}"

      else

       echo "#Moving Match: ${EMAIL} => ${MNUM} => ${MID} => ${TRASH} " >> "${LOG}"

       echo "#undo: /opt/zimbra/bin/zmmailbox -z -m '${EMAIL}' mm '${MID}' /${SEARCH_FOLDER}" >> "${LOG}"

       echo /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" mm "${MID}" /Trash >> "${LOG}"

       echo "Moving msg ${MID} for ${EMAIL} to TRASH"

       /opt/zimbra/bin/zmmailbox -z -m "${EMAIL}" mm "${MID}" /Trash

      fi

     fi

    fi

   done

  fi

 fi

done

FINISHED_DATE="$(date)"

FINISHED_SECS=$(date +%s)

echo ""

echo ""

echo "Started: ${START_DATE}" | tee -a "${LOG}"

echo "Finished: ${FINISHED_DATE}" | tee -a "${LOG}"

if [ $DELETE -eq 1 ] || [ $MOVE -eq 1 ]; then

 echo "Run time: $((($FINISHED_SECS-$START_SECS)/60)) mins" | tee -a "${LOG}"

else

 echo "Run time: $((($FINISHED_SECS-$START_SECS)/60)) mins" 

fi

echo ""

exit 0

Updated: 9/28/2018