From 6afbaf1fb2fb6ab033ed64242cdceebc20cd80fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 29 Oct 2023 17:03:58 +0100 Subject: [PATCH] fix BITH --- Makefile | 13 +- README.md | 1 - src/bash/checkAltered.sh | 78 +++++++++ src/bash/checkShrinker.sh | 77 +++++++++ src/bash/filter.sh | 36 ++-- src/bash/filterTest.sh | 67 ++++---- src/cpp/Attachment.cpp | 13 +- src/cpp/MainAttachment.cpp | 163 +++++++++++------- src/cpp/eMailShrinker.cpp | 8 +- src/cpp/jirafeauAPI.cpp | 294 --------------------------------- src/cpp/kazMisc.cpp | 55 +++--- src/include/MainAttachment.hpp | 10 +- src/include/kazMisc.hpp | 2 +- src/mainpage.md | 5 +- structures.odt | Bin 0 -> 39669 bytes 15 files changed, 360 insertions(+), 462 deletions(-) create mode 100755 src/bash/checkAltered.sh create mode 100755 src/bash/checkShrinker.sh delete mode 100644 src/cpp/jirafeauAPI.cpp create mode 100644 structures.odt diff --git a/Makefile b/Makefile index e7ce487..575f0fa 100644 --- a/Makefile +++ b/Makefile @@ -50,12 +50,6 @@ KAZ_SRC = $(patsubst %, $(CPP_DIR)/%.cpp, $(KAZ_MOD)) KAZ_OBJ = $(patsubst %, $(OBJ_DIR)/%.o, $(KAZ_MOD)) KAZ_OUT = $(patsubst %, $(OUT_DIR)/%, $(KAZ_PRG)) -JIR_PRG = jirafeauAPI -JIR_MOD = jirafeauAPI SizeArg kazDebug kazMisc -JIR_SRC = $(patsubst %, $(CPP_DIR)/%.cpp, $(JIR_MOD)) -JIR_OBJ = $(patsubst %, $(OBJ_DIR)/%.o, $(JIR_MOD)) -JIR_OUT = $(patsubst %, $(OUT_DIR)/%, $(JIR_PRG)) - ## FLAGS ############################### #DFLAGS = -O2 -DDISABLE_LOG @@ -71,18 +65,13 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/*/%.cpp $(CC) $< $(IFLAGS) -cpp -c -o $@ ## ENTRIES ############################# -all: init eMailShrinker jirafeauAPI doc +all: init eMailShrinker doc eMailShrinker: $(KAZ_OUT) $(KAZ_OUT): $(KAZ_OBJ) $(CC) $(KAZ_OBJ) $(IFLAGS) -cpp -L$(LIB_DIR) $(LFLAGS) -o $@ -jirafeauAPI: $(JIR_OUT) - -$(JIR_OUT): $(JIR_OBJ) - $(CC) $(JIR_OBJ) $(IFLAGS) -cpp -L$(LIB_DIR) $(LFLAGS) -o $@ - doc: doxygen src/Doxyfile diff --git a/README.md b/README.md index 7dae8b3..0cea90b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ depollueur/ │   ├── Attachment.cpp │   ├── eMailShrinker.cpp │   ├── EmbeddedData.cpp - │   ├── jirafeauAPI.cpp │   ├── kazDebug.cpp │   ├── kazMisc.cpp │   ├── MainAttachment.cpp diff --git a/src/bash/checkAltered.sh b/src/bash/checkAltered.sh new file mode 100755 index 0000000..b1d677d --- /dev/null +++ b/src/bash/checkAltered.sh @@ -0,0 +1,78 @@ +#!/bin/bash +########################################################################## +# Copyright KAZ 2021 # +# # +# contact (at) kaz.bzh # +# # +# This software is a filter to shrink email by attachment extraction. # +# # +# This software is governed by the CeCILL-B license under French law and # +# abiding by the rules of distribution of free software. You can use, # +# modify and/or redistribute the software under the terms of the # +# CeCILL-B license as circulated by CEA, CNRS and INRIA at the following # +# URL "http://www.cecill.info". # +# # +# As a counterpart to the access to the source code and rights to copy, # +# modify and redistribute granted by the license, users are provided # +# only with a limited warranty and the software's author, the holder of # +# the economic rights, and the successive licensors have only limited # +# liability. # +# # +# In this respect, the user's attention is drawn to the risks associated # +# with loading, using, modifying and/or developing or reproducing the # +# software by the user in light of its specific status of free software, # +# that may mean that it is complicated to manipulate, and that also # +# therefore means that it is reserved for developers and experienced # +# professionals having in-depth computer knowledge. Users are therefore # +# encouraged to load and test the software's suitability as regards # +# their requirements in conditions enabling the security of their # +# systems and/or data to be ensured and, more generally, to use and # +# operate it in the same conditions as regards security. # +# # +# The fact that you are presently reading this means that you have had # +# knowledge of the CeCILL-B license and that you accept its terms. # +########################################################################## + +PRG=$(basename $0) + +usage () { + echo "Usage: ${PRG} logDir" + exit 1 +} + +BOLD='\e[1m' +RED='\e[0;31m' +GREEN='\e[0;32m' +YELLOW='\e[0;33m' +BLUE='\e[0;34m' +MAGENTA='\e[0;35m' +CYAN='\e[0;36m' +NC='\e[0m' # No Color +NL=' +' +[ "$#" -eq 1 ] || usage +logDir=$(realpath "$1") + +######################################## +# recherche des binaires +cd $(dirname $0) +eMailShrinker="$(realpath "./eMailShrinker")" +[ -x "${eMailShrinker}" ] || eMailShrinker="$(realpath "../../build/out/eMailShrinker")" +[ -x "${eMailShrinker}" ] || ( echo "${RED}eMailShrinker not found${NC}" ; exit) + + +cd "${logDir}" +for i in in.*.orig; +do + clear + echo -e " ${GREEN}${BOLD}${i//.orig}${NC}\n" + "${eMailShrinker}" -l ${i} + echo -e "\n ####################\n" + "${eMailShrinker}" -l ${i//.orig}.altered + echo -en "\n(q = quit / y = rm / default = continue)? " + read rep + case "${rep}" in + "q" ) break;; + "y") rm ${i} ${i//.orig}.altered + esac +done diff --git a/src/bash/checkShrinker.sh b/src/bash/checkShrinker.sh new file mode 100755 index 0000000..8ee010e --- /dev/null +++ b/src/bash/checkShrinker.sh @@ -0,0 +1,77 @@ +#!/bin/bash +########################################################################## +# Copyright KAZ 2021 # +# # +# contact (at) kaz.bzh # +# # +# This software is a filter to shrink email by attachment extraction. # +# # +# This software is governed by the CeCILL-B license under French law and # +# abiding by the rules of distribution of free software. You can use, # +# modify and/or redistribute the software under the terms of the # +# CeCILL-B license as circulated by CEA, CNRS and INRIA at the following # +# URL "http://www.cecill.info". # +# # +# As a counterpart to the access to the source code and rights to copy, # +# modify and redistribute granted by the license, users are provided # +# only with a limited warranty and the software's author, the holder of # +# the economic rights, and the successive licensors have only limited # +# liability. # +# # +# In this respect, the user's attention is drawn to the risks associated # +# with loading, using, modifying and/or developing or reproducing the # +# software by the user in light of its specific status of free software, # +# that may mean that it is complicated to manipulate, and that also # +# therefore means that it is reserved for developers and experienced # +# professionals having in-depth computer knowledge. Users are therefore # +# encouraged to load and test the software's suitability as regards # +# their requirements in conditions enabling the security of their # +# systems and/or data to be ensured and, more generally, to use and # +# operate it in the same conditions as regards security. # +# # +# The fact that you are presently reading this means that you have had # +# knowledge of the CeCILL-B license and that you accept its terms. # +########################################################################## + +PRG=$(basename $0) +FILTER_TEST="$(realpath "$(dirname $0)/filterTest.sh")" + +ATTACH_MODE="FOOTER" + +usage () { + echo "Usage: ${PRG} {NONE|FOOTER|ATTACHMENT|BOTH} mbox..." + exit 1 +} + +######################################## +BOLD='\e[1m' +RED='\e[0;31m' +GREEN='\e[0;32m' +YELLOW='\e[0;33m' +BLUE='\e[0;34m' +MAGENTA='\e[0;35m' +CYAN='\e[0;36m' +NC='\e[0m' # No Color +NL=' +' + +######################################## +[ "$#" -lt 2 ] && usage + +ATTACH_MODE="$1"; shift + +case "${ATTACH_MODE}" in + ""|NONE|FOOTER|ATTACHMENT|BOTH);; + *) usage;; +esac + +for mbox in $*; do + clear + echo -e " ${GREEN}${BOLD}${mbox}${NC}\n" + "${FILTER_TEST}" -s -m "${ATTACH_MODE}" "${mbox}" + echo -en "\n(q = quit / default = continue)? " + read rep + case "${rep}" in + "q" ) break;; + esac +done diff --git a/src/bash/filter.sh b/src/bash/filter.sh index 0790fa8..6d54c6e 100755 --- a/src/bash/filter.sh +++ b/src/bash/filter.sh @@ -38,9 +38,8 @@ # - installer l'utilitaire dos2unix # - le contenu de INSPECT_DIR doit être accessible en écriture pour le # proriétaire du script -# - shrinkEMail et jirafeau.sh doivent être accessible en execution pour -# le roriétaire du script -# - il faut que root fasse avant : +# - shrinkEMail doit être accessible en execution pour le proriétaire +# - comme le programme n'a pas de privigèle, il faut que root fasse avant : # mkdir -p "${DIR_LOG}/pb/" ; chmod a+rwx "${DIR_LOG}/pb/" ########################################################################## @@ -63,7 +62,6 @@ MAILS=/tmp/FILTER MAX_KEEP_IN_MAIL=5ki MAX_UPLOAD_SIZE=1Gi SHRINK_CMD=/home/filter/eMailShrinker -JIRAFEAU_CMD=/home/filter/jirafeauAPI JIRAFEAU_URL=https://depot.${DOMAINNAME:-"kaz.bzh"} JIRAFEAU_LOCAL=http://depot MD5_CMD=/usr/bin/md5sum @@ -80,20 +78,20 @@ KEEP_FAILED=true DEBUG=true #################### FONCTIONS ############################################ -BOLD='[1m' -RED='[0;31m' -GREEN='[0;32m' -YELLOW='[0;33m' -BLUE='[0;34m' -MAGENTA='[0;35m' -CYAN='[0;36m' -NC='[0m' # No Color +BOLD='\e[1m' +RED='\e[0;31m' +GREEN='\e[0;32m' +YELLOW='\e[0;33m' +BLUE='\e[0;34m' +MAGENTA='\e[0;35m' +CYAN='\e[0;36m' +NC='\e[0m' # No Color NL=' ' #--------------------- Fichier de LOG ------------------- LOG_FIC () { - echo "${BLUE}$(date +%d-%m-%Y-%H-%M-%S)${NC} : $*" >> "${TMP_LOG}" + echo -e "${BLUE}$(date +%d-%m-%Y-%H-%M-%S)${NC} : $*" >> "${TMP_LOG}" } quitFilter () { @@ -124,11 +122,11 @@ curlJirafeauSend () { # $4: name # $5: password - type=$3 - [ -z "${type}" ] && type="text/plain" - LOG_FIC " - curl -X POST -F \"time=$1\" -F \"key=$5\" -F \"file=@$2;type=${type};filename=\\\"$4\\\"\" \"${JIRAFEAU_LOCAL}/a.php\"" + type="type=$3;" + [ -z "$3" -o "$3" = "/" ] && type="" + LOG_FIC " - curl -X POST -F \"time=$1\" -F \"key=$5\" -F \"file=@$2;${type}filename=\\\"$4\\\"\" \"${JIRAFEAU_LOCAL}/a.php\"" for num in {1..2}; do - OUTPUT=$(curl -X POST -F "time=$1" -F "key=$5" -F "file=@$2;type=${type};filename=\"$4\"" "${JIRAFEAU_LOCAL}/a.php") + OUTPUT=$(curl -X POST -F "time=$1" -F "key=$5" -F "file=@$2;${type}filename=\"$4\"" "${JIRAFEAU_LOCAL}/a.php") read JIR_TOKEN <<< "${OUTPUT}" case "${JIR_TOKEN}" in "" | no | *Error* | \<* ) @@ -151,7 +149,7 @@ function check_skip_domains() { local domain="${email##*@}" # Utiliser grep pour vérifier si le domaine est dans la liste des domaines à sauter - if echo "$SKIP_DOMAINS" | grep -q -w "$domain"; then + if echo "${SKIP_DOMAINS}" | grep -q -w "${domain}"; then echo "yes" return fi @@ -162,7 +160,7 @@ function check_skip_domains() { } #################### MAIN ################################################# -echo "${NL}${BLUE}$(date +%d-%m-%Y-%H-%M-%S)${NC} : ${GREEN}######################################## filter start (log in ${TMP_LOG})${NC}" >> "${FIC_LOG}" +echo -e "${NL}${BLUE}$(date +%d-%m-%Y-%H-%M-%S)${NC} : ${GREEN}######################################## filter start (log in ${TMP_LOG})${NC}" >> "${FIC_LOG}" LOG_FIC "${GREEN}######################################## ${TMP_LOG} ${NC}" if ! mkdir -p "${MAILS}"; then diff --git a/src/bash/filterTest.sh b/src/bash/filterTest.sh index 6761bb8..d515e08 100755 --- a/src/bash/filterTest.sh +++ b/src/bash/filterTest.sh @@ -36,22 +36,21 @@ PRG=$(basename $0) ATTACH_MODE="FOOTER" - -BOLD='' -RED='' -GREEN='' -YELLOW='' -BLUE='' -MAGENTA='' -CYAN='' -NC='' # No Color +export SIMULATE="" + +BOLD='\e[1m' +RED='\e[0;31m' +GREEN='\e[0;32m' +YELLOW='\e[0;33m' +BLUE='\e[0;34m' +MAGENTA='\e[0;35m' +CYAN='\e[0;36m' +NC='\e[0m' # No Color NL=' ' - -TTY=$(tty) ######################################## LOG () { - echo "$1" >> "${TTY}" + echo -e "$1" 1>&2 } usage () { @@ -64,6 +63,7 @@ while : ; do -h*) usage;; -v*) "${eMailShrinker}" -v; exit;; -g) DEBUG="-g"; shift;; + -s) SIMULATE="echo"; shift;; -m) shift; ATTACH_MODE="$1"; shift;; *) break;; esac @@ -83,11 +83,11 @@ mbox=$(realpath "$1") cd $(dirname $0) eMailShrinker="$(realpath "./eMailShrinker")" [ -x "${eMailShrinker}" ] || eMailShrinker="$(realpath "../../build/out/eMailShrinker")" -[ -x "${eMailShrinker}" ] || ( echo "${RED}eMailShrinker not found${NC}" ; exit) +[ -x "${eMailShrinker}" ] || ( echo -e "${RED}eMailShrinker not found${NC}" ; exit) ######################################## dos2unix "${mbox}" -DOMAINNAME="$(cat domainname)" +[ -z "${SIMULATE}" ] && DOMAINNAME="$(cat domainname)" JIRAFEAU_URL="https://depot.${DOMAINNAME}" JIRAFEAU_LOCAL="${JIRAFEAU_URL}" @@ -99,8 +99,7 @@ curlJirafeauUpdate () { # $1: periode # $2: jirafeauItemRef - curl -X POST -d "u=$1" -d "h=$2" "${JIRAFEAU_LOCAL}/a.php" - #"${jirafeauAPI}" -f "${JIRAFEAU_LOCAL}" -t "$1" update "$2" + [ -z "${SIMULATE}" ] && curl -X POST -d "u=$1" -d "h=$2" "${JIRAFEAU_LOCAL}/a.php" } curlJirafeauSend () { @@ -110,11 +109,14 @@ curlJirafeauSend () { # $4: name # $5: password - type=$3 - [ -z "${type}" ] && type="text/plain" - LOG "curl -X POST -F \"time=$1\" -F \"key=$5\" -F \"file=@$2;type=${type};filename=\\\"$4\\\"\" \"${JIRAFEAU_LOCAL}/a.php\"" - curl -X POST -F "time=$1" -F "key=$5" -F "file=@$2;type=${type};filename=\"$4\"" "${JIRAFEAU_LOCAL}/a.php" || exit 1 - #"${jirafeauAPI}" -f "${JIRAFEAU_LOCAL}" -t "$1" -s "1Gi" -c "${type}" -n "$4" send "$2" "$5" + if [ -n "${SIMULATE}" ]; then + echo -e "TokenXXX\nCodeX" + return + fi + type="type=$3;" + [ -z "$3" -o "$3" = "/" ] && type="" + LOG "curl -X POST -F \"time=$1\" -F \"key=$5\" -F \"file=@$2;${type}filename=\\\"$4\\\"\" \"${JIRAFEAU_LOCAL}/a.php\"" + curl -X POST -F "time=$1" -F "key=$5" -F "file=@$2;${type}filename=\"$4\"" "${JIRAFEAU_LOCAL}/a.php" || exit 1 } ######################################## @@ -126,19 +128,18 @@ echo -e "time: $(date "+%Y-%m-%d-%H:%M:%S")\nid: $(date +%s)" > "${TMP_DIR}/arch ######################################## # affichage de la structure de départ -LOG " - ${BLUE}mbox: ${mbox}${NC}" +LOG " - ${BLUE}mbox: ${mbox}${NC}${YELLOW}" "${eMailShrinker}" -l "${mbox}" -LOG +LOG "${NC}" ######################################## # recherche des prolongations des délais de grace -"${eMailShrinker}" ${DEBUG} -u "${mbox}" > "${TMP_DIR}/url-to-refresh.txt" 2>> "${TTY}" +"${eMailShrinker}" ${DEBUG} -u "${mbox}" > "${TMP_DIR}/url-to-refresh.txt" cat "${TMP_DIR}/url-to-refresh.txt" | grep "${JIRAFEAU_URL}" | while read REMOTE_LINK; do REMOTE_REF=$(echo "${REMOTE_LINK}" | sed -e 's/.*h=\([^&]*\).*/\1/' -e 's/.*http.*//') [ -z "${REMOTE_REF}" ] && continue LOG " - ${BLUE}update ${REMOTE_REF}${NC}" - curlJirafeauUpdate "month" "${REMOTE_REF}" 2>> "${TTY}" - LOG + curlJirafeauUpdate "month" "${REMOTE_REF}" echo "old: ${REMOTE_REF} ${REMOTE_KEY}" >> "${TMP_DIR}/archive-content.txt" done @@ -167,7 +168,7 @@ cat "${TMP_DIR}/PJ-name.txt" | { LOG " - ${BLUE}find ${ATTACH_NAME} / (${ATTACH_CONTENT_TYPE}) / ${ATTACH_MEDIA} ${NC}" PASSWORD=$(apg -n 1 -m 12 -M cln) PASSWORD_MD5=$(echo -n ${PASSWORD} | ${MD5_CMD} | cut -d \ -f 1) - curlJirafeauSend "month" "${ATTACH_MEDIA}" "${ATTACH_CONTENT_TYPE}" "${ATTACH_NAME}" "${PASSWORD}" 2>> "${TTY}" > "${TMP_DIR}/one.txt" + curlJirafeauSend "month" "${ATTACH_MEDIA}" "${ATTACH_CONTENT_TYPE}" "${ATTACH_NAME}" "${PASSWORD}" > "${TMP_DIR}/one.txt" cat "${TMP_DIR}/one.txt" | { read JIR_TOKEN @@ -175,7 +176,7 @@ cat "${TMP_DIR}/PJ-name.txt" | { case "${JIR_TOKEN}" in "" | no | *Error* | \<* ) LOG " - ${RED}can't upload ${ATTACH_MEDIA} <${JIR_TOKEN}> <${JIR_CODE}>${NC}" - cat "${TMP_DIR}/one.txt" >> "${TTY}" + cat "${TMP_DIR}/one.txt" >&2 echo "url:" exit 1 ;; @@ -191,7 +192,7 @@ cat "${TMP_DIR}/PJ-name.txt" | { if [ "${NB_ATTACH}" -gt 1 ]; then PASSWORD=$(apg -n 1 -m 12 -M cln) PASSWORD_MD5=$(echo -n ${PASSWORD} | ${MD5_CMD} | cut -d \ -f 1) - curlJirafeauSend "month" "${TMP_DIR}/archive-content.txt" "text/kaz_email_archive" "archive_content" "${PASSWORD}" 2>> "${TTY}" > "${TMP_DIR}/one.txt" || exit 1 + curlJirafeauSend "month" "${TMP_DIR}/archive-content.txt" "text/kaz_email_archive" "archive_content" "${PASSWORD}" > "${TMP_DIR}/one.txt" || exit 1 cat "${TMP_DIR}/one.txt" | { read JIR_TOKEN read JIR_CODE @@ -221,12 +222,12 @@ LOG " - ${GREEN}ATTACH_MODE: ${ATTACH_MODE}${NC}" ######################################## # substitution des pièces jointes par les codes fournis par jirafeau -cat "${TMP_DIR}/PJ-Keys.txt" | "${eMailShrinker}" ${DEBUG} ${ATTACH_MODE} -s "5ki" "${mbox}" "${TMP_DIR}/new-mbox" 2>> "${TTY}" || exit 1 +cat "${TMP_DIR}/PJ-Keys.txt" | "${eMailShrinker}" ${DEBUG} ${ATTACH_MODE} -s "5ki" "${mbox}" "${TMP_DIR}/new-mbox" || exit 1 ######################################## # affichage de la structure à la fin -LOG " - ${BLUE}new-mbox:${NC}" -"${eMailShrinker}" -l "${TMP_DIR}/new-mbox" 2>> "${TTY}" || exit 1 +LOG " - ${BLUE}new-mbox:${NC}${YELLOW}" +"${eMailShrinker}" -l "${TMP_DIR}/new-mbox" || ( LOG "${NC}" ; exit 1) -echo -e "\nresul in ${TMP_DIR}/new-mbox" +echo -e "\n${NC}${GREEN}${BOLD}resul in ${TMP_DIR}/new-mbox${NC}" exit 0 diff --git a/src/cpp/Attachment.cpp b/src/cpp/Attachment.cpp index d8db6c2..57c4ce7 100644 --- a/src/cpp/Attachment.cpp +++ b/src/cpp/Attachment.cpp @@ -125,7 +125,7 @@ Attachment::removeSection (string &content, const string &beginTag, const string // ================================================================================ string Attachment::getSection (const string &content, const string &beginTag, const string &endTag) { - DEF_LOG ("Attachment::getSection", "beginTag: " << beginTag << " endTag: " << endTag << " content: " << content); + DEF_LOG ("Attachment::getSection", "beginTag: " << beginTag << " endTag: " << endTag << " content: " << content.substr (0, 100) << "..."); vector list; getSection (content, beginTag, endTag, list); size_t sum (0); @@ -142,18 +142,17 @@ Attachment::getSection (const string &content, const string &beginTag, const str // ================================================================================ void Attachment::getSection (const string &content, const string &beginTag, const string &endTag, vector &result) { - DEF_LOG ("Attachment::getSection", "beginTag: " << beginTag << " endTag: " << endTag << " content: " << content); + DEF_LOG ("Attachment::getSection", "beginTag: " << beginTag << " endTag: " << endTag << " content: " << content.substr (0, 100) << "..."); for (string::size_type startPos (0); (startPos = caseInsensitiveFind (content, beginTag, startPos)) != string::npos; ) { LOG (beginTag << ": " << startPos); string::size_type stopPos = caseInsensitiveFind (content, endTag, startPos); - LOG_BUG (stopPos == string::npos, break, "eMailShrinker: bug A3: " << endTag << " not found! at: " << startPos); - // LOG_BUG (stopPos == string::npos, break, "eMailShrinker: bug A3: " << endTag << " not found! at: " << startPos << endl << content); + LOG_BUG (stopPos == string::npos, break, "eMailShrinker: bug A3: " << endTag << " not found! at: " << startPos << endl << content.substr (0, 100) << "..."); LOG ("start: " << startPos << " stop: " << stopPos); - LOG_BUG (startPos == stopPos, /**/, "eMailShrinker: bug A4: " << endTag << " without " << beginTag << " at: " << startPos); + LOG_BUG (startPos == stopPos, /* */, "eMailShrinker: bug A4: " << endTag << " without " << beginTag << " at: " << startPos); if (startPos != stopPos) { startPos += beginTag.length (); result.push_back (content.substr (startPos, stopPos-startPos)); @@ -317,7 +316,7 @@ Attachment::readMime (ifstream &mbox, streamoff &curPos) { if (line[0] == ' ' || line[0] == '\t') { if (lastVar.empty ()) { - LOG_BUG (true, /**/, "eMailShrinker: bug A5: not compliant MIME. pos: " << (curPos - (line.length () + 1)) << " line: " << line); + LOG_BUG (true, /* */, "eMailShrinker: bug A5: not compliant MIME. pos: " << (curPos - (line.length () + 1)) << " line: " << line); } else { LOG ("add line to var: " << line); env.find (lastVar)->second += cleanString (line); @@ -500,7 +499,7 @@ Attachment::getContent (ifstream &mbox) const { // ================================================================================ void Attachment::println (ofstream &outbox, string content) const { - DEF_LOG ("Attachment::println", "content: " << content); + DEF_LOG ("Attachment::println", "content: " << content.substr (0, 100) << "..."); if (isBase64Encoding ()) base64Encode (content); if (isQuotedPrintableEnconding ()) diff --git a/src/cpp/MainAttachment.cpp b/src/cpp/MainAttachment.cpp index 4637dd8..d8278e7 100644 --- a/src/cpp/MainAttachment.cpp +++ b/src/cpp/MainAttachment.cpp @@ -160,14 +160,13 @@ kaz::operator >> (istream &in, AttachMode &attachMode) { // ================================================================================ const string kaz::headerTypeLabels[] = { - "Same", "Multi", "MainPlain", "AttachHtml" + "Same", "Mixed", "MainPlain" }; const map kaz::headerTypeMap = boost::assign::map_list_of ("same", SAME) - ("multi", MULTI) + ("mixed", MIXED) ("mainplain", MAIN_PLAIN) - ("attachhtml", ATTACH_HTML) ; ostream & kaz::operator << (ostream &out, const HeaderType &headerType) { @@ -254,7 +253,7 @@ MainAttachment::addLink (string &plain, string &html, const string &url, const s // ================================================================================ void MainAttachment::getDisclaim (string &plain, string &html) const { - DEF_LOG ("Attachment::getDisclaim", ""); + DEF_LOG ("MainAttachment::getDisclaim", ""); plain = html = ""; int linkCount (0); @@ -310,7 +309,7 @@ MainAttachment::getDisclaim (string &plain, string &html) const { // ================================================================================ void MainAttachment::addPrevious (const string &href, const string &name, const bool &trust) { - DEF_LOG ("Attachment::addPrevious", "href: " << href << " name: " << name); + DEF_LOG ("MainAttachment::addPrevious", "href: " << href << " name: " << name); const string oldVal = previousLinks [href]; if (name.empty ()) return; @@ -324,7 +323,7 @@ MainAttachment::addPrevious (const string &href, const string &name, const bool void MainAttachment::extractLinks (const string &extractedPlainKAZ) { // plain text => "* name " - DEF_LOG ("Attachment::extractedPlainKAZ", "extractedPlainKAZ: " << extractedPlainKAZ); + DEF_LOG ("MainAttachment::extractedPlainKAZ", "extractedPlainKAZ: " << extractedPlainKAZ); for (string::size_type startPos (0); (startPos = extractedPlainKAZ.find ("http", startPos)) != string::npos; ) { @@ -369,7 +368,7 @@ MainAttachment::extractLinks (const string &extractedPlainKAZ) { void MainAttachment::extractLinks (const vector &liOne) { // html text => "
  • name" - DEF_LOG ("Attachment::extractedPlainKAZ", "liOne.size: " << liOne.size ()); + DEF_LOG ("MainAttachment::extractedPlainKAZ", "liOne.size: " << liOne.size ()); for (const string &one : liOne) { if (caseInsensitiveFind (one, CLASS_ONE) == string::npos) continue; @@ -463,38 +462,60 @@ MainAttachment::removePreviousArchive () { // ================================================================================ void MainAttachment::rewriteHeaders (ifstream &mbox, ofstream &outbox, const HeaderType &headerType) { DEF_LOG ("MainAttachment::rewriteHeaders", "headerType: " << headerType); - if (SAME == headerType) { + switch (headerType) { + case SAME: copy (mbox, outbox, 0, contentPos); return; + case MAIN_PLAIN: + break; + case MIXED: + if (boost::iequals (getContentType (), "multipart/mixed")) { + copy (mbox, outbox, 0, contentPos); + return; + } + break; } + string mime (getMime (mbox)); + mime.insert (0, "\n"); string::size_type startPos = (0); - for (string token : {string ("content-transfer-encoding"), Attachment::contentTypeToken}) { - startPos = caseInsensitiveFind (mime, token); - for (string::size_type stopPos (startPos); + // remove previous content-type at begin of ligne (after a "\n") + for (string token : {string ("content-transfer-encoding"), + Attachment::contentTypeToken}) { + LOG ("token:" << token); + startPos = caseInsensitiveFind (mime, string ("\n")+token); + if (startPos == string::npos) + continue; + for (string::size_type stopPos (startPos+1); (stopPos = mime.find ("\n", stopPos)) != string::npos; ) { if (string (" \t").find (mime [stopPos+1]) == string::npos) { + // not wrap after + movedContentType += mime.substr (startPos+1, stopPos-startPos); mime.erase (startPos, stopPos-startPos); break; } + // merge wrap lines + // find next endl + ++stopPos; } } string contentType (KAZ_EMPTY_TEXT_PLAIN); switch (headerType) { - case SAME: /* no way */; + case SAME: /* no way */ break; case MAIN_PLAIN: contentType = KAZ_EMPTY_TEXT_PLAIN; break; - case ATTACH_HTML: contentType = KAZ_ATTACHMENT_TEXT_HTML; break; - case MULTI: - boundary = "__KAZ__"+boundaryGen (40); - contentType = "Content-Type: multipart/mixed; boundary=\""+boundary+"\""; - boundary = "--"+boundary+"--"; - boundaryMiddleSize = boundary.length () - 2; + case MIXED: + addedBoundary = "__KAZ__"+boundaryGen (40); + contentType = "Content-Type: multipart/mixed; boundary=\""+addedBoundary+"\"\n"; + addedBoundary = "--"+addedBoundary+"--"; + addedBoundaryMiddleSize = addedBoundary.length () - 2; } + // skip '\n' + ++startPos; if (startPos >= mime.length ()) - startPos = mime.length ()-1; + startPos = mime.length () - 1; mime.insert (startPos, contentType); - outbox << mime << flush; + outbox << mime.substr (1) << endl << flush; } // ================================================================================ @@ -641,12 +662,12 @@ MainAttachment::extract (ifstream &mbox, const SizeArg &minSize) const { text/plainOKmute multimute multi empty mailmute plainmute multimute html - */ +*/ void MainAttachment::substitute (ifstream &mbox, ofstream &outbox, const SizeArg &minSize, AttachMode attachMode) { DEF_LOG ("MainAttachment::substitute", "minSize: " << minSize << " AttachMode: " << attachMode); - // preparation + // setup extractPreviousKAZ (mbox); removePreviousArchive (); map translateHtml; @@ -676,28 +697,35 @@ MainAttachment::substitute (ifstream &mbox, ofstream &outbox, const SizeArg &min string plainDisclaim, htmlDisclaim; getDisclaim (plainDisclaim, htmlDisclaim); + LOG_BUG (plainDisclaim.empty () ^ htmlDisclaim.empty (), /* */, + "eMailShrinker: bug M8: not empty disclaim. (plainDisclaim: " << plainDisclaim.size () << " htmlDisclaim: " << htmlDisclaim.size () << ")"); + if (plainDisclaim.empty ()) { + // no change + cerr << "eMailShrinker: original email inchanged" << endl; + copy (mbox, outbox, 0, endPos); + return; + } + + if (boost::iequals (getContentType (), "multipart/report")) { + // no change + cerr << "eMailShrinker: repport email inchange" << endl; + copy (mbox, outbox, 0, endPos); + return; + } + HeaderType headerType (SAME); // copy email - if (!boundary.size () && plainDisclaim.size ()) - switch (attachMode) { - case NONE: LOG_BUG (true, /* */, "eMailShrinker: bug M12: nothing to do"); break; - case FOOTER: headerType = (emptyEMail ? MAIN_PLAIN : SAME); break; - case BOTH: headerType = MULTI; break; - case ATTACHMENT: headerType = ATTACH_HTML; break; - } + switch (attachMode) { + case NONE: LOG_BUG (true, /* */, "eMailShrinker: bug M12: nothing to do"); break; + case FOOTER: headerType = (emptyEMail && !boundary.size ()) ? MAIN_PLAIN : SAME; break; + case BOTH: headerType = MIXED; break; + case ATTACHMENT: headerType = MIXED; break; + } rewriteHeaders (mbox, outbox, headerType); streamoff curPos = contentPos; if (MAIN_PLAIN == headerType) { - LOG ("Replace old content with plain"); - string content (plainDisclaim); - base64Encode (content); - outbox << content << endl; - outbox.flush (); - return; - } - if (ATTACH_HTML == headerType) { - LOG ("Replace old content with html"); + LOG ("Replace old content with plain"); string content (plainDisclaim); base64Encode (content); outbox << content << endl; @@ -705,29 +733,38 @@ MainAttachment::substitute (ifstream &mbox, ofstream &outbox, const SizeArg &min return; } - if (plainDisclaim.size () && emptyEMail && (attachMode & FOOTER)) { + // // XXX => MIXED + // if (ATTACH_HTML == headerType) { + // LOG ("Replace old content with html"); + // string content (plainDisclaim); + // base64Encode (content); + // outbox << content << endl; + // outbox.flush (); + // return; + // } + + if (emptyEMail && (attachMode & FOOTER)) { // case : multi LOG ("Force main text"); cerr << "eMailShrinker: force main text" << endl; string content (plainDisclaim); base64Encode (content); - outbox << boundary.substr (0, boundary.length () -2) << endl + outbox << boundary.substr (0, boundaryMiddleSize) << endl << KAZ_EMPTY_TEXT_PLAIN << endl << content << endl; outbox.flush (); } - if (MULTI == headerType) { + if (movedContentType.size ()) { LOG ("New boundary"); - map::const_iterator it (env.find (contentTypeToken)); - LOG_BUG (it == env.end (), /* */, "eMailShrinker: bug M13: no content-type"); - outbox << boundary.substr (0, boundary.length () -2) << endl - << Attachment::contentTypeToken << ": " << it->second << endl; + outbox << addedBoundary.substr (0, addedBoundaryMiddleSize) << endl + << movedContentType << endl; } for (Attachment *attachP : allMarkedPtrs) { copy (mbox, outbox, curPos, attachP->beginInParent); - LOG_BUG (attachP->toUpdate && attachP->toExtract, /**/, "eMailShrinker: bug M5: update and extract. pos: " << attachP->beginPos); + outbox << endl; // force end MIME section + LOG_BUG (attachP->toUpdate && attachP->toExtract, /* */, "eMailShrinker: bug M5: update and extract. pos: " << attachP->beginPos); if (attachP->isSigned) { LOG ("don't change signed content"); @@ -742,8 +779,8 @@ MainAttachment::substitute (ifstream &mbox, ofstream &outbox, const SizeArg &min bool isHtml = textProp == HTML; bool isDisclaimer = attachP->toDisclaim; - LOG_BUG (isPlain && isHtml, /**/, "eMailShrinker: bug M6: plain and html: " << attachP->getContentType ()); - LOG_BUG (! (isPlain || isHtml), /**/, "eMailShrinker: bug M7: not plain or html: " << attachP->getContentType ()); + LOG_BUG (isPlain && isHtml, /* */, "eMailShrinker: bug M6: plain and html: " << attachP->getContentType ()); + LOG_BUG (! (isPlain || isHtml), /* */, "eMailShrinker: bug M7: not plain or html: " << attachP->getContentType ()); LOG ("toUpdate: isPlain: " << isPlain << " isHtml: " << isHtml << " isDisclaimer: " << isDisclaimer); if (attachP != this) copy (mbox, outbox, attachP->beginInParent, attachP->contentPos); @@ -806,25 +843,31 @@ MainAttachment::substitute (ifstream &mbox, ofstream &outbox, const SizeArg &min curPos = attachP->endPos; } - if (plainDisclaim.size () && (attachMode & ATTACHMENT)) { + if (subAttachements.size ()) { + streamoff lastPos = subAttachements.back ().endPos; + copy (mbox, outbox, curPos, lastPos); + curPos = lastPos; + } + if (attachMode & ATTACHMENT) { LOG ("Add kaz attachment"); + if (movedContentType.size ()) { + copy (mbox, outbox, curPos, endPos); + outbox << addedBoundary.substr (0, addedBoundaryMiddleSize) << endl + << KAZ_ATTACHMENT_TEXT_HTML << endl; + } else + outbox << boundary.substr (0, boundaryMiddleSize) << endl + << KAZ_ATTACHMENT_TEXT_HTML << endl; cerr << "eMailShrinker: force attachment" << endl; - if (subAttachements.size ()) { - streamoff lastPos = subAttachements.back ().endPos; - copy (mbox, outbox, curPos, lastPos); - curPos = lastPos; - } string content (KAZ_HTML_CONTENT+htmlDisclaim+BODY_END+HTML_END); base64Encode (content); - outbox << boundary.substr (0, boundary.length () -2) << endl - << KAZ_ATTACHMENT_TEXT_HTML << endl - << content << endl; + outbox << content << endl; outbox.flush (); } - copy (mbox, outbox, curPos, endPos); - if (MULTI == headerType) - outbox << boundary.substr (0, boundary.length ()) << endl; + if (!movedContentType.size ()) + copy (mbox, outbox, curPos, endPos); + else + outbox << addedBoundary << endl; outbox.close (); } diff --git a/src/cpp/eMailShrinker.cpp b/src/cpp/eMailShrinker.cpp index c84f0ea..9f01c76 100644 --- a/src/cpp/eMailShrinker.cpp +++ b/src/cpp/eMailShrinker.cpp @@ -33,8 +33,8 @@ //////////////////////////////////////////////////////////////////////////// #include "version.hpp" -const std::string kaz::LAST_VERSION_NUM ("2.17"); -const std::string kaz::LAST_VERSION_DATE ("2023-04-23"); +const std::string kaz::LAST_VERSION_NUM ("2.18"); +const std::string kaz::LAST_VERSION_DATE ("2023-10-29"); const std::string kaz::LAST_VERSION (LAST_VERSION_NUM+" "+LAST_VERSION_DATE+" eMailShrinker"); #include @@ -202,9 +202,11 @@ main (int argc, char** argv) { attachment.markSignificant (minAttachSize, mbox); mbox.close (); - if (listFlag) + if (listFlag) { // debug cerr << attachment; + return 0; + } if (updateListFlag) { // case update diff --git a/src/cpp/jirafeauAPI.cpp b/src/cpp/jirafeauAPI.cpp deleted file mode 100644 index ec52de8..0000000 --- a/src/cpp/jirafeauAPI.cpp +++ /dev/null @@ -1,294 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// Copyright KAZ 2021 // -// // -// contact (at) kaz.bzh // -// // -// This software is a filter to shrink email by attachment extraction. // -// // -// This software is governed by the CeCILL-B license under French law and // -// abiding by the rules of distribution of free software. You can use, // -// modify and/or redistribute the software under the terms of the // -// CeCILL-B license as circulated by CEA, CNRS and INRIA at the following // -// URL "http://www.cecill.info". // -// // -// As a counterpart to the access to the source code and rights to copy, // -// modify and redistribute granted by the license, users are provided // -// only with a limited warranty and the software's author, the holder of // -// the economic rights, and the successive licensors have only limited // -// liability. // -// // -// In this respect, the user's attention is drawn to the risks associated // -// with loading, using, modifying and/or developing or reproducing the // -// software by the user in light of its specific status of free software, // -// that may mean that it is complicated to manipulate, and that also // -// therefore means that it is reserved for developers and experienced // -// professionals having in-depth computer knowledge. Users are therefore // -// encouraged to load and test the software's suitability as regards // -// their requirements in conditions enabling the security of their // -// systems and/or data to be ensured and, more generally, to use and // -// operate it in the same conditions as regards security. // -// // -// The fact that you are presently reading this means that you have had // -// knowledge of the CeCILL-B license and that you accept its terms. // -//////////////////////////////////////////////////////////////////////////// - -#define LAST_VERSION "1.1 2022-10-30 jirafeauAPI" - -#include -#include -#include -#include -#include -#include - -#include "kazDebug.hpp" -#include "kazMisc.hpp" -#include "SizeArg.hpp" - -using namespace std; -using namespace boost; -using namespace boost::program_options; -using namespace kaz; - -namespace bfs = boost::filesystem; - -// ================================================================================ -static options_description mainDescription ("Main options", getCols ()); -static options_description hide ("Hidded options", getCols ()); -static char *prog = NULL; - -// ================================================================================ -void -usage (const string &msg = "", const bool &hidden = false) { - if (!msg.empty ()) { - cout << msg << endl; - exit (1); - } - cout << endl - << "Usage: " << endl - << " A) " << prog << " [-s size] [-t period] [-c content-type] [-n attachName] [-f server] send file [password] > url,delCode" << endl - << " B) " << prog << " [-t period] [-f server] update ref > dealine" << endl - << endl << " store ficle" << endl << endl - << " A: send file (options : s, t)" << endl - << " B: update deadline (options : t) " << endl - << endl << mainDescription - << endl; - if (hidden) - cout << hide << endl; - exit (0); -} - -void -version () { - cout << LAST_VERSION << " KAZ team production (https://kaz.bzh/)" << endl; - exit (0); -} - -static auto startPrg = std::chrono::high_resolution_clock::now (); -void -showTime (string msg) { - using namespace std::chrono; - static auto stopPrg = high_resolution_clock::now (); - - cerr << msg << " done in " << ns2string (duration_cast > (stopPrg-startPrg).count ()) << endl; -} - -// ================================================================================ -static size_t -WriteCallback (void *contents, size_t size, size_t nmemb, void *userp) { - ((std::string*) userp)->append ((char*) contents, size * nmemb); - return size * nmemb; -} - -// ================================================================================ -static const string inputFile = "input-file"; -static const char *const inputFileC = inputFile.c_str (); - -int -main (int argc, char** argv) { - // uncomment next line in case of debug parse options - // Log::debug = true; - DEF_LOG ("main:", ""); - prog = argv [0]; - bool - debugFlag (false), - helpFlag (false), - versionFlag (false), - useTheForceLuke (false); - enum JirCmd { SEND, UPDATE } jirCmd; - string - inputFileName, - password, - contentType, - attachName, - urlBase ("http://file.kaz.bzh"), - uploadPage ("/a.php"), - updatePage ("/a.php"), - minimumAvailability ("month"), - proxy; - - SizeArg maxUploadSize ("100 Mi"); - - try { - mainDescription.add_options () - ("help,h", bool_switch (&helpFlag), "produce this help message") - ("version,v", bool_switch (&versionFlag), "display version information") - ("contentType,c", value (&contentType)->default_value (contentType), "content-type of the sended file") - ("attachName,n", value (&attachName)->default_value (attachName), "force attachment name") - ("minimumAvailability,t", value (&minimumAvailability)->default_value (minimumAvailability), "minimum period of available download") - ("maxUploadSize,s", value (&maxUploadSize)->default_value (maxUploadSize), "maximum upload size") - ("file server registery,f", value (&urlBase)->default_value (urlBase), "server where file are temporary stored") - ; - - hide.add_options () - ("useTheForceLuke", bool_switch (&useTheForceLuke), "display hidded options") - ("debug,g", bool_switch (&debugFlag), "debug mode") - ("proxy,p", value (&proxy)->default_value (proxy), "set proxy (proxy-host.org:8080)") - ("uploadPage,u", value (&uploadPage)->default_value (uploadPage), "upload page") - ("updatePage,v", value (&updatePage)->default_value (updatePage), "update page") - ; - - options_description cmd ("All options"); - cmd.add (mainDescription).add (hide).add_options () - (inputFileC, value > (), "input") - ; - positional_options_description p; - p.add (inputFileC, -1); - variables_map vm; - basic_parsed_options parsed = command_line_parser (argc, argv).options (cmd).positional (p).run (); - store (parsed, vm); - notify (vm); - - if (debugFlag) { -#ifdef DISABLE_LOG - cerr << "No debug option available (was compiled with -DDISABLE_LOG)" << endl; -#endif - } - Log::debug = debugFlag; - - if (useTheForceLuke) - usage ("", true); - if (versionFlag) - version (); - if (helpFlag) - usage (); - - if (vm.count (inputFileC)) { - vector var = vm[inputFileC].as > (); - int nbArgs = vm[inputFileC].as > ().size (); - if (!nbArgs) - usage ("No command"); - if (var [0].compare ("send") == 0) - jirCmd = SEND; - else if (var [0].compare ("update") == 0) - jirCmd = UPDATE; - else - usage ("Unknown command ("+var [0]+")"); - if (nbArgs < 2) - usage ("no input file"); - inputFileName = var [1]; - if (nbArgs == 3) - password = var [2]; - if (nbArgs > 3) - usage ("Too much arguments"); - } - } catch (std::exception &e) { - cerr << "error: " << e.what() << endl; - usage (); - return 1; - } catch (...) { - cerr << "Exception of unknown type!" << endl; - return 1; - } - - if (inputFileName.empty ()) - usage ("no input"); - - CURL *easyhandle = curl_easy_init (); - if (! easyhandle) { - cerr << "no curl" << endl; - return 1; - } - - string readBuffer; - if (proxy.length ()) - curl_easy_setopt(easyhandle, CURLOPT_PROXY, proxy.c_str ()); - curl_easy_setopt (easyhandle, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt (easyhandle, CURLOPT_WRITEDATA, &readBuffer); - curl_mime *multipart = curl_mime_init (easyhandle); - curl_mimepart *part = nullptr; - - switch (jirCmd) { - case SEND: { - LOG ("SEND: " << (urlBase+uploadPage)); - curl_easy_setopt (easyhandle, CURLOPT_URL, (urlBase+uploadPage).c_str ()); - - LOG ("maxUploadSize: " << maxUploadSize); - long uploadsize = (size_t) maxUploadSize; - curl_easy_setopt (easyhandle, CURLOPT_INFILESIZE, uploadsize); - - LOG ("time: " << minimumAvailability); - part = curl_mime_addpart (multipart); - curl_mime_name (part, "time"); - curl_mime_data (part, minimumAvailability.c_str (), CURL_ZERO_TERMINATED); - - if (password.size ()) { - LOG ("key: " << password); - part = curl_mime_addpart (multipart); - curl_mime_name (part, "key"); - curl_mime_data (part, password.c_str (), CURL_ZERO_TERMINATED); - } - - LOG ("inputFileName: " << bfs::path (inputFileName).filename ()); - part = curl_mime_addpart (multipart); - curl_mime_name (part, "file"); - if (contentType.length ()) { - LOG ("contentType: " << contentType); - curl_mime_type (part, contentType.c_str ()); - } - if (attachName.empty ()) { - attachName = bfs::path (inputFileName).filename ().c_str (); - LOG ("attachName: " << attachName); - } - curl_mime_filename (part, attachName.c_str ()); - FILE *fp = fopen (inputFileName.c_str (), "r"); - fseek (fp, 0L, SEEK_END); - long int fsize (ftell (fp)); - fseek (fp, 0L, SEEK_SET); - curl_mime_data_cb (part, fsize, - (curl_read_callback) fread, - (curl_seek_callback) fseek, - NULL, //(curl_seek_callback) fclose, - fp); - } - break; - - case UPDATE: { - LOG ("UPDATE: " << (urlBase+updatePage)); - curl_easy_setopt (easyhandle, CURLOPT_URL, (urlBase+updatePage).c_str ()); - - LOG ("h: " << inputFileName); - part = curl_mime_addpart (multipart); - curl_mime_name (part, "h"); - curl_mime_data (part, inputFileName.c_str (), CURL_ZERO_TERMINATED); - - LOG ("u: " << minimumAvailability); - part = curl_mime_addpart (multipart); - curl_mime_name (part, "u"); - curl_mime_data (part, minimumAvailability.c_str (), CURL_ZERO_TERMINATED); - } - break; - } - - curl_easy_setopt (easyhandle, CURLOPT_MIMEPOST, multipart); - CURLcode res (curl_easy_perform (easyhandle)); - curl_easy_cleanup (easyhandle); - cout << readBuffer << endl; - - showTime ("Upload"); - if (res != CURLE_OK) - cerr << prog << " failed: " << curl_easy_strerror (res) << endl; - return 0; -} - -// ================================================================================ diff --git a/src/cpp/kazMisc.cpp b/src/cpp/kazMisc.cpp index 03f17a0..1e09ba5 100644 --- a/src/cpp/kazMisc.cpp +++ b/src/cpp/kazMisc.cpp @@ -105,7 +105,7 @@ kaz::ns2string (const double &delta) { // ================================================================================ void kaz::replaceAll (string& str, const string &from, const string &to) { - DEF_LOG ("kaz::replaceAll", "form: " << from << " to: " << to); + DEF_LOG ("kazMisc::replaceAll", "form: " << from << " to: " << to); if (str.empty () || from.empty ()) return; for (string::size_type startPos (0); @@ -116,7 +116,7 @@ kaz::replaceAll (string& str, const string &from, const string &to) { void kaz::replaceAll (string& str, const map &subst) { - DEF_LOG ("kaz::replaceAll", "str: " << str); + DEF_LOG ("kazMisc::replaceAll", "str: " << str.substr (0, 100) << "..."); for (map::const_iterator it = subst.begin (); it != subst.end (); ++it) replaceAll (str, it->first, it->second); } @@ -124,16 +124,16 @@ kaz::replaceAll (string& str, const map &subst) { // ================================================================================ void kaz::toLower (string &content) { - DEF_LOG ("kaz::toLower", "content: " << content); + DEF_LOG ("kazMisc::toLower", "content: " << content.substr (0, 100) << "..."); static locale loc; for (string::size_type i = 0; i < content.length (); ++i) content [i] = tolower (content[i], loc); - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } const string & kaz::toUpperIfNeed (const string &src, string &tmp) { - DEF_LOG ("kaz::toUpperIfNeed", "src: " << src); + DEF_LOG ("kazMisc::toUpperIfNeed", "src: " << src); for (string::const_iterator it = src.begin (); it != src.end (); ++it) if (*it != toupper (*it)) { tmp.reserve (); @@ -151,7 +151,7 @@ caseInsensitiveCharCompare (char a, char b) { string::size_type kaz::caseInsensitiveFind (const string& s, const string& pattern, const string::size_type &pos) { - DEF_LOG ("kaz::caseInsensitiveFind", "pattern: " << pattern << " pos: " << pos << " s: " << s); + DEF_LOG ("kazMisc::caseInsensitiveFind", "pattern: " << pattern << " pos: " << pos << " s: " << s.substr (0, 100) << "..."); string tmp; const string &upperPattern (toUpperIfNeed (pattern, tmp)); LOG ("pattern: " << upperPattern); @@ -164,7 +164,7 @@ kaz::caseInsensitiveFind (const string& s, const string& pattern, const string:: string::size_type kaz::caseInsensitiveRFind (const string& s, const string& pattern, const string::size_type &pos) { - DEF_LOG ("kaz::caseInsensitiveRFind", "pattern: " << pattern << " pos: " << pos << " s: " << s); + DEF_LOG ("kazMisc::caseInsensitiveRFind", "pattern: " << pattern << " pos: " << pos << " s: " << s.substr (0, 100) << "..."); string tmp; const string &upperPattern (toUpperIfNeed (pattern, tmp)); LOG ("pattern: " << upperPattern); @@ -192,7 +192,7 @@ kaz::boundaryGen (const int &size) { template void kaz::quotedDecode (string &content) { - DEF_LOG ("kaz::quotedDecode", "delim: " << delim << " content: " << content); + DEF_LOG ("kazMisc::quotedDecode", "delim: " << delim << " content: " << content.substr (0, 100) << "..."); string::size_type len (content.length ()); if (!len) return; @@ -206,25 +206,24 @@ kaz::quotedDecode (string &content) { continue; } if (p+1 < content.end () && *(p+1) == '\n') { - - LOG_BUG (q == content.begin (), ++p;continue, "kazMisc::quotedDecode bug: bad quoted-printable format. (start with '=', delim: " << int (delim) << " content: " << content << ")"); + LOG_BUG (q == content.begin (), ++p;continue, "kazMisc::quotedDecode bug: bad quoted-printable format. (start with '=', delim: " << int (delim) << " content: " << content.substr (0, 100) << "...)"); ++p; --q; continue; } - LOG_BUG (p+3 > content.end () || !isxdigit (p[1]) || !isxdigit (p[2]), return, "kazMisc::quotedDecode bug: bad quoted-printable format. (delim: " << int (delim) << " content: " << content << ")"); + LOG_BUG (p+3 > content.end () || !isxdigit (p[1]) || !isxdigit (p[2]), return, "kazMisc::quotedDecode bug: bad quoted-printable format. (delim: " << int (delim) << " p:" << content.substr (p-content.begin (), 3) << " content: " << content.substr (0, 100) << "... len: " << len << ")"); *q = (char) ((getHexaVal (p[1]) << 4) + getHexaVal (p[2])); p += 2; } content.resize (q-content.begin ()); - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::quotedEncode (string &content) { - DEF_LOG ("kaz::quotedDecode", "content: " << content); + DEF_LOG ("kazMisc::quotedDecode", "content: " << content.substr (0, 100) << "..."); string::size_type nbQuoted (0); for (string::const_iterator it = content.begin (); it != content.end (); ++it) if (isQuotedPrintable (*it)) @@ -267,13 +266,13 @@ kaz::quotedEncode (string &content) { ++cols; } content.swap (result); - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::base64Decode (string &content) { - DEF_LOG ("kaz::base64Decode", "content: " << content); + DEF_LOG ("kazMisc::base64Decode", "content: " << content.substr (0, 100) << "..."); string::size_type len (content.length ()); if (!len) return; @@ -294,7 +293,7 @@ kaz::base64Decode (string &content) { if (!isBase64 (c)) { content.resize (lastOK-content.begin ()); - LOG ("kazMisc::base64Decode bug: bad base64 format. (content: " << content << ")"); + LOG ("kazMisc::base64Decode bug: bad base64 format. (content: " << content.substr (0, 100) << "...)"); return; } buff [idx] = getBase64Val (c); @@ -317,13 +316,13 @@ kaz::base64Decode (string &content) { } } content.resize (q-content.begin ()); - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::base64Encode (string &content) { - DEF_LOG ("kaz::base64Encode", "content: " << content); + DEF_LOG ("kazMisc::base64Encode", "content: " << content.substr (0, 100) << "..."); string::size_type length (content.length ()); std::string result; result.reserve ((length + 2) / 3 * 4 + length / MAX_QUOTED_PRINTABLE_SIZE + 1); @@ -353,13 +352,13 @@ kaz::base64Encode (string &content) { } } content = result; - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::iso2utf (string &content) { - DEF_LOG ("kaz::iso2utf", "content: " << content); + DEF_LOG ("kazMisc::iso2utf", "content: " << content.substr (0, 100) << "..."); string::size_type len (content.length ()); if (!len) return; @@ -385,14 +384,14 @@ kaz::iso2utf (string &content) { if (p == q) break; } - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::encodedWordDecode (string &content) { // rfc2047 - DEF_LOG ("kaz::encodedWordDecode", "content: " << content); + DEF_LOG ("kazMisc::encodedWordDecode", "content: " << content.substr (0, 100) << "..."); string::size_type charsetPos = content.find ("=?"); if (charsetPos == string::npos) return; @@ -436,29 +435,29 @@ kaz::encodedWordDecode (string &content) { pos = m.position () + m.str ().length (); } content = result + content.substr (pos); - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ void kaz::charsetValueDecode (string &content) { // rfc2184 - DEF_LOG ("kaz::charsetValueDecode", "content: " << content); + DEF_LOG ("kazMisc::charsetValueDecode", "content: " << content.substr (0, 100) << "..."); string::size_type langPos = content.find ("'"); - LOG_BUG (langPos == string::npos, return, "kazMisc::charsetValueDecode bug: no '. (content: " << content << ")"); + LOG_BUG (langPos == string::npos, return, "kazMisc::charsetValueDecode bug: no '. (content: " << content.substr (0, 100) << "...)"); string::size_type contentPos = content.find ("'", langPos+1); - LOG_BUG (contentPos == string::npos, return, "kazMisc::charsetValueDecode bug: no double '. (content: " << content << ")"); + LOG_BUG (contentPos == string::npos, return, "kazMisc::charsetValueDecode bug: no double '. (content: " << content.substr (0, 100) << "...)"); string tmp (content.substr (contentPos+1)); quotedDecode<'%'> (tmp); - LOG ("tmp: " << tmp); + LOG ("tmp: " << tmp.substr (0, 100) << "..."); string charset (content.substr (0, langPos)); toLower (charset); if (! caseInsensitiveFind (charset, "ISO")) iso2utf (tmp); content = tmp; - LOG ("content: " << content); + LOG ("content: " << content.substr (0, 100) << "..."); } // ================================================================================ diff --git a/src/include/MainAttachment.hpp b/src/include/MainAttachment.hpp index edc95c4..74d4781 100644 --- a/src/include/MainAttachment.hpp +++ b/src/include/MainAttachment.hpp @@ -51,7 +51,7 @@ namespace kaz { ostream &operator << (ostream &out, const AttachMode &attachMode); istream &operator >> (istream &in, AttachMode &attachMode); - enum HeaderType { SAME, MULTI, MAIN_PLAIN, ATTACH_HTML }; + enum HeaderType { SAME, MIXED, MAIN_PLAIN }; extern const string headerTypeLabels[]; extern const map headerTypeMap; ostream &operator << (ostream &out, const HeaderType &headerType); @@ -105,6 +105,14 @@ namespace kaz { vector allMarkedPtrs; /*! previous links find in mbox */ map previousLinks; + /*! boundary if a mixed section is added including previous and next "--" */ + string addedBoundary; + /*! size of added boundary before the last "--" */ + streamoff addedBoundaryMiddleSize; + /*! moved conten-type headers */ + string movedContentType; + + /*! add link only if no significant value already exist. Trust the values from html.*/ void addPrevious (const string &href, const string &name, const bool &trust = false); diff --git a/src/include/kazMisc.hpp b/src/include/kazMisc.hpp index 03ca6a4..112cd17 100644 --- a/src/include/kazMisc.hpp +++ b/src/include/kazMisc.hpp @@ -100,7 +100,7 @@ namespace kaz { inline bool isQuotedPrintable (const char &c) { return - c == ' ' || c == '\t' || (c >= 33 && c <= 126 && c != '=' && c != '.'); + c == ' ' || (c >= 33 && c <= 126 && c != '=' && c != '.'); // '.' is available in rfc2184 but it avoid to check '.' alone in a line :-) } diff --git a/src/mainpage.md b/src/mainpage.md index de42aec..e644860 100644 --- a/src/mainpage.md +++ b/src/mainpage.md @@ -16,11 +16,10 @@ Dans le cas où l'on souhaite signer ses messages, il faut au préalable dépose -Main Programmes: +Main Programme: * [eMailShrinker](eMailShrinker_8cpp.html) - * [jirafeauAPI](jirafeauAPI_8cpp.html) Main classes: * [MainAttachment](classkaz_1_1MainAttachment.html) * [Attachment](classkaz_1_1Attachment.html) - \ No newline at end of file + diff --git a/structures.odt b/structures.odt new file mode 100644 index 0000000000000000000000000000000000000000..f4e003bce7e04120712cf0bd4897f541090c0c5d GIT binary patch literal 39669 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�KklT$2Sr(DS#9JK=6MT?lbS?#bre> zeOD1(e-wxv1Pu3b0{tQs%HUR2~mMjcvZD-@=0)*Sapsu`JKoAJ*2u3tvE?obF zME@I7*xw-8L4Y=3S1uVlI0WeA`j24Lzkz-J8!$Kw=I8{3f*t=6hw?X^tN(yw1^nl! z{XQCA#KriNBv&{P?&kV`;rvdv{omj?xxj2(z^?x%((fZG_&X$+lbaI+40Zc|@H6{2 zIM$9ZARPS9{QS-`G4lWB=WZasvh&kvsy(&+f`&Nja})po_qS`Mgh2Tpz2WlI`sdR7 zDJhbDj{OB8Ifan?ed&iGdJqdAc;|WjDJAxUX%3130OaufdA?XQM6?_XhucAI{uM9( zDf#nOqdcqPW^e-l?&|OJ6~aT@jJ9qND=5&;(UlASbC=TzYV#B2=cs>mAMfJx|1K|r zevQ`uDfg?=y>l-AT`;46b2a}I{8fA0>EwSG?BxHTp|w=e(C-c-LTq4!((z8YYyB6< CmLnYi literal 0 HcmV?d00001