nutools/lib/ulib/ldif

1440 lines
38 KiB
Plaintext
Raw Permalink Normal View History

2013-08-27 15:14:44 +04:00
##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# outils pour gérer des fichiers .ldif
##@cooked nocomments
##@require base
##@require awk
uprovide ldif
urequire base awk
function def_match_attr() {
local attr first script
script="function fix_attr(attr) {
gsub(/:+$/, \"\", attr)
return attr \":\"
}
function norm_attr(attr) {
2015-04-16 12:36:00 +04:00
return tolower(attr)
2013-08-27 15:14:44 +04:00
}
function match_attr(attr) {
attr = tolower(attr)
if ("
if [ -n "$*" ]; then
first=1
for attr in "$@"; do
attr="${attr%%:*}"
if [ -n "$first" ]; then
first=
else
script="$script || "
fi
script="${script}attr == tolower(\"$attr:\") || attr == tolower(\"$attr::\")"
done
else
script="${script}1"
fi
script="$script) return 1
else return 0
}"
echo "$script"
}
function def_match_value() {
local pattern first script
script="function match_value(line) {
sub(/^[^:]+::?[ ]+/, \"\", line)
if ("
if [ -n "$*" ]; then
first=1
for pattern in "$@"; do
pattern="${pattern//\\\\/\\\\}"
if [ -n "$first" ]; then
first=
else
script="$script || "
fi
script="${script}line ~ /$pattern/"
done
else
script="${script}1"
fi
script="$script) return 1
else return 0
}"
echo "$script"
}
function uncut_lines() {
# reformer les lignes qui sont coupées
awk '
BEGIN {
inline = 0
}
{
if (!inline) {
line = $0
inline = 1
} else {
if ($0 ~ /^ /) {
line = line substr($0, 2)
} else {
print line
inline = 0
line = $0
inline = 1
}
}
}
END {
2015-04-16 12:36:00 +04:00
if (inline) print line
2013-08-27 15:14:44 +04:00
}
'
}
function cut_lines() {
# couper les lignes trop longues
awk '
{
if (length($0) > 78) {
prefix = ""
maxlen = 78
line = $0
while (length(line) > maxlen) {
print prefix substr(line, 1, maxlen)
line = substr(line, maxlen + 1)
prefix = " "
maxlen = 77
}
if (line != "") print prefix line
} else {
print
}
}
'
}
function ensure_complete_objects() {
# S'assurer que le ldif ne contient que des objets complets (éliminant ainsi
# les groupes ayant seulement dn:)
awk '
function dump_ldif() {
if (attrs) {
print ldif
print ""
}
ldif = ""
attrs = 0
}
/^dn:/ {
dump_ldif()
ldif = $0
next
}
$0 != "" {
ldif = ldif "\n" $0
attrs = 1
}
END {
dump_ldif()
}
'
}
function delete_marked_objects() {
# Supprimer les objets marqués avec --DELETE--:
awk '
BEGIN { keep = 0 }
function dump_ldif() {
if (keep) {
print ldif
print ""
}
ldif = ""
keep = 1
}
/^dn:/ {
dump_ldif()
ldif = $0
next
}
/^--DELETE--:/ { keep = 0 }
$0 != "" { ldif = ldif "\n" $0 }
END { dump_ldif() }
'
}
function tl_addattr() {
awk '
/^dn:/ {
2015-04-16 12:36:00 +04:00
lastattr = ""
dn = $0
next
2013-08-27 15:14:44 +04:00
}
{
2015-04-16 12:36:00 +04:00
attr = $1
if (attr != "") {
if (dn != "") {
print dn
print "changetype: add"
dn = ""
2013-08-27 15:14:44 +04:00
}
2015-04-16 12:36:00 +04:00
sub(/:+$/, "", attr)
}
print
lastattr = attr
2013-08-27 15:14:44 +04:00
}
'
}
function tl_modifyattr() {
local modtype="$1"
awkrun modtype="$modtype" '
/^dn:/ {
2015-04-16 12:36:00 +04:00
lastattr = ""
dn = $0
next
2013-08-27 15:14:44 +04:00
}
{
2015-04-16 12:36:00 +04:00
attr = $1
if (attr == "") {
if (lastattr != "") {
print "-"
2013-08-27 15:14:44 +04:00
}
2015-04-16 12:36:00 +04:00
} else {
if (dn != "") {
print dn
print "changetype: modify"
dn = ""
}
sub(/:+$/, "", attr)
if (lastattr != attr) {
if (lastattr != "") {
print "-"
}
print modtype ": " attr
}
}
print
lastattr = attr
2013-08-27 15:14:44 +04:00
}
'
}
function tl_deleteattr() {
awk '
/^dn:/ {
2015-04-16 12:36:00 +04:00
lastattr = ""
dn = $0
next
2013-08-27 15:14:44 +04:00
}
{
2015-04-16 12:36:00 +04:00
attr = $1
if (attr == "") {
if (lastattr != "") {
print "-"
print
}
} else {
if (dn != "") {
print dn
print "changetype: modify"
dn = ""
2013-08-27 15:14:44 +04:00
}
2015-04-16 12:36:00 +04:00
sub(/:+$/, "", attr)
if (lastattr != attr) {
if (lastattr != "") {
print "-"
}
print "delete: " attr
}
}
lastattr = attr
2013-08-27 15:14:44 +04:00
}
'
}
function tl_deleteentry() {
awk '
/^dn:/ {
2015-04-16 12:36:00 +04:00
dn = $0
print dn
print "changetype: delete"
print ""
next
2013-08-27 15:14:44 +04:00
}
'
}
function tl_modrdn() {
local newrdn newsup keep
local suffix="$(strlower "$3")"
local dn="$(strlower "$1")"
if [ "${1%,}" != "$1" ]; then
# ignorer le suffixe éventuellement fourni
set -- "${1%,}" "$2"
elif [ -n "$suffix" -a "${dn%,$suffix}" == "$dn" ]; then
# rajouter le suffixe
set -- "$1,$3" "$2"
fi
splitfsep "$1" , newrdn newsup
case "$2" in k|keep|keepold|keepoldrdn) keep=1;; esac
awkrun newrdn:str="$newrdn" newsup:str="$newsup" keep:int="$keep" '
/^dn:/ {
actual_newrdn = newrdn
if (actual_newrdn == "") {
actual_newrdn = $2
gsub(/,.*$/, "", actual_newrdn)
}
print
print "changetype: modrdn"
print "newrdn: " actual_newrdn
if (keep) print "deleteoldrdn: 0"
else print "deleteoldrdn: 1"
if (newsup != "") print "newsuperior: " newsup
print ""
next
}
'
}
2014-09-16 08:26:27 +04:00
function tl_touchentry() {
awk '
/^dn:/ {
2015-04-16 12:36:00 +04:00
dn = $0
print dn
print "changetype: modify"
print ""
next
2014-09-16 08:26:27 +04:00
}
'
}
2013-08-27 15:14:44 +04:00
function tl_keepattr() {
local match_attr="$1"
awk "$match_attr"'
$0 == "" || match_attr($1) {
2015-04-16 12:36:00 +04:00
print
2013-08-27 15:14:44 +04:00
}
'
}
function tl_keepval() {
local match_attr="$1" match_value="$2"
awk "$match_attr
$match_value"'
match_attr($1) {
if (match_value($0)) {
print
}
next
}
{ print }
'
}
function tl_excludeattr() {
local match_attr="$1"
awk "$match_attr"'
! match_attr($1) {
print
}
'
}
function tl_excludeval() {
local match_attr="$1" match_value="$2"
awk "$match_attr
$match_value"'
match_attr($1) {
if (! match_value($0)) {
print
}
next
}
{ print }
'
}
function tl_keepvalentry() {
local match_attr="$1" match_value="$2"
awk "$match_attr
$match_value"'
function keep_maybe() {
if (! keep) print "--DELETE--: yes"
done = 1
}
/^dn:/ {
keep = 0
done = 0
}
match_attr($1) {
if (match_value($0)) keep = 1
}
$0 == "" { keep_maybe() }
{ print }
END {
if (! done) keep_maybe()
}
'
}
function tl_excludevalentry() {
local match_attr="$1" match_value="$2"
awk "$match_attr
$match_value"'
function keep_maybe() {
if (! keep) print "--DELETE--: yes"
done = 1
}
/^dn:/ {
keep = 1
done = 0
}
match_attr($1) {
if (match_value($0)) keep = 0
}
$0 == "" { keep_maybe() }
{ print }
END {
if (! done) keep_maybe()
}
'
}
function tl_replval() {
local script="$1"; shift
script="$script
function print_values() {
for (i = 0; i < count; i++) {
print attr \" \" values[i]
}
}
BEGIN {
count=$#"
local i value
let i=0
for value in "$@"; do
script="$script
values[$i] = $(quoted_awk "$value")"
let i=$i+1
done
script="$script
}"'
match_attr($1) {
attr = fix_attr($1)
while (match_attr($1)) {
if (getline <= 0) break
}
print_values()
}
{ print }
'
awkrun value="$value" "$script"
}
function tl_addval() {
local attr="$1"; shift
local script="$(def_match_attr "$attr")
function print_values() {
for (i = 0; i < count; i++) {
print attr \" \" values[i]
}
add = 0
}
BEGIN {
add=0
attr=fix_attr($(quoted_awk "$attr"))
count=$#"
local i value
let i=0
for value in "$@"; do
script="$script
values[$i] = $(quoted_awk "$value")"
let i=$i+1
done
script="$script
}"'
/^dn:/ {
add = 1
}
add && match_attr($1) {
while (match_attr($1)) {
print
status = getline
if (status == 0) {
# traiter EOF de façon particulière, pour ne pas dupliquer le dernier print
print_values()
next
} else if (status < 0) {
break
}
}
print_values()
}
add && $0 == "" {
2015-04-16 12:36:00 +04:00
print_values()
2013-08-27 15:14:44 +04:00
}
{ print }
END {
2015-04-16 12:36:00 +04:00
if (add) print_values()
2013-08-27 15:14:44 +04:00
}
'
awk "$script"
}
function tl_defval() {
local attr="$1"; shift
local script="$(def_match_attr "$attr")
function resetp() {
add = 0
found = 0
}
function print_values() {
for (i = 0; i < count; i++) {
print attr \" \" values[i]
}
resetp()
}
BEGIN {
add = 0
found = 0
attr = fix_attr($(quoted_awk "$attr"))
count = $#"
local i value
let i=0
for value in "$@"; do
script="$script
values[$i] = $(quoted_awk "$value")"
let i=$i+1
done
script="$script
}"'
/^dn:/ {
add = 1
}
add && match_attr($1) {
found = 1
}
add && $0 == "" {
if (!found) print_values()
resetp()
}
{ print }
END {
2015-04-16 12:36:00 +04:00
if (add && !found) print_values()
}
'
awk "$script"
}
function tl_ensureval() {
local attr="$1"; shift
local script="$(def_match_attr "$attr")
2015-04-16 13:41:40 +04:00
BEGIN {
resetp()
attr = fix_attr($(quoted_awk "$attr"))
ensure_count = $#"
local i value
let i=0
for value in "$@"; do
script="$script
ensure_values[$i] = $(quoted_awk "$value")"
let i=$i+1
done
script="$script
found_count = 0
found_values[0] = 0
delete found_values
}"'
function resetp() {
ensure = 0
found = 0
}
function get_attrvalue(line) {
2015-04-16 13:41:40 +04:00
sub(/^[^:]+::?[ ]+/, "", line)
return line
}
function add_to_found_values(value) {
found_values[found_count] = value
found_count++
}
2015-04-16 13:41:40 +04:00
function in_found_values(value, i, found_value) {
if (value ~ /^{}/) {
sub(/^{}/, "", value)
for (i = 0; i < found_count; i++) {
found_value = found_values[i]
sub(/^{[0-9]+}/, "", found_value)
if (found_value == value) {
return 1
}
}
} else {
for (i = 0; i < found_count; i++) {
if (found_values[i] == value) {
return 1
}
}
}
return 0
}
function print_ensure_values() {
for (i = 0; i < ensure_count; i++) {
2015-04-16 13:41:40 +04:00
value = ensure_values[i]
sub(/^{}/, "", value)
print attr " " value
}
resetp()
}
function print_missing_values( value) {
for (i = 0; i < ensure_count; i++) {
value = ensure_values[i]
if (!in_found_values(value)) {
2015-04-16 13:41:40 +04:00
sub(/^{}/, "", value)
print attr " " value
}
}
resetp()
}
/^dn:/ {
ensure = 1
}
ensure && match_attr($1) {
found = 1
value = get_attrvalue($0)
add_to_found_values(value)
next
}
ensure && $0 == "" {
if (found) print_missing_values()
else print_ensure_values()
}
{ print }
END {
if (ensure) {
if (found) print_missing_values()
else print_ensure_values()
}
}
'
awk "$script"
}
2013-08-27 15:14:44 +04:00
function tl_decode() {
local match_attr="$1"
awkrun -f "$match_attr"'
function split_base64(text, result) {
2015-04-16 12:36:00 +04:00
result = ""
while (text != "") {
if (result != "") result = result "\n"
result = result substr(text, 1, 64)
text = substr(text, 65)
}
return result
2013-08-27 15:14:44 +04:00
}
/^[^:]+:: / && match_attr($1) {
2015-04-16 12:36:00 +04:00
name = $0; sub(/::.*$/, "", name)
value = $0; sub(/^[^:]+:: /, "", value)
decoded_value = b64decode(value)
#cmd = "echo '\''" split_base64(value) "'\'' | openssl base64 -d"
#decoded_value = ""
#while ((cmd | getline line) > 0) {
# decoded_value = decoded_value line
#}
#close(cmd)
print name ": " decoded_value
next
2013-08-27 15:14:44 +04:00
}
{ print }
'
}
function tl_encode() {
local match_attr="$1"
awkrun "$match_attr"'
function quote(text) {
2015-04-16 12:36:00 +04:00
# remplacer les quotes par le caractere echappement approprie
gsub('"/'/, \"'\\\\''\""', text)
return text
2013-08-27 15:14:44 +04:00
}
/^[^:]+: / && match_attr($1) {
2015-04-16 12:36:00 +04:00
name = $0; sub(/:.*$/, "", name)
value = $0; sub(/^[^:]+: /, "", value)
2013-08-27 15:14:44 +04:00
2015-04-16 12:36:00 +04:00
cmd = "echo '\''" quote(value) "'\'' | openssl base64"
coded_value = ""
while ((cmd | getline line) > 0) {
coded_value = coded_value line
}
close(cmd)
print name ":: " coded_value
next
2013-08-27 15:14:44 +04:00
}
{ print }
'
}
2015-02-09 12:16:54 +04:00
__AWK_VSEP_FUNCTIONS='
function quote_vsep(value, vsep, hvsep) {
gsub(/\\/, "\\5c", value)
if (vsep != "\\") {
hvsep = "\\" hex(ord(vsep))
gsub(quote_regexp(vsep), hvsep, value)
}
return value
}
function unquote_vsep(value, vsep, hvsep) {
if (vsep != "\\") {
hvsep = "\\" hex(ord(vsep))
gsub(quote_regexp(hvsep), vsep, value)
}
gsub(quote_regexp("\\5c"), "\\", value)
return value
}
'
2013-08-27 15:14:44 +04:00
function tl_format() {
local args show_headers asep vsep qsep qempty escape shell cmd begincmd endcmd localvars
2013-08-27 15:14:44 +04:00
show_headers=
asep=$'\t'
vsep=';'
qsep=
if parse_opts \
-h,--show-headers show_headers \
-F: asep= \
-R: vsep= \
--quote: qsep= \
--quote-empty qempty=::EMPTY:: \
--empty-value: qempty= \
2013-08-27 15:14:44 +04:00
--escape: escape \
-e shell \
-c: cmd= \
--bc: begincmd= \
--ec: endcmd= \
-l,--local localvars=1 \
@ args -- "$@"; then
set -- "${args[@]}"
else
eerror "$args"
return 1
fi
[ -n "$shell" ] && show_headers=1
local -a attrs mapattrs
local attr mapattr
2013-08-27 15:14:44 +04:00
for attr in "$@"; do
splitpair "$attr" mapattr attr
[ -n "$attr" ] || attr="$mapattr"
attr="${attr//:/}"; array_add attrs "$attr"
mapattr="${mapattr//:/}"; array_add mapattrs "$mapattr"
2013-08-27 15:14:44 +04:00
done
local match_attr="$(def_match_attr "${attrs[@]}")"
2013-08-27 15:14:44 +04:00
awkrun -f show_headers:int="$show_headers" asep="$asep" vsep="$vsep" qsep="$qsep" qempty="$qempty" escape="$escape" \
shell:int="$shell" cmd="$cmd" begincmd="$begincmd" endcmd="$endcmd" localvars:int="$localvars" \
2015-02-09 12:16:54 +04:00
attrs[@] mapattrs[@] "$__AWK_VSEP_FUNCTIONS"'
2013-08-27 15:14:44 +04:00
function reset_values() {
for (i = 1; i <= attrs_count; i++) {
2013-08-27 15:14:44 +04:00
attr = norm_attr(attrs[i])
values[attr] = ""
}
has_values = 0
}
function quote_shell(value) {
gsub(/'\''/, "'\'\\\\\''", value)
return "'\''" value "'\''"
}
function dump_headers() {
line = ""
if (shell) {
if (localvars) {
print "local attributes index"
for (i = 1; i <= attrs_count; i++) {
attr = norm_attr(mapattrs[i])
2013-08-27 15:14:44 +04:00
print "local " attr
}
}
line = line "attributes=("
for (i = 1; i <= attrs_count; i++) {
attr = norm_attr(mapattrs[i])
2013-08-27 15:14:44 +04:00
if (i > 1) line = line " "
line = line quote_shell(attr)
}
line = line ")"
} else {
for (i = 1; i <= attrs_count; i++) {
attr = mapattrs[i]
2013-08-27 15:14:44 +04:00
if (i > 1) line = line asep
line = line attr
}
}
if (line != "") print line
}
function dump_cmd(atbegin, atend) {
if (atbegin && begincmd != "") print begincmd
if (atend && endcmd != "") print endcmd
}
function __should_quote(s) {
if (s ~ /^[[:blank:][:cntrl:][:space:]]/) return 1
if (s ~ /[[:blank:][:cntrl:][:space:]]$/) return 1
return 0
}
function dump_values(atbegin, value) {
if (shell) {
print "index=" nr
for (i = 1; i <= attrs_count; i++) {
2013-08-27 15:14:44 +04:00
attr = norm_attr(attrs[i])
mapattr = norm_attr(mapattrs[i])
print mapattr "=" values[attr]
2013-08-27 15:14:44 +04:00
}
if (!atbegin && cmd != "") print cmd
} else {
line = ""
for (i = 1; i <= attrs_count; i++) {
2013-08-27 15:14:44 +04:00
attr = norm_attr(attrs[i])
if (i > 1) line = line asep
value = values[attr]
if (qsep != "" && index(value, qsep) != 0) {
if (escape) gsub(qsep, quote_subrepl(escape) "&", value);
else gsub(qsep, "&&", value);
}
if (qsep != "" && (index(value, qsep) != 0 || index(value, asep) != 0 || __should_quote(value))) {
line = line qsep value qsep
} else {
line = line value
}
}
if (line != "") print line
}
nr = nr + 1
}
function parse_attr(line) {
sub(/::?.*$/, "", line)
return line
}
function parse_value(line) {
sub(/^[^:]+::? */, "", line)
2013-08-27 15:14:44 +04:00
return line
}
'"$match_attr"'
BEGIN {
if (show_headers) dump_headers()
reset_values()
nr = -1
if (shell) dump_values(1)
dump_cmd(1, 0)
}
$0 != "" && match_attr($1) {
has_values = 1
attr = norm_attr(parse_attr($0))
value = parse_value($0)
if (shell) {
if (values[attr] == "") {
values[attr] = quote_shell(value)
} else {
tmp = values[attr]
if (substr(tmp, 1, 1) == "(") tmp = substr(tmp, 2, length(tmp) - 2)
tmp = tmp " " quote_shell(value)
values[attr] = "(" tmp ")"
}
} else {
2015-02-25 11:45:38 +04:00
if (value == "") value = qempty
value = quote_vsep(value, vsep)
2013-08-27 15:14:44 +04:00
if (values[attr] == "") {
2015-02-25 11:45:38 +04:00
values[attr] = value
2013-08-27 15:14:44 +04:00
} else {
2015-02-25 11:45:38 +04:00
values[attr] = values[attr] vsep value
2013-08-27 15:14:44 +04:00
}
}
}
$0 == "" {
if (has_values) dump_values()
reset_values()
}
END {
if (has_values) dump_values()
if (shell) dump_cmd(0, 1)
}
'
}
function tl_formatcsv() {
local args show_headers vsep qsep escape
show_headers=1
vsep=';'
qsep='"'
qempty=1
2013-08-27 15:14:44 +04:00
escape=
if parse_opts \
-h,--show-headers show_headers=1 \
-n,--no-headers show_headers= \
-R: vsep= \
--quote: qsep= \
--quote-empty qempty=1 \
--no-quote-empty qempty= \
2013-08-27 15:14:44 +04:00
--escape: escape \
@ args -- "$@"; then
set -- "${args[@]}"
else
eerror "$args"
return 1
fi
tl_format ${show_headers:+--show-headers} -F , -R "$vsep" --quote "$qsep" ${qempty:+--quote-empty} --escape "$escape" "$@"
2013-08-27 15:14:44 +04:00
}
function tl_parsecsv() {
local -a args headers
local basedn= rdnattr= skip_lines=0 parse_headers= vsep=';' qempty=::EMPTY::
2013-08-27 15:14:44 +04:00
if parse_opts \
-u:,--rdnattr: rdnattr= \
-s:,--skip-lines: skip_lines= \
-h,--parse-headers parse_headers=1 \
-R: vsep= \
--no-parse-empty qempty= \
--parse-empty qempty=::EMPTY:: \
--empty-value: qempty= \
2013-08-27 15:14:44 +04:00
@ args -- "$@"; then
set -- "${args[@]}"
else
eerror "$args"
return 1
fi
basedn="$1"; shift
2014-07-04 18:43:19 +04:00
if [ -n "$basedn" ]; then
[ -n "$rdnattr" ] || rdnattr=uid
else
[ -n "$rdnattr" ] || rdnattr=dn
fi
2013-08-27 15:14:44 +04:00
[ -n "$*" ] && headers=("$@")
[ -n "${headers[*]}" ] || parse_headers=1
awkrun -f \
basedn="$basedn" rdnattr="$rdnattr" \
skip_lines:int="$skip_lines" parse_headers:int="$parse_headers" headers[@] \
vsep="$vsep" qempty="$qempty" "$__AWK_VSEP_FUNCTIONS"'
2013-08-27 15:14:44 +04:00
NR <= skip_lines { next }
parse_headers {
array_parsecsv(headers, $0)
headers_count = array_len(headers)
2013-08-27 15:14:44 +04:00
parse_headers = 0
next
}
{
array_parsecsv(attrvalues, $0)
2013-08-27 15:14:44 +04:00
rdnindex = key_index(rdnattr, headers)
rdnvalue = attrvalues[rdnindex]
2014-07-04 18:43:19 +04:00
if (basedn != "") {
print "dn: " rdnattr "=" rdnvalue "," basedn
} else if (rdnattr != "dn") {
print "dn: " rdnattr "=" rdnvalue
2014-07-04 18:43:19 +04:00
} else {
print "dn: " rdnvalue
}
for (i = 1; i <= headers_count; i++) {
2013-08-27 15:14:44 +04:00
attr = headers[i]
if (basedn == "" && rdnattr == "dn" && attr == rdnattr) {
2014-07-04 18:43:19 +04:00
continue # ne pas écrire deux fois le même attribut
}
attrvalue = attrvalues[i]
split(attrvalue, values, vsep)
values_count = array_len(values)
for (j = 1; j <= values_count; j++) {
value = unquote_vsep(values[j], vsep)
if (value == qempty) value = ""
print attr ": " value
2013-08-27 15:14:44 +04:00
}
}
print ""
}
'
}
function tl_parsecsvmod() {
local -a args headers
local basedn= rdnattr= skip_lines=0 parse_headers= vsep=';' qempty=::EMPTY::
2013-08-27 15:14:44 +04:00
if parse_opts \
-u:,--rdnattr: rdnattr= \
-s:,--skip-lines: skip_lines= \
-h,--parse-headers parse_headers=1 \
-R: vsep= \
--no-parse-empty qempty= \
--parse-empty qempty=::EMPTY:: \
--empty-value: qempty= \
2013-08-27 15:14:44 +04:00
@ args -- "$@"; then
set -- "${args[@]}"
else
eerror "$args"
return 1
fi
basedn="$1"; shift
2014-07-04 18:43:19 +04:00
if [ -n "$basedn" ]; then
[ -n "$rdnattr" ] || rdnattr=uid
else
[ -n "$rdnattr" ] || rdnattr=dn
fi
2013-08-27 15:14:44 +04:00
[ -n "$*" ] && headers=("$@")
[ -n "${headers[*]}" ] || parse_headers=1
awkrun -f \
basedn="$basedn" rdnattr="$rdnattr" \
skip_lines:int="$skip_lines" parse_headers:int="$parse_headers" headers[@] \
vsep="$vsep" qempty="$qempty" "$__AWK_VSEP_FUNCTIONS"'
2013-08-27 15:14:44 +04:00
NR <= skip_lines { next }
parse_headers {
array_parsecsv(headers, $0)
headers_count = array_len(headers)
2013-08-27 15:14:44 +04:00
parse_headers = 0
next
}
{
array_parsecsv(attrvalues, $0)
rdnindex = key_index(rdnattr, headers)
rdnvalue = attrvalues[rdnindex]
2014-07-04 18:43:19 +04:00
# déterminer d"abord s"il faut écrire l"objet
printobj = 0
for (i = 1; i <= headers_count; i++) {
2013-08-27 15:14:44 +04:00
attr = headers[i]
if (attr == rdnattr) continue
attrvalue = attrvalues[i]
2014-07-04 18:43:19 +04:00
if (attrvalue == "") continue
# ok on doit ecrire le dn
if (basedn != "") {
print "dn: " rdnattr "=" rdnvalue "," basedn
2013-08-27 15:14:44 +04:00
} else {
2014-07-04 18:43:19 +04:00
print "dn: " rdnvalue
2013-08-27 15:14:44 +04:00
}
2014-07-04 18:43:19 +04:00
print "changetype: modify"
printobj = 1
break
}
# maintenant, écrire l"objet le cas échéant
if (printobj) {
for (i = 1; i <= headers_count; i++) {
2014-07-04 18:43:19 +04:00
attr = headers[i]
if (attr == rdnattr) continue
attrvalue = attrvalues[i]
if (attrvalue == "") {
continue
} else if (attrvalue ~ /^(replace|add|delete):/) {
changetype = attrvalue
sub(/:.*/, "", changetype)
attrvalue = substr(attrvalue, length(changetype) + 2)
} else {
changetype = "replace"
}
print changetype ": " attr
split(attrvalue, values, vsep)
values_count = array_len(values)
for (j = 1; j <= values_count; j++) {
value = unquote_vsep(values[j], vsep)
if (value == qempty) value = ""
print attr ": " value
2014-07-04 18:43:19 +04:00
}
print "-"
2013-08-27 15:14:44 +04:00
}
2014-07-04 18:43:19 +04:00
print ""
2013-08-27 15:14:44 +04:00
}
}
'
}
TRANSFORM_CMD_HELP="\
Il est possible de spécifier plusieurs commandes séparées par //
Les expressions régulières sont celles reconnues par awk.
u, uncut
Fusionner les lignes coupées. Cette action est en principe effectuée
automatiquement au début, sauf avec l'option --nu
c, cut
Couper les lignes trop longues. Il n'est pas conseillé d'appliquer des
méthodes de transformation après avoir utilisé cette action.
dec, decode [ATTRS...]
2013-08-27 15:14:44 +04:00
Décoder les valeurs des attributs mentionnés si nécessaire (c'est à dire
s'ils sont encodés en base64)
enc, encode [ATTRS...]
2013-08-27 15:14:44 +04:00
Encoder en base64 les valeurs des attributs mentionnés.
xe, xempty, excludeempty
Supprimer les objets sans attributs, qui ont uniquement une ligne dn:
k, keepattr [--no-xempty] ATTRS...
Garder uniquement les attributs mentionnés, puis supprimer les objets sans
attributs avec xempty, sauf si l'option --no-xempty est spécifiée (en
d'autres termes, keepattr sans argument supprime *tout* le flux)
kv, keepval [--no-xempty] ATTR PATTERNS...
2013-08-27 15:14:44 +04:00
Pour l'attribut attr, garder uniquement les lignes pour lesquelles les
valeurs correspondent aux expressions régulières. Les autres attributs ne
sont pas modifiés. Ensuite, supprimer les objets sans attributs avec xempty,
sauf si l'option --no-xempty est spécifiée
x, excludeattr [--no-xempty] ATTRS...
2013-08-27 15:14:44 +04:00
Supprimer les lignes des attributs mentionnés. Ensuite, supprimer les objets
sans attributs avec xempty, sauf si l'option --no-xempty est spécifiée
xv, excludeval [--no-xempty] ATTR PATTERNS...
2013-08-27 15:14:44 +04:00
Pour l'attribut attr, supprimer les lignes pour lesquelles les valeurs
correspondent aux expressions régulières. Les autres attributs ne sont pas
modifiés. Ensuite, supprimer les objets sans attributs avec xempty, sauf si
l'option --no-xempty est spécifiée.
kve, keepvalentry ATTR PATTERNS...
2013-08-27 15:14:44 +04:00
Pour l'attribut attr, vérifier si *au moins une* valeur correspond à l'une
des expressions régulières. Si c'est le cas, garder toute l'entrée, sinon
supprimer toute l'entrée.
xve, excludevalentry ATTR PATTERNS...
2013-08-27 15:14:44 +04:00
Pour l'attribut attr, vérifier si *aucune* des valeurs ne correspond à l'une
des expressions régulières. Si c'est le cas, garder toute l'entrée, sinon
supprimer toute l'entrée.
rv, replval ATTR VALUES...
2013-08-27 15:14:44 +04:00
Remplacer toutes les valeurs de l'attribut attr par les valeurs spécifiées.
av, addval ATTR VALUES...
2013-08-27 15:14:44 +04:00
Ajouter un nouvel attribut avec les valeurs spécifiées. Si l'attribut existe
déjà, les nouvelles valeurs sont ajoutées à la fin.
fv, defval ATTR VALUES...
Ajouter un nouvel attribut avec les valeurs spécifiées, s'il n'existe pas
déjà. Si l'attribut existe déjà, cette opération est un NOP.
ev, ensureval ATTR VALUES...
A utiliser avec modaddval, ne garder que les valeurs que l'attribut n'a pas
déjà. Par exemple, pour s'assurer que l'attribut ATTR a les valeurs V0 et
V1, on ferait:
ensureval ATTR V0 V1 // modaddval
2015-04-16 13:41:40 +04:00
Certains attributs sont ordonnés. Leurs valeurs sont étiquettées avec leur
index, e.g {2}value. Si l'on ne veut pas faire de correspondance exacte,
c'est à dire tester uniquement la présence d'une valeur indépendamment de
son index, il faut utiliser la syntaxe {}value, e.g:
ensureval ATTR {}value // modaddval
S'il faut ajouter les valeurs, elle sont mentionnées sans le préfixe {}
sed ARGS
awk ARGS
grep ARGS
sort [ARGS]
awkrun [ARGS]
2013-08-27 15:14:44 +04:00
Traiter le flux avec respectivement les commandes sed, awk, grep, sort et
awkrun. awkrun est la version 'améliorée' de awk offerte par nutools.
Note: ces commandes ne tiennent pas compte de la locale: elles sont lancées
avec la variable LANG=C
Note: aucun argument n'est filtré, mais il ne faudrait pas utiliser les
options qui provoquent la modification en place d'un fichier, comme par
exemple l'option -i de sed
format [OPTIONS] ATTRS...
2013-08-27 15:14:44 +04:00
Formater le flux au format ldif en données tabulaires.
Chaque attribut peut être de la forme newname:oldname pour avoir des
en-têtes avec un nom différent des noms des attributs. L'effet est le même
qu'avec awkcsv -m
2013-08-27 15:14:44 +04:00
-h, --show-headers
Afficher les en-têtes. Par défaut, les en-têtes ne sont pas affichés.
-F ASEP
Spécifier le séparateur pour les attributs. Par défaut, il s'agit du
caractère de tabulation.
-R VSEP
Spécifier le séparateur pour les valeurs des attributs. Par défaut, il
s'agit du point-virgule ';'
--quote QUOTE
Spécifier le caractère à employer pour encadrer une valeur si elle
contient l'un des caractères ASEP ou QSEP. Par défaut, les valeurs ne
sont pas encadrées.
--escape ESCAPE
Indiquer que le caractère QUOTE dans une chaine doit être mis en
échappement avec le caractère ESCAPE. Si ESCAPE est une valeur vide,
doubler le caractère QUOTE.
e.g. si QUOTE='\"' et ESCAPE='\\', la valeur abc\"def est écrite ainsi:
\"abc\\\"def\"
Par contre, si ESCAPE='', alors la valeur est écrite ainsi:
\"abc\"\"def\"
-e
Retourner les valeurs comme des variables shell. Les options -F et -R
sont ignorées. Les attributs multivalués sont écrits sous forme de
tableaux. Par exemple:
attributes=('mail' 'givenName')
index=0
mail='user@domain.fr'
givenName=('peter' 'gabriel')
-l, --local
Ajouter une définition pour rendre locales les variables utilisées,
e.g. avec l'exemple précédent:
local attributes index
local mail
local givenName
attributes=('mail' 'givenName')
index=0
mail='user@domain.fr'
givenName=('peter' 'gabriel')
--bc
Dans le mode -e, spécifier une commande à insérer avant le premier
enregistrement. Quand cette commande est lancée, index==-1
-c
Dans le mode -e, spécifier une commande à insérer après chaque
enregistrement
--ec
Dans le mode -e, spécifier une commande à insérer après le dernier
enregistrement
formatcsv [OPTIONS] ATTRS...
2013-08-27 15:14:44 +04:00
Equivalent à 'format -F , --show-headers --quote \\\"'
-n, --no-headers
Ne pas afficher les en-têtes. Par défaut, les en-têtes sont affichés.
-R VSEP
--quote QUOTE
--escape ESCAPE
Ces options ont la même signification que pour format
parsecsv [OPTIONS] BASEDN [ATTRS...]
2013-08-27 15:14:44 +04:00
Analyser le flux au format CSV (qui a par exemple été générée avec la
commande formatcsv), et reconstruire les objets LDAP associés.
En principe, BASEDN n'est pas vide, et l'option -u détermine l'attribut dont
la valeur est utilisée pour nommer les objets. Le cas particulier BASEDN==''
2014-07-04 18:43:19 +04:00
est aussi supporté, auquel cas la valeur par défaut de rdnattr n'est plus
'uid' mais 'dn'
BASEDN
2013-08-27 15:14:44 +04:00
DN de base des objets à construire
-u, --rdnattr rdnattr[=uid]
Nommer les objets avec la valeur de l'attribut spécifié
-s, --skip-lines nblines
Sauter nblines au début du flux
-h, --parse-headers
Lire la liste des attributs à partir de la première ligne non ignorée du
flux. Si la liste des attributs est vide, cette option est implicitement
activée.
-R VSEP
Spécifier le séparateur pour les valeurs des attributs. Par défaut, il
s'agit du point-virgule ';'
parsecsvmod [OPTIONS] BASEDN [ATTRS...]
2013-08-27 15:14:44 +04:00
Analyser le flux au format CSV qui contient des ordres de modification des
objets LDAP, et construire les modifications au format LDIF.
L'attribut rdnattr (spécifié avec l'option -u) est le seul dont la valeur
doit être au format standard. Les valeurs des autres attributs doivent être
de la forme [CHANGETYPE:]value où CHANGETYPE peut valoir replace, add, ou
delete. CHANGETYPE vaut replace par défaut. Si une valeur est vide, elle est
ignorée
En principe, BASEDN n'est pas vide, et l'option -u détermine l'attribut dont
2014-07-04 18:43:19 +04:00
la valeur est utilisée pour nommer les objets. Le cas particulier basedn==''
est aussi supporté, auquel cas la valeur par défaut de rdnattr n'est plus
'uid' mais 'dn'
BASEDN
2013-08-27 15:14:44 +04:00
DN de base des objets à construire
-u, --rdnattr rdnattr[=uid]
Nommer les objets avec la valeur de l'attribut spécifié
-s, --skip-lines nblines
Sauter nblines au début du flux
-h, --parse-headers
Lire la liste des attributs à partir de la première ligne non ignorée du
flux. Si la liste des attributs est vide, cette option est implicitement
activée.
-R VSEP
Spécifier le séparateur pour les valeurs des attributs. Par défaut, il
s'agit du point-virgule ';'
awkcsv [OPTIONS] [ATTRS...]
2013-08-27 15:14:44 +04:00
$(sed 's/^/ /g' <<<"$__AWKCSV_HELP")
mergecsv [OPTIONS] LEFT RIGHT -k FIELD
2013-08-27 15:14:44 +04:00
$(sed 's/^/ /g' <<<"$__MERGECSV_HELP")
sortcsv [OPTIONS] INPUT -k FIELD
2013-08-27 15:14:44 +04:00
$(sed 's/^/ /g' <<<"$__SORTCSV_HELP")
dumpcsv [OPTIONS] FIELDS...
$(sed 's/^/ /g' <<<"$__DUMPCSV_HELP")
printcsv [OPTIONS] VALUES...
$(sed 's/^/ /g' <<<"$__PRINTCSV_HELP")
2013-08-27 15:14:44 +04:00
lsed ARGS
lawk ARGS
lgrep ARGS
lsort [ARGS]
lawkrun [ARGS]
lawkcsv [OPTIONS] [ATTRS...]
lmergecsv [OPTIONS] LEFT RIGHT -k FIELD
lsortcsv [OPTIONS] INPUT -k FIELD
ldumpcsv [OPTIONS] FIELDS...
lprintcsv [OPTIONS] VALUES...
2013-08-27 15:14:44 +04:00
Ces commandes sont comme les commandes sans le préfixe 'l', mais elles sont
lancées sans LANG=C, ce qui fait qu'elle tiennent compte de la locale.
En effet, les outils de coreutils utilisent les règles de la locale pour le
tri et la comparaison des chaines, et par exemple, avec LANG=fr_FR.UTF-8, la
locale indique que les ponctuations doivent être ignorées pour le tri, ce
qui peut poser problème.
csed ARGS
cawk ARGS
cgrep ARGS
csort [ARGS]
cawkrun [ARGS]
cawkcsv [OPTIONS] [ATTRS...]
cmergecsv [OPTIONS] LEFT RIGHT -k FIELD
csortcsv [OPTIONS] INPUT -k FIELD
2013-08-27 15:14:44 +04:00
Ces commandes existent pour compatibilité. Ces commandes sont des aliases
des commandes sans le préfixe 'c', et existent pour insister sur le fait
qu'elles sont lancées avec LANG=C.
Les directives suivantes transforment le flux au format ldif en une suite de
commandes de modifications pour ldapmodify:
A, modaddattr
Créer un objet de toutes pièces avec les attributs donnés et leurs valeurs
a, modaddval
Ajouter les valeurs spécifiée à l'attribut
r, modreplval
Remplacer les valeurs de l'attribut par celles spécifiées
d, moddelval
Supprimer les valeurs spécifiées de l'attribut
D, moddelattr
Supprimer l'attribut
delentry
2014-09-16 08:26:27 +04:00
Supprimer l'objet
rename, modrdn NEWRDN[,NEWSUPERIOR] [keep[oldrdn]] [SUFFIX]
Déplacer/renommer l'objet. L'ancienne valeur est supprimée, à moins que
l'argument keep ne soit spécifié.
Si NEWRDN est vide, alors ',NEWSUPERIOR' doit être spécifié (remarquez la
virgule avant le nouveau supérieur), et cela signifie que l'objet a le même
RDN dans la nouvelle branche.
SUFFIX est le suffixe de la base de donnée. Le DN 'NEWRDN,NEWSUPERIOR' est
exprimé relativement à SUFFIX, sauf s'il se termine par une virgule. Par
exemple, étant donné SUFFIX='dc=domain,dc=tld', les expressions suivantes
sont équivalentes:
modrdn uid=new,ou=people <=> modrdn uid=new,ou=people,dc=domain,dc=tld
modrdn uid=new,cn=config, <=> modrdn uid=new,cn=config
2014-09-16 08:26:27 +04:00
touchentry
Forcer la réplication de l'objet en simulant une modification"
2013-08-27 15:14:44 +04:00
function get_transform_cmd() {
# Créer une liste de commandes bash à évaluer en fonction des arguments: une
# suite de commandes séparées par //
# Les variables suivantes peuvent être définies en entrée:
# _T_inputfile:
# Si cette variable est non vide, lire à partir du fichier $_T_inputfile
# au lieu de stdin
# _T_uncut_before:
# faut-il fusionner automatiquement les lignes *avant* de lancer les
# commandes.
# _T_cut_after:
# faut-il découper automatiquement les lignes *après* avoir lancé les
# commandes.
# _T_suffix:
# suffixe de la base de données pour les commandes qui prennent des RDN,
# e.g. modrdn
2013-08-27 15:14:44 +04:00
local -a cmds cmdparts
local cmd dest first=1 xempty xdel
local auto_uncut="$_T_uncut_before"
local auto_cut="$_T_cut_after" last_is_cut
[ -n "$_T_inputfile" ] && dest='cat "$_T_inputfile"'
if [ -n "$*" ]; then
cmd=
while [ "$#" -gt 0 ]; do # parce qu'on peut avoir un argument vide!
if [ "$1" == // ]; then
[ -n "$cmd" ] && cmds=("${cmds[@]}" "$cmd")
cmd=
shift
continue
fi
cmd="${cmd:+"$cmd "}$(quoted_arg "$1")"
shift
done
[ -n "$cmd" ] && cmds=("${cmds[@]}" "$cmd")
cmd=
fi
for cmd in "${cmds[@]}"; do
eval "set -- $cmd"
cmd="$1"; shift
xempty=
xdel=
last_is_cut=
case "$cmd" in
u|uncut)
if [ -n "$first" -a -n "$auto_uncut" ]; then
# l'uncut automatique est demandé, et la première commande est
# uncut: pas besoin de faire l'uncut automatique
auto_uncut=
fi
cmdparts=(uncut_lines)
;;
c|cut)
cmdparts=(cut_lines)
last_is_cut=1
;;
dec|decode) cmdparts=(tl_decode "$(def_match_attr "$@")");;
enc|encode) cmdparts=(tl_encode "$(def_match_attr "$@")");;
xe|xempty|excludeempty) cmdparts=(ensure_complete_objects);;
2013-08-27 15:14:44 +04:00
k|keep|keepattr)
[ "$1" == --no-xempty ] && shift || xempty=1
2013-08-27 15:14:44 +04:00
cmdparts=(tl_keepattr "$(def_match_attr dn "$@")")
;;
K|kv|keepval)
[ "$1" == --no-xempty ] && shift || xempty=1
2013-08-27 15:14:44 +04:00
local match_attr="$(def_match_attr "$1")"; shift
cmdparts=(tl_keepval "$match_attr" "$(def_match_value "$@")")
;;
x|exclude|excludeattr)
[ "$1" == --no-xempty ] && shift || xempty=1
2013-08-27 15:14:44 +04:00
cmdparts=(tl_excludeattr "$(def_match_attr "$@")")
;;
dv|delval|X|xv|excludeval)
[ "$1" == --no-xempty ] && shift || xempty=1
2013-08-27 15:14:44 +04:00
local match_attr="$(def_match_attr "$1")"; shift
cmdparts=(tl_excludeval "$match_attr" "$(def_match_value "$@")")
;;
xve|excludevalentry)
local match_attr="$(def_match_attr "$1")"; shift
cmdparts=(tl_excludevalentry "$match_attr" "$(def_match_value "$@")")
xdel=1
;;
kve|keepvalentry)
local match_attr="$(def_match_attr "$1")"; shift
cmdparts=(tl_keepvalentry "$match_attr" "$(def_match_value "$@")")
xdel=1
;;
sv|setval|rv|replval)
local match_attr="$(def_match_attr "$1")"; shift
cmdparts=(tl_replval "$match_attr" "$@")
;;
av|addval) cmdparts=(tl_addval "$@");;
fv|defval) cmdparts=(tl_defval "$@");;
ev|ensureval) cmdparts=(tl_ensureval "$@");;
2013-08-27 15:14:44 +04:00
sed|awk|grep|sort) cmdparts=(LANG=C "$cmd" "$@");;
csed|cawk|cgrep|csort) cmdparts=(LANG=C "${cmd#c}" "$@");;
lsed|lawk|lgrep|lsort) cmdparts=("${cmd#l}" "$@");;
awkrun|cawkrun) cmdparts=(cawkrun "$@");;
lawkrun) cmdparts=(lawkrun "$@");;
awkcsv|csvawk|cawkcsv) cmdparts=(cawkcsv "$@");;
lawkcsv) cmdparts=(lawkcsv "$@");;
mergecsv|cmergecsv) cmdparts=(cmergecsv "$@");;
lmergecsv) cmdparts=(lmergecsv "$@");;
sortcsv|csortcsv) cmdparts=(csortcsv "$@");;
lsortcsv) cmdparts=(lsortcsv "$@");;
dumpcsv|cdumpcsv) cmdparts=(cdumpcsv "$@");;
ldumpcsv) cmdparts=(ldumpcsv "$@");;
printcsv|cprintcsv) cmdparts=(cprintcsv "$@");;
lprintcsv) cmdparts=(lprintcsv "$@");;
2013-08-27 15:14:44 +04:00
f|format) cmdparts=(tl_format "$@");;
fcsv|formatcsv|format_csv|csv) cmdparts=(tl_formatcsv "$@");;
pcsv|parsecsv|parse_csv) cmdparts=(tl_parsecsv "$@");;
pcsvmod|parsecsvmod|parse_csvmod) cmdparts=(tl_parsecsvmod "$@");;
A|modaddattr) cmdparts=(tl_addattr);;
a|ma|modadd|modaddval) cmdparts=(tl_modifyattr add);;
r|mr|modrepl|modreplval) cmdparts=(tl_modifyattr replace);;
d|md|moddel|moddelval) cmdparts=(tl_modifyattr delete);;
D|moddelattr) cmdparts=(tl_deleteattr);;
delentry|moddelentry) cmdparts=(tl_deleteentry);;
rename|modrdn|modrename|modmodrdn) cmdparts=(tl_modrdn "$1" "$2" "${3:-$_T_suffix}");;
2014-09-16 08:26:27 +04:00
touch|touchentry|modtouchentry) cmdparts=(tl_touchentry);;
litteral) cmdparts=("$@");;
2013-08-27 15:14:44 +04:00
*)
eerror "$cmd: commande inconnue. elle sera ignorée"
continue
;;
esac
[ -n "$auto_uncut" ] && dest="${dest:+"$dest | "}"'uncut_lines'
dest="${dest:+"$dest | "}$(quoted_args "${cmdparts[@]}")"
[ -n "$xempty" ] && dest="${dest:+"$dest | "}"'ensure_complete_objects'
[ -n "$xdel" ] && dest="${dest:+"$dest | "}"'delete_marked_objects'
first=
auto_uncut=
done
[ -n "$auto_cut" -a -z "$last_is_cut" ] && dest="${dest:+"$dest | "}"'cut_lines'
echo "$dest"
}
function transform() { eval "$(get_transform_cmd "$@")"; }