-->

2026年5月18日月曜日

alfresco26.x メール取り込み設定(スパム対策あり)

1. システム構成の概要

メールサーバーから自動受信したメールをSpamAssassinでフィルタリングし、Alfrescoに取り込む構成です。

📬 メールサーバー (POP3S)
getmail
spamc-wrapper.sh
SpamAssassin
postfix
🗂 Alfresco
コンポーネント役割備考
getmail 6.xPOP3S でメール取得cron で5分毎に実行
SpamAssassin 4.0スパム判定・件名に ***SPAM*** 追加スコア7以上は破棄
postfix 3.8Alfresco への配送・遅延制御5秒/通の遅延設定
Alfresco 26.1メール格納・振り分けCommunity版 Ansible インストール
ℹ️ Alfresco Community版はSMTPの細かい制御ができないため、postfixで遅延処理を行い配送エラーを防止しています。

2. Alfresco 事前設定(alfresco-global.properties)

Alfresco のメール受信機能を有効にするため、設定ファイルにメール関連プロパティを追加します。

① サンプルファイルから設定を確認

terminal
$ vi /etc/opt/alfresco/content-services/classpath/alfresco-global.properties.sample

② alfresco-global.properties に追記

terminal
$ sudo vi /etc/opt/alfresco/content-services/classpath/alfresco-global.properties

ファイル末尾の ### Begin - Custom user properties - ANSIBLE MANAGED BLOCK セクションに追記します。

alfresco-global.properties(追記内容)
# Enable/Disable the inbound email service.
email.inbound.enabled=true
# Email Server properties
email.server.enabled=true
email.server.port=1025 # 25はpostfixと干渉するため変更
email.server.domain=alfresco.example.com
email.inbound.unknownUser=admin
alfresco.host=サーバーのIPアドレス # 空欄だとWebDAV URLが旧IPで表示される
alfresco.port=8080
alfresco.protocol=http
⚠️ email.server.port=25 はpostfixが使用しているため、1025など別のポートに変更してください。alfresco.host を空欄にすると Share の WebDAV URL 表示で旧サーバーの IP が使われることがあります。必ず設定してください。

③ Alfrescoサービスを再起動

terminal
$ sudo systemctl restart alfresco-content.service

3. Postfix のインストールと設定

インストール

terminal
$sudo apt install postfix -y
$sudo systemctl enable --now postfix

/etc/postfix/main.cf の設定

/etc/postfix/main.cf
myhostname = alfresco.example.com
mydomain = example.com
inet_interfaces = localhost
relayhost = [alfresco.example.com]:1025
message_size_limit = 40960000
# 配送遅延(Alfrescoの処理速度に合わせて調整)
smtp_destination_rate_delay = 5s
smtp_destination_concurrency_limit = 1
⚠️ 遅延設定なしで振り分けルールが設定されたフォルダに配送すると、処理しきれず配送エラーや0byteファイルが発生します。

4. getmail のインストールと設定

インストール

terminal
$sudo apt install getmail6 -y && mkdir -m 0700 ~/.getmail

~/.getmail/getmailrc の設定

~/.getmail/getmailrc
[options]
read_all = false
max_messages_per_session = 10
[retriever]
type = SimplePOP3SSLRetriever
server = mail.example.com
username = info@example.com
password = xxxxxxxx
[destination]
type = MDA_external
path = /usr/local/bin/spamc-wrapper.sh
arguments = ("%(sender)",)

cron 設定(5分毎)

crontab -e
# 標準エラーを /dev/null に捨てないと配送エラーになる場合がある
*/5 * * * * /usr/bin/getmail >> /var/log/getmail.log 2>&1

5. SpamAssassin の導入

インストール

terminal
$sudo apt install spamassassin spamc -y
$sudo systemctl enable --now spamd

/etc/default/spamd の設定

Ubuntu 24.04 では /etc/default/spamdOPTIONS--siteconfigpath を追加しないと、local.cf のブラックリスト設定が spamd に読み込まれません。

⚠️ この設定がないと spamassassin -t ではブラックリストが有効でも、spamc 経由では無効になります。必ず設定してください。
/etc/default/spamd(OPTIONS行を変更)
# 変更前
OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
# 変更後(--siteconfigpath を追加)
OPTIONS="--create-prefs --max-children 5 --helper-home-dir --siteconfigpath /etc/spamassassin"
terminal
$sudo systemctl restart spamd

/etc/spamassassin/local.cf の設定

/etc/spamassassin/local.cf
required_score 5.0
ok_locales ja
ok_languages ja en
rewrite_header Subject ***SPAM***
# ブラックリスト(スパム送信元として多い国別ドメイン)
blacklist_from *@*.cn
blacklist_from *@*.su
blacklist_from *@スパムドメイン.*
blacklist_from MAILER-DAEMON@*
# ホワイトリスト(自組織・信頼するドメイン)
whitelist_from *@example.com
whitelist_from *@信頼ドメイン.co.jp
whitelist_from *@docomo.ne.jp
whitelist_from *@softbank.ne.jp
whitelist_from *@au.com
スコア対応
5.0 以上スパム判定(件名に ***SPAM*** 追加)
7.0 以上破棄(spamc-wrapper.sh で Alfresco に配送しない)
5.0 未満正常メール(Alfresco へ配送)

6. spamc-wrapper スクリプト

getmail から呼び出され、SpamAssassin でフィルタリング後に postfix 経由で Alfresco へ配送します。

⚠️ postfix の content_filter = spamassassin を設定するとメールループが発生します。必ずコメントアウトし、getmailrc からラッパースクリプトを直接呼び出す方式にしてください。
/usr/local/bin/spamc-wrapper.sh
#!/bin/bash
# MAILER-DAEMON は破棄
if [[ "$1" == *"MAILER-DAEMON"* ]] || [[ "$1" == "" ]]; then
echo "DISCARDED: MAILER-DAEMON" >> /tmp/spamc-wrapper.log
exit 0
fi
TMPFILE=$(mktemp)
/usr/bin/spamc -f > "$TMPFILE"
# スコアを取得
SPAM_FLAG=$(grep "X-Spam-Flag:" "$TMPFILE" | head -1 | awk '{print $2}')
SPAM_SCORE=$(grep "X-Spam-Status:" "$TMPFILE" | grep -oP 'score=\K[0-9.]+' | sort -rn | head -1)
# スコア7以上は破棄
if [ -n "$SPAM_SCORE" ]; then
SCORE_INT=$(echo "$SPAM_SCORE" | cut -d. -f1)
if [ "$SCORE_INT" -ge 7 ]; then
echo "DISCARDED(score): $1 score=$SPAM_SCORE" >> /tmp/spamc-wrapper.log
rm -f "$TMPFILE"; exit 0
fi
fi
# X-Spam-Flag: YES は破棄
if grep -qi "^X-Spam-Flag: YES" "$TMPFILE"; then
echo "DISCARDED(flag-yes): $1" >> /tmp/spamc-wrapper.log
rm -f "$TMPFILE"; exit 0
fi
# 件名・X-Spam-Prev-SubjectにSPAMが含まれる場合も破棄
if grep -i "^Subject:\|^X-Spam-Prev-Subject:" "$TMPFILE" | grep -qi "SPAM"; then
echo "DISCARDED(subject): $1" >> /tmp/spamc-wrapper.log
rm -f "$TMPFILE"; exit 0
fi
# ISO-2022-JP → UTF-8 変換
# ヘッダーのcharset指定またはJISエスケープシーケンスで判定
if grep -qi "charset.*iso-2022-jp\|charset.*jis" "$TMPFILE" || grep -q $'\x1b\$B\|\x1b\$@' "$TMPFILE"; then
CONVERTED=$(mktemp)
iconv -f ISO-2022-JP -t UTF-8//IGNORE "$TMPFILE" > "$CONVERTED" 2>/dev/null
if [ -s "$CONVERTED" ]; then
echo "CONVERTED(iso2022jp->utf8): $1" >> /tmp/spamc-wrapper.log
sed -i 's/charset=iso-2022-jp/charset=UTF-8/gI' "$CONVERTED"
sed -i 's/charset="iso-2022-jp"/charset="UTF-8"/gI' "$CONVERTED"
mv "$CONVERTED" "$TMPFILE"
else
echo "CONVERT_FAILED(iso2022jp->utf8): $1" >> /tmp/spamc-wrapper.log
rm -f "$CONVERTED"
fi
fi
# Alfrescoへ配送
/usr/sbin/sendmail -oi -f "$1" mail@alfresco.example.com < "$TMPFILE"
rm -f "$TMPFILE"
terminal
$sudo chmod +x /usr/local/bin/spamc-wrapper.sh

7. Alfresco スクリプト群

メール格納フォルダのルールに登録するJavaScriptスクリプトです。リポジトリ > Data Dictionary > Scripts に登録し、メール格納フォルダのルールに設定します。

スクリプト役割実行設定
mailRename.js件名に送信日時を付加・URLエンコード文字を全角に置換・MIMEタイプ修正通常実行
mailFolderCreate.jsメール本体受信時にフォルダを作成・アスペクト付与通常実行
mailFolderMove.jsメールをフォルダへ移動バックグラウンド実行
mailSort.js送信者アドレスで振り分け・SPAMはspamフォルダへ・自組織ドメインは別サイトへ通常実行
mailSortAdd.js手動振り分け時に送信者アドレスを登録各振り分けフォルダのルール
⚠️ mailFolderMove.js のみ「バックグラウンドで実行」にチェックが必要です。move API はバックグラウンド実行が必要なため、別スクリプトに分離しています。

mailRename.js — ファイル名に送信日時を追加・MIMEタイプ修正

mailRename.js
// 送信日時を取得してファイル名に付加
var mailDate = document.properties['cm:sentdate'];
var creatDate = document.properties['cm:created'];
if (mailDate != null && document.isDocument) {
var year_str = ('0' + mailDate.getFullYear()).slice(-2);
var month_str = ('0' + (1 + mailDate.getMonth())).slice(-2);
var day_str = ('0' + mailDate.getDate()).slice(-2);
var hour_str = ('0' + mailDate.getHours()).slice(-2);
var minute_str = ('0' + mailDate.getMinutes()).slice(-2);
var second_str = ('0' + mailDate.getSeconds()).slice(-2);
var Csecond_str = ('0' + creatDate.getSeconds()).slice(-1);
var format_str = 'YYMMDDhhmmssz'
.replace(/YY/g, year_str).replace(/MM/g, month_str)
.replace(/DD/g, day_str).replace(/hh/g, hour_str)
.replace(/mm/g, minute_str).replace(/ss/g, second_str)
.replace(/z/g, Csecond_str);
var stringDate = format_str + '_';
} else {
var stringDate = '';
}
// MIMEタイプを拡張子で判定・修正
var fullName = document.properties['cm:name'];
var lowerName = new String(fullName).toLowerCase();
var ext = lowerName.lastIndexOf('.') >= 0 ? lowerName.substring(lowerName.lastIndexOf('.')+1) : '';
if (ext==='pdf') { document.mimetype='application/pdf'; }
else if (ext==='xlsx') { document.mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; }
else if (ext==='xls') { document.mimetype='application/vnd.ms-excel'; }
else if (ext==='docx') { document.mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document'; }
else if (ext==='doc') { document.mimetype='application/msword'; }
else if (ext==='pptx') { document.mimetype='application/vnd.openxmlformats-officedocument.presentationml.presentation'; }
else if (ext==='ppt') { document.mimetype='application/vnd.ms-powerpoint'; }
else if (ext==='csv') { document.mimetype='text/csv'; }
else if (ext==='zip') { document.mimetype='application/zip'; }
else {
var mailContent = document.content;
if ((mailContent.match(/(<([^>]+)>)/gi) || []).length > 5) { document.mimetype='text/html'; }
}
// URLエンコード文字を全角に置換
var replaced = fullName
.replace(/%3a/g,':').replace(/%2f/g,'/').replace(/%3f/g,'?')
.replace(/%7c/g,'|').replace(/%22/g,'"').replace(/%2a/g,'*')
.replace(/%3c/g,'<').replace(/%3e/g,'>');
document.properties['cm:name'] = stringDate + replaced;
document.save();

mailFolderCreate.js — メールフォルダ作成

mailFolderCreate.js
var parentNode = companyhome.childByNamePath('Shared/mail');
var mailFrom = document.properties['cm:originator'];
if (mailFrom != null) {
var fullName = document.properties['cm:name'];
var fileName = fullName.split('.')[0];
var titleFolder = space.childByNamePath(fileName);
if (fullName.indexOf('.') == -1 && titleFolder.isDocument
|| titleFolder == null && space.hasPermission('CreateChildren')) {
titleFolder = space.createFolder('⁑' + fileName);
var mailContent = new String(document.content)
.replace(/\r?\n/g,' ')
.replace(/[^0-9A-Za-z\u3041-\u9fff\uFF10-\uFF9F]/g, '');
titleFolder.properties['cm:description'] = mailContent.slice(0,60);
titleFolder.save();
var props = new Array(2);
props['cm:sentdate'] = document.properties['cm:sentdate'];
props['cm:originator'] = mailFrom;
titleFolder.addAspect('cm:emailed', props);
var CSprops = new Array(1);
CSprops['cs:publishdate'] = document.properties['cm:sentdate'];
titleFolder.addAspect('cs:published', CSprops);
}
parentNode.properties['cs:address'] = titleFolder;
parentNode.save();
}

mailFolderMove.js — メールをフォルダへ移動(バックグラウンド実行)

mailFolderMove.js
var parentNode = companyhome.childByNamePath('Shared/mail');
var nodeString = parentNode.properties['cs:address'];
var convNode = search.findNode(nodeString);
var props = new Array(2);
props['cs:publishdate'] = convNode.properties['cm:sentdate'];
props['cs:publisher'] = convNode.properties['cm:originator'];
document.addAspect('cs:published', props);
document.move(convNode);

mailSort.js — 振り分けスクリプト 最新版

件名にSPAMを含むメールは最優先でspamフォルダへ、自組織ドメインのメールは別サイトのフォルダへ直接移動します。それ以外は各振り分けフォルダの cs:address と照合して移動します。

移動後に setInheritsPermissions(true) を呼び出すことで、移動後のフォルダが再移動できなくなる問題(SHARED ACL問題)を防止しています。

ℹ️ SHARED ACL問題について:フォルダルールで別サイトのフォルダへ document.move() した後、そのフォルダを再度移動しようとすると「移動できませんでした」エラーが発生することがあります。setInheritsPermissions(true) を追加することでACLがDEFINING状態に再設定され、再移動が可能になります。
mailSort.js
// ① SPAMは「spam」フォルダへ(sa-learn学習用)
var fileName = document.properties['cm:name'];
if (fileName.indexOf("SPAM") != -1) {
var dest = document.parent.childByNamePath('spam');
document.move(dest);
}
else {
var mailFrom = document.properties['cm:originator'];
if (mailFrom != null) {
// ② 自組織ドメインは別サイトのフォルダへ直接移動
if (new String(mailFrom).toLowerCase().indexOf('@example.com') != -1) {
var teishutsu = search.findNode(
'workspace://SpacesStore/移動先フォルダのノードID');
if (teishutsu != null) {
document.move(teishutsu);
document.setInheritsPermissions(true); // SHARED ACL問題の対策
}
}
// ③ それ以外はcs:addressで振り分け先判定
else {
function getAdd(sortFolder) {
dest = document.parent.childByNamePath(sortFolder);
if (dest == null) return new RegExp('(?!)');
var adList = dest.properties['cs:address'];
if (adList == null) return new RegExp('(?!)');
var adListStr = new String(adList).trim();
if (adListStr==='' || adListStr==='null') return new RegExp('(?!)');
return new RegExp(adListStr.replace(/\r?\n/g, '|'));
}
if (getAdd('フォルダA').test(mailFrom)) { document.move(dest); }
else if (getAdd('フォルダB').test(mailFrom)) { document.move(dest); }
else if (getAdd('フォルダC').test(mailFrom)) { document.move(dest); }
else if (getAdd('その他').test(mailFrom)) { document.move(dest); }
// 必要に応じて else if を追加
}
}
}
⚠️ cs:address の容量上限に注意:各振り分けフォルダの cs:address は最大50,000文字(d:text型)です。上限に達するとルール実行時にエラーが大量ループしサーバー全体の負荷が急増します。定期的に整理してください。

mailSortAdd.js — アドレス登録スクリプト

mailSortAdd.js
var created = document.properties['cm:created'];
var modified = document.properties['cm:modified'];
// 作成日と変更日が異なる=手動移動の場合のみ続行
if (created != modified) {
var mailFrom = document.properties['cm:originator'];
if (mailFrom != null) {
var parentNode = document.parent;
var adList = parentNode.properties['cs:address'];
if (adList == null || new String(adList).trim()==='' || new String(adList).trim()==='null') {
parentNode.properties['cs:address'] = mailFrom;
parentNode.save();
} else {
var adListStr = new String(adList);
var regexp = new RegExp(adListStr.replace(/\r?\n/g, '|'));
if (!regexp.test(mailFrom)) {
parentNode.properties['cs:address'] = adListStr + '\n' + mailFrom;
parentNode.save();
}
}
}
}

8. カスタムモデルの設定

アスペクト名プロパティ用途
cs:publishedcs:publishdated:date発行日(ソート用)
cs:publisherd:text発行者
cs:maillistcs:addressd:text(maxLength=50000)振り分けアドレスリスト
⚠️ cs:addressd:text 型・maxLength=50000 で設定してください。上限を超えるとエラーが大量ループしサーバー全体の負荷が急増します。

モデルXML(cs:maillist アスペクト部分)

bunsho_model.xml(抜粋)
<aspect name="cs:maillist">
<title>メール一覧</title>
<properties>
<property name="cs:address">
<title>アドレスリスト</title>
<type>d:text</type>
<mandatory>false</mandatory>
<multiple>false</multiple>
<constraints>
<constraint name="cs:ADDRESS_LENGTH" type="LENGTH">
<parameter name="maxLength"><value>50000</value></parameter>
<parameter name="minLength"><value>0</value></parameter>
</constraint>
</constraints>
</property>
</properties>
</aspect>

振り分け先フォルダへのアスペクト設定

1
振り分け先フォルダを右クリック → 「アスペクトの管理」
2
「使用可能なアスペクト」から cs:maillist(メール一覧) を選択 → 「追加」
3
「変更を適用」 → 振り分け先フォルダ全てに繰り返す

9. Alfresco Share の設定(メールエイリアス)

メール受信先フォルダに Eメールエイリアスを設定します。

1
メール受信先フォルダを右クリック → 「アスペクトの管理」
2
「Eメールエイリアス(emailserver:aliasable)」を選択 → 「追加」→「変更を適用」
3
フォルダを右クリック → 「プロパティを編集」 → 「Eメールエイリアス」に送信先名を入力(例:mail
4
「保存」
設定箇所設定値(例)メールアドレス
Eメールエイリアスmailmail@alfresco.example.com
email.server.domainalfresco.example.com
postfix relayhost[alfresco.example.com]:1025Alfresco SMTPポートへ中継

10. spamフォルダの定期学習・自動整理

件名に ***SPAM*** が付いたメールを専用フォルダへ振り分け、定期的に SpamAssassin のベイジアンフィルタへ学習させたうえで自動整理します。

認証情報の外部ファイル管理

⚠️ 認証情報をスクリプト本体に直書きすると crontab や Git 履歴から漏洩するリスクがあります。必ず外部ファイルに分離してください。
/etc/alfresco/credentials
ALFRESCO_USER=admin
ALFRESCO_PASS=xxxxxxxx
ALFRESCO_HOST=http://localhost:8080
SPAM_FOLDER_ID=spamフォルダのノードID
CLEAR_SCRIPT_ID=clearAndDeleteSpam.jsのノードID
SONOTA_ID=その他フォルダのノードID
BATCH_SCRIPT_ID=deleteSpamBatch.jsのノードID
terminal(パーミッション設定)
$sudo chmod 640 /etc/alfresco/credentials
$sudo chown root:実行ユーザー名 /etc/alfresco/credentials
⚠️ ファイルのオーナーが root:root のままだと、cronを一般ユーザーで実行した際に Permission denied エラーになります。cronを実行するユーザーをグループオーナーに設定し、640 権限にしてください。

deleteSpamBatch.js — 蓄積SPAMを少しずつ削除

deleteSpamBatch.js
var BATCH_SIZE = 100;
var sonotaFolder = companyhome.childByNamePath('Shared/mail/その他');
var deletedCount = 0;
var children = sonotaFolder.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.isContainer) {
var folderName = new String(child.name);
if (folderName.indexOf('SPAM') != -1) {
child.remove();
deletedCount++;
if (deletedCount >= BATCH_SIZE) { break; }
}
}
}
logger.log('削除完了: ' + deletedCount + '件(上限: ' + BATCH_SIZE + '件)');

spam-batch-delete.sh — バッチ削除シェルスクリプト

/usr/local/bin/spam-batch-delete.sh
#!/bin/bash
source /etc/alfresco/credentials
LOGFILE="/var/log/spam-batch-delete.log"
echo "=== $(date) 実行 ===" >> "$LOGFILE"
# 残り件数を確認
REMAINING=$(curl -s -u "$ALFRESCO_USER:$ALFRESCO_PASS" \
"$ALFRESCO_HOST/alfresco/api/-default-/public/alfresco/versions/1/nodes/$SONOTA_ID/children?maxItems=1&where=(isFolder=true)" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['list']['pagination']['totalItems'])" 2>/dev/null)
echo "残り件数: $REMAINING" >> "$LOGFILE"
if [ "$REMAINING" = "0" ]; then
echo "削除完了。cronを無効化してください。" >> "$LOGFILE"
exit 0
fi
# バッチ削除実行
curl -s -u "$ALFRESCO_USER:$ALFRESCO_PASS" \
-X POST -H "Content-Type: application/json" \
"$ALFRESCO_HOST/alfresco/s/api/actionQueue?async=false" \
-d "{\"actionedUponNode\":\"workspace://SpacesStore/$SONOTA_ID\",\"actionDefinitionName\":\"script\",\"parameterValues\":{\"script-ref\":\"workspace://SpacesStore/$BATCH_SCRIPT_ID\"}}" \
>> "$LOGFILE" 2>&1

cron登録

ℹ️ タイムゾーンを日本時間(JST)に設定してからcronを登録してください。sudo timedatectl set-timezone Asia/Tokyo で設定できます。sudo crontab -e(root)と crontab -e(一般ユーザー)は別ファイルで管理されます。どのユーザーで登録したか必ず記録してください。
一般ユーザー crontab(crontab -e)
# getmail 5分毎
*/5 * * * * /usr/bin/getmail > /dev/null 2>&1
# 蓄積済みSPAMのバッチ削除(完了後にこの行を削除)
0 */2 * * * /usr/local/bin/spam-batch-delete.sh
ℹ️ /var/log/spam-batch-delete.log に「削除完了。cronを無効化してください。」と表示されたら、crontabから spam-batch-delete.sh の行を削除してください。

11. ISO-2022-JP メール文字化け対応

古いメールクライアントから送信されたISO-2022-JP(JIS)エンコードのメールはAlfrescoがUTF-8として処理するため文字化けします。spamc-wrapper.sh でAlfrescoへ配送前にUTF-8へ変換することで対処します。

11-1. ISO-2022-JP(JISエンコード)メールの文字化け対応

古いメールクライアントから送信されたISO-2022-JPエンコードのメールはAlfrescoがUTF-8として処理するため文字化けします。spamc-wrapper.sh でAlfrescoへ配送前にUTF-8へ変換することで対処します。

ℹ️ ISO-2022-JPメールはヘッダーに charset=iso-2022-jp または本文に \x1b$B(JISエスケープ)を含みます。//IGNORE オプションで不正バイトを無視して変換することで、変換失敗を防止します。

第6章の spamc-wrapper.sh にISO-2022-JP変換処理が含まれています(Alfrescoへの配送直前に自動変換)。変換ログは /tmp/spamc-wrapper.logCONVERTED(iso2022jp->utf8) として記録されます。

11-2. 既存ISO-2022-JPファイルの一括変換

サーバー移行前に蓄積済みのISO-2022-JPメールを一括変換するスクリプトです。REST API経由でコンテンツをダウンロード・変換・アップロードします。

/usr/local/bin/iso2022jp-convert.sh(抜粋)
#!/bin/bash
source /etc/alfresco/credentials
LOGFILE="/var/log/alfresco/iso2022jp-convert.log"
BATCH_SIZE=100
SKIP_COUNT=0 # 中断・再開時はここを変更
# ISO-2022-JPか判定して変換・アップロード
if grep -q $'\x1b\$B\|\x1b\$@' "$TMPFILE" || file "$TMPFILE" | grep -qi "iso-2022\|jis"; then
iconv -f ISO-2022-JP -t UTF-8//IGNORE "$TMPFILE" > "$CONVERTED_FILE" 2>/dev/null
if [ -s "$CONVERTED_FILE" ]; then
curl -s -u "$ALFRESCO_USER:$ALFRESCO_PASS" -X PUT \
-H "Content-Type: text/plain; charset=UTF-8" \
--data-binary @"$CONVERTED_FILE" \
"$ALFRESCO_HOST/.../nodes/$NODE_ID/content?majorVersion=false"
fi
fi
項目内容
対象mail配下のtext/plainファイル(約95,000件)
処理時間約3〜4時間(ISO-2022-JPは全体の約26%)
中断・再開ログの進捗を確認してSKIP_COUNTを変更して再実行
実行推奨時間業務時間外(screenコマンドで実行)
バージョン記録majorVersion=falseのためマイナーバージョンとして記録
⚠️ 実行前に必ずデータベースのバックアップを取得してください。sudo -u postgres pg_dump alfresco > /tmp/alfresco_backup_$(date +%Y%m%d).sql

12. トラブルシューティング

移動したフォルダが再移動できない(SHARED ACL問題)

  • 原因:フォルダルールで document.move() を実行後、ノードのACLがSHARED(type=2)状態になる
  • 症状:Share UIで「移動できませんでした」、REST APIで setFixedAcls: unexpected shared acl エラー
  • 新規メール:修正版 mailSort.jssetInheritsPermissions(true) で防止済み
  • 既存ノード(数件):ゴミ箱経由での復旧が安全(下記参照)
ℹ️ ゴミ箱経由での復旧手順:(1) Share UIで対象フォルダをゴミ箱へ移動 → (2) http://サーバーIP:8080/share/page/user/admin/user-trashcan にアクセス → (3) [復元] をクリック → (4) 復元されたフォルダを目的のフォルダへ移動

添付ファイルのMIMEタイプ誤設定

  • 原因:メール取り込み時に添付ファイルのMIMEタイプが text/html に誤設定される
  • 新規メール:修正版 mailRename.js で自動的に正しいMIMEタイプが設定される
  • 既存ファイル:fixMime.js をData Dictionary/Scriptsに登録してREST API経由で実行

メールループ(too many hops)

🚨 postfix の content_filter = spamassassin を設定するとメールループが発生します。必ずコメントアウトしてください。

振り分けアドレス上限超過

  • 症状:IntegrityException: String length is not in range [0; 50,000] が大量ループしサーバーの応答が低下する
  • 対処:対象フォルダの cs:address を REST API で null にクリアし必要なアドレスのみ再登録する

cronが動かない(認証エラー)

  • 原因:/etc/alfresco/credentials のパーミッションが root:root 600 のままでcron実行ユーザーが読めない
  • 対処:sudo chown root:実行ユーザー名 /etc/alfresco/credentials && sudo chmod 640 /etc/alfresco/credentials

cronが動かない(ログファイルへの書き込みエラー)

  • 原因:/var/log/spam-batch-delete.log のオーナーが root:root でcron実行ユーザーが書き込めない
  • 対処:sudo chown 実行ユーザー名:実行ユーザー名 /var/log/spam-batch-delete.log

ログ確認コマンド

terminal
$sudo tail -50 /var/log/mail.log | grep -E "status=|error|bounce"
$cat /tmp/spamc-wrapper.log | tail -30
$sudo tail -100 /var/log/alfresco/alfresco.log | grep -i "error\|permission\|move"
$tail -10 /var/log/spam-batch-delete.log

設定確認チェックリスト

  • postfix の content_filter がコメントアウトされている
  • spamc-wrapper.sh に実行権限がある(chmod +x)
  • getmailrc の destination が spamc-wrapper.sh を参照している
  • SpamAssassin(spamd)が起動している
  • /etc/default/spamd の OPTIONS に --siteconfigpath が設定されている
  • 振り分けフォルダに cs:maillist アスペクトが適用されている
  • /etc/alfresco/credentials のパーミッションが root:実行ユーザー 640 になっている
  • /var/log/spam-batch-delete.log のオーナーが実行ユーザーになっている
  • alfresco-global.properties の alfresco.host にサーバーIPが設定されている
  • spamc-wrapper.sh にISO-2022-JP → UTF-8変換処理が含まれている
正常に設定できると、受信メールが自動的にスパム判定・振り分けされてAlfrescoに格納されます。

Alfresco Community 26.1 / Ubuntu 24.04 / Ansible インストール環境での設定例です。