Scripting‎ > ‎

Zimbra (CLI) - Search & Delete messages from users

posted Sep 26, 2018, 4:43 AM by Chris Franklin   [ updated Sep 28, 2018, 4:07 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
ċ
zimbra_remove_messages.bash
(10k)
Chris Franklin,
Sep 28, 2018, 4:07 AM
Comments