ctdb-failover: Split statd_callout add-client/del-client
[Samba.git] / python / samba / netcmd / contact.py
blob064a3ce6691a4a87b6794d61da7b4d55312e58f7
1 # samba-tool contact management
3 # Copyright Bjoern Baumbach 2019 <bbaumbach@samba.org>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import samba.getopt as options
20 import ldb
21 import os
22 import tempfile
23 from subprocess import check_call, CalledProcessError
24 from operator import attrgetter
25 from samba.auth import system_session
26 from samba.samdb import SamDB
27 from samba import (
28 credentials,
29 dsdb,
31 from samba.net import Net
33 from samba.netcmd import (
34 Command,
35 CommandError,
36 SuperCommand,
37 Option,
39 from samba.common import get_bytes
40 from . import common
43 class cmd_add(Command):
44 """Add a new contact.
46 This command adds a new contact to the Active Directory domain.
48 The name of the new contact can be specified by the first argument
49 'contactname' or the --given-name, --initial and --surname arguments.
50 If no 'contactname' is given, contact's name will be made up of the given
51 arguments by combining the given-name, initials and surname. Each argument
52 is optional. A dot ('.') will be appended to the initials automatically.
54 Example1:
55 samba-tool contact add "James T. Kirk" --job-title=Captain \\
56 -H ldap://samba.samdom.example.com -UAdministrator%Passw1rd
58 The example shows how to add a new contact to the domain against a remote
59 LDAP server.
61 Example2:
62 samba-tool contact add --given-name=James --initials=T --surname=Kirk
64 The example shows how to add a new contact to the domain against a local
65 server. The resulting name is "James T. Kirk".
66 """
68 synopsis = "%prog [contactname] [options]"
70 takes_options = [
71 Option("-H", "--URL", help="LDB URL for database or target server",
72 type=str, metavar="URL", dest="H"),
73 Option("--ou",
74 help=("DN of alternative location (with or without domainDN "
75 "counterpart) in which the new contact will be created. "
76 "E.g. 'OU=<OU name>'. "
77 "Default is the domain base."),
78 type=str),
79 Option("--surname", help="Contact's surname", type=str),
80 Option("--given-name", help="Contact's given name", type=str),
81 Option("--initials", help="Contact's initials", type=str),
82 Option("--display-name", help="Contact's display name", type=str),
83 Option("--job-title", help="Contact's job title", type=str),
84 Option("--department", help="Contact's department", type=str),
85 Option("--company", help="Contact's company", type=str),
86 Option("--description", help="Contact's description", type=str),
87 Option("--mail-address", help="Contact's email address", type=str),
88 Option("--internet-address", help="Contact's home page", type=str),
89 Option("--telephone-number", help="Contact's phone number", type=str),
90 Option("--mobile-number",
91 help="Contact's mobile phone number",
92 type=str),
93 Option("--physical-delivery-office",
94 help="Contact's office location",
95 type=str),
98 takes_args = ["fullcontactname?"]
100 takes_optiongroups = {
101 "sambaopts": options.SambaOptions,
102 "credopts": options.CredentialsOptions,
103 "versionopts": options.VersionOptions,
106 def run(self,
107 fullcontactname=None,
108 sambaopts=None,
109 credopts=None,
110 versionopts=None,
111 H=None,
112 ou=None,
113 surname=None,
114 given_name=None,
115 initials=None,
116 display_name=None,
117 job_title=None,
118 department=None,
119 company=None,
120 description=None,
121 mail_address=None,
122 internet_address=None,
123 telephone_number=None,
124 mobile_number=None,
125 physical_delivery_office=None):
127 lp = sambaopts.get_loadparm()
128 creds = credopts.get_credentials(lp)
130 try:
131 samdb = SamDB(url=H,
132 session_info=system_session(),
133 credentials=creds,
134 lp=lp)
135 ret_name = samdb.newcontact(
136 fullcontactname=fullcontactname,
137 ou=ou,
138 surname=surname,
139 givenname=given_name,
140 initials=initials,
141 displayname=display_name,
142 jobtitle=job_title,
143 department=department,
144 company=company,
145 description=description,
146 mailaddress=mail_address,
147 internetaddress=internet_address,
148 telephonenumber=telephone_number,
149 mobilenumber=mobile_number,
150 physicaldeliveryoffice=physical_delivery_office)
151 except Exception as e:
152 raise CommandError("Failed to add contact", e)
154 self.outf.write("Contact '%s' added successfully\n" % ret_name)
157 class cmd_delete(Command):
158 """Delete a contact.
160 This command deletes a contact object from the Active Directory domain.
162 The contactname specified on the command is the common name or the
163 distinguished name of the contact object. The distinguished name of the
164 contact can be specified with or without the domainDN component.
166 Example:
167 samba-tool contact delete Contact1 \\
168 -H ldap://samba.samdom.example.com \\
169 --username=Administrator --password=Passw1rd
171 The example shows how to delete a contact in the domain against a remote
172 LDAP server.
174 synopsis = "%prog <contactname> [options]"
176 takes_options = [
177 Option("-H",
178 "--URL",
179 help="LDB URL for database or target server",
180 type=str,
181 metavar="URL",
182 dest="H"),
185 takes_args = ["contactname"]
187 takes_optiongroups = {
188 "sambaopts": options.SambaOptions,
189 "credopts": options.CredentialsOptions,
190 "versionopts": options.VersionOptions,
193 def run(self,
194 contactname,
195 sambaopts=None,
196 credopts=None,
197 versionopts=None,
198 H=None):
199 lp = sambaopts.get_loadparm()
200 creds = credopts.get_credentials(lp, fallback_machine=True)
201 samdb = SamDB(url=H,
202 session_info=system_session(),
203 credentials=creds,
204 lp=lp)
205 base_dn = samdb.domain_dn()
206 scope = ldb.SCOPE_SUBTREE
208 filter = ("(&(objectClass=contact)(name=%s))" %
209 ldb.binary_encode(contactname))
211 if contactname.upper().startswith("CN="):
212 # contact is specified by DN
213 filter = "(objectClass=contact)"
214 scope = ldb.SCOPE_BASE
215 try:
216 base_dn = samdb.normalize_dn_in_domain(contactname)
217 except Exception as e:
218 raise CommandError('Invalid dn "%s": %s' %
219 (contactname, e))
221 try:
222 res = samdb.search(base=base_dn,
223 scope=scope,
224 expression=filter,
225 attrs=["dn"])
226 contact_dn = res[0].dn
227 except IndexError:
228 raise CommandError('Unable to find contact "%s"' % (contactname))
230 if len(res) > 1:
231 for msg in sorted(res, key=attrgetter('dn')):
232 self.outf.write("found: %s\n" % msg.dn)
233 raise CommandError("Multiple results for contact '%s'\n"
234 "Please specify the contact's full DN" %
235 contactname)
237 try:
238 samdb.delete(contact_dn)
239 except Exception as e:
240 raise CommandError('Failed to remove contact "%s"' % contactname, e)
241 self.outf.write("Deleted contact %s\n" % contactname)
244 class cmd_list(Command):
245 """List all contacts.
248 synopsis = "%prog [options]"
250 takes_options = [
251 Option("-H",
252 "--URL",
253 help="LDB URL for database or target server",
254 type=str,
255 metavar="URL",
256 dest="H"),
257 Option("-b", "--base-dn",
258 help="Specify base DN to use.",
259 type=str),
260 Option("--full-dn",
261 dest="full_dn",
262 default=False,
263 action='store_true',
264 help="Display contact's full DN instead of the name."),
267 takes_optiongroups = {
268 "sambaopts": options.SambaOptions,
269 "credopts": options.CredentialsOptions,
270 "versionopts": options.VersionOptions,
273 def run(self,
274 sambaopts=None,
275 credopts=None,
276 versionopts=None,
277 H=None,
278 base_dn=None,
279 full_dn=False):
280 lp = sambaopts.get_loadparm()
281 creds = credopts.get_credentials(lp, fallback_machine=True)
283 samdb = SamDB(url=H,
284 session_info=system_session(),
285 credentials=creds,
286 lp=lp)
288 search_dn = samdb.domain_dn()
289 if base_dn:
290 search_dn = samdb.normalize_dn_in_domain(base_dn)
292 res = samdb.search(search_dn,
293 scope=ldb.SCOPE_SUBTREE,
294 expression="(objectClass=contact)",
295 attrs=["name"])
296 if (len(res) == 0):
297 return
299 if full_dn:
300 for msg in sorted(res, key=attrgetter('dn')):
301 self.outf.write("%s\n" % msg.dn)
302 return
304 for msg in res:
305 contact_name = msg.get("name", idx=0)
307 self.outf.write("%s\n" % contact_name)
310 class cmd_edit(Command):
311 """Modify a contact.
313 This command will allow editing of a contact object in the Active Directory
314 domain. You will then be able to add or change attributes and their values.
316 The contactname specified on the command is the common name or the
317 distinguished name of the contact object. The distinguished name of the
318 contact can be specified with or without the domainDN component.
320 The command may be run from the root userid or another authorized userid.
322 The -H or --URL= option can be used to execute the command against a remote
323 server.
325 Example1:
326 samba-tool contact edit Contact1 -H ldap://samba.samdom.example.com \\
327 -U Administrator --password=Passw1rd
329 Example1 shows how to edit a contact's attributes in the domain against a
330 remote LDAP server.
332 The -H parameter is used to specify the remote target server.
334 Example2:
335 samba-tool contact edit CN=Contact2,OU=people,DC=samdom,DC=example,DC=com
337 Example2 shows how to edit a contact's attributes in the domain against a
338 local server. The contact, which is located in the 'people' OU,
339 is specified by the full distinguished name.
341 Example3:
342 samba-tool contact edit Contact3 --editor=nano
344 Example3 shows how to edit a contact's attributes in the domain against a
345 local server using the 'nano' editor.
347 synopsis = "%prog <contactname> [options]"
349 takes_options = [
350 Option("-H",
351 "--URL",
352 help="LDB URL for database or target server",
353 type=str,
354 metavar="URL",
355 dest="H"),
356 Option("--editor",
357 help="Editor to use instead of the system default, "
358 "or 'vi' if no system default is set.",
359 type=str),
362 takes_args = ["contactname"]
363 takes_optiongroups = {
364 "sambaopts": options.SambaOptions,
365 "credopts": options.CredentialsOptions,
366 "versionopts": options.VersionOptions,
369 def run(self,
370 contactname,
371 sambaopts=None,
372 credopts=None,
373 versionopts=None,
374 H=None,
375 editor=None):
376 lp = sambaopts.get_loadparm()
377 creds = credopts.get_credentials(lp, fallback_machine=True)
378 samdb = SamDB(url=H, session_info=system_session(),
379 credentials=creds, lp=lp)
380 base_dn = samdb.domain_dn()
381 scope = ldb.SCOPE_SUBTREE
383 filter = ("(&(objectClass=contact)(name=%s))" %
384 ldb.binary_encode(contactname))
386 if contactname.upper().startswith("CN="):
387 # contact is specified by DN
388 filter = "(objectClass=contact)"
389 scope = ldb.SCOPE_BASE
390 try:
391 base_dn = samdb.normalize_dn_in_domain(contactname)
392 except Exception as e:
393 raise CommandError('Invalid dn "%s": %s' %
394 (contactname, e))
396 try:
397 res = samdb.search(base=base_dn,
398 scope=scope,
399 expression=filter)
400 contact_dn = res[0].dn
401 except IndexError:
402 raise CommandError('Unable to find contact "%s"' % (contactname))
404 if len(res) > 1:
405 for msg in sorted(res, key=attrgetter('dn')):
406 self.outf.write("found: %s\n" % msg.dn)
407 raise CommandError("Multiple results for contact '%s'\n"
408 "Please specify the contact's full DN" %
409 contactname)
411 for msg in res:
412 result_ldif = common.get_ldif_for_editor(samdb, msg)
414 if editor is None:
415 editor = os.environ.get('EDITOR')
416 if editor is None:
417 editor = 'vi'
419 with tempfile.NamedTemporaryFile(suffix=".tmp") as t_file:
420 t_file.write(get_bytes(result_ldif))
421 t_file.flush()
422 try:
423 check_call([editor, t_file.name])
424 except CalledProcessError as e:
425 raise CalledProcessError("ERROR: ", e)
426 with open(t_file.name) as edited_file:
427 edited_message = edited_file.read()
430 msgs_edited = samdb.parse_ldif(edited_message)
431 msg_edited = next(msgs_edited)[1]
433 res_msg_diff = samdb.msg_diff(msg, msg_edited)
434 if len(res_msg_diff) == 0:
435 self.outf.write("Nothing to do\n")
436 return
438 try:
439 samdb.modify(res_msg_diff)
440 except Exception as e:
441 raise CommandError("Failed to modify contact '%s': " % contactname,
444 self.outf.write("Modified contact '%s' successfully\n" % contactname)
447 class cmd_show(Command):
448 """Display a contact.
450 This command displays a contact object with it's attributes in the Active
451 Directory domain.
453 The contactname specified on the command is the common name or the
454 distinguished name of the contact object. The distinguished name of the
455 contact can be specified with or without the domainDN component.
457 The command may be run from the root userid or another authorized userid.
459 The -H or --URL= option can be used to execute the command against a remote
460 server.
462 Example1:
463 samba-tool contact show Contact1 -H ldap://samba.samdom.example.com \\
464 -U Administrator --password=Passw1rd
466 Example1 shows how to display a contact's attributes in the domain against
467 a remote LDAP server.
469 The -H parameter is used to specify the remote target server.
471 Example2:
472 samba-tool contact show CN=Contact2,OU=people,DC=samdom,DC=example,DC=com
474 Example2 shows how to display a contact's attributes in the domain against
475 a local server. The contact, which is located in the 'people' OU, is
476 specified by the full distinguished name.
478 Example3:
479 samba-tool contact show Contact3 --attributes=mail,mobile
481 Example3 shows how to display a contact's mail and mobile attributes.
483 synopsis = "%prog <contactname> [options]"
485 takes_options = [
486 Option("-H",
487 "--URL",
488 help="LDB URL for database or target server",
489 type=str,
490 metavar="URL",
491 dest="H"),
492 Option("--attributes",
493 help=("Comma separated list of attributes, "
494 "which will be printed."),
495 type=str,
496 dest="contact_attrs"),
499 takes_args = ["contactname"]
500 takes_optiongroups = {
501 "sambaopts": options.SambaOptions,
502 "credopts": options.CredentialsOptions,
503 "versionopts": options.VersionOptions,
506 def run(self,
507 contactname,
508 sambaopts=None,
509 credopts=None,
510 versionopts=None,
511 H=None,
512 contact_attrs=None):
514 lp = sambaopts.get_loadparm()
515 creds = credopts.get_credentials(lp, fallback_machine=True)
516 samdb = SamDB(url=H,
517 session_info=system_session(),
518 credentials=creds,
519 lp=lp)
520 base_dn = samdb.domain_dn()
521 scope = ldb.SCOPE_SUBTREE
523 attrs = None
524 if contact_attrs:
525 attrs = contact_attrs.split(",")
527 filter = ("(&(objectClass=contact)(name=%s))" %
528 ldb.binary_encode(contactname))
530 if contactname.upper().startswith("CN="):
531 # contact is specified by DN
532 filter = "(objectClass=contact)"
533 scope = ldb.SCOPE_BASE
534 try:
535 base_dn = samdb.normalize_dn_in_domain(contactname)
536 except Exception as e:
537 raise CommandError('Invalid dn "%s": %s' %
538 (contactname, e))
540 try:
541 res = samdb.search(base=base_dn,
542 expression=filter,
543 scope=scope,
544 attrs=attrs)
545 contact_dn = res[0].dn
546 except IndexError:
547 raise CommandError('Unable to find contact "%s"' % (contactname))
549 if len(res) > 1:
550 for msg in sorted(res, key=attrgetter('dn')):
551 self.outf.write("found: %s\n" % msg.dn)
552 raise CommandError("Multiple results for contact '%s'\n"
553 "Please specify the contact's DN" %
554 contactname)
556 for msg in res:
557 contact_ldif = common.get_ldif_for_editor(samdb, msg)
558 self.outf.write(contact_ldif)
561 class cmd_move(Command):
562 """Move a contact object to an organizational unit or container.
564 The contactname specified on the command is the common name or the
565 distinguished name of the contact object. The distinguished name of the
566 contact can be specified with or without the domainDN component.
568 The name of the organizational unit or container can be specified as the
569 distinguished name, with or without the domainDN component.
571 The command may be run from the root userid or another authorized userid.
573 The -H or --URL= option can be used to execute the command against a remote
574 server.
576 Example1:
577 samba-tool contact move Contact1 'OU=people' \\
578 -H ldap://samba.samdom.example.com -U Administrator
580 Example1 shows how to move a contact Contact1 into the 'people'
581 organizational unit on a remote LDAP server.
583 The -H parameter is used to specify the remote target server.
585 Example2:
586 samba-tool contact move Contact1 OU=Contacts,DC=samdom,DC=example,DC=com
588 Example2 shows how to move a contact Contact1 into the OU=Contacts
589 organizational unit on the local server.
592 synopsis = "%prog <contactname> <new_parent_dn> [options]"
594 takes_options = [
595 Option("-H",
596 "--URL",
597 help="LDB URL for database or target server",
598 type=str,
599 metavar="URL",
600 dest="H"),
603 takes_args = ["contactname", "new_parent_dn"]
604 takes_optiongroups = {
605 "sambaopts": options.SambaOptions,
606 "credopts": options.CredentialsOptions,
607 "versionopts": options.VersionOptions,
610 def run(self,
611 contactname,
612 new_parent_dn,
613 sambaopts=None,
614 credopts=None,
615 versionopts=None,
616 H=None):
617 lp = sambaopts.get_loadparm()
618 creds = credopts.get_credentials(lp, fallback_machine=True)
619 samdb = SamDB(url=H,
620 session_info=system_session(),
621 credentials=creds,
622 lp=lp)
623 base_dn = samdb.domain_dn()
624 scope = ldb.SCOPE_SUBTREE
626 filter = ("(&(objectClass=contact)(name=%s))" %
627 ldb.binary_encode(contactname))
629 if contactname.upper().startswith("CN="):
630 # contact is specified by DN
631 filter = "(objectClass=contact)"
632 scope = ldb.SCOPE_BASE
633 try:
634 base_dn = samdb.normalize_dn_in_domain(contactname)
635 except Exception as e:
636 raise CommandError('Invalid dn "%s": %s' %
637 (contactname, e))
639 try:
640 res = samdb.search(base=base_dn,
641 scope=scope,
642 expression=filter,
643 attrs=["dn"])
644 contact_dn = res[0].dn
645 except IndexError:
646 raise CommandError('Unable to find contact "%s"' % (contactname))
648 if len(res) > 1:
649 for msg in sorted(res, key=attrgetter('dn')):
650 self.outf.write("found: %s\n" % msg.dn)
651 raise CommandError("Multiple results for contact '%s'\n"
652 "Please specify the contact's full DN" %
653 contactname)
655 try:
656 full_new_parent_dn = samdb.normalize_dn_in_domain(new_parent_dn)
657 except Exception as e:
658 raise CommandError('Invalid new_parent_dn "%s": %s' %
659 (new_parent_dn, e))
661 full_new_contact_dn = ldb.Dn(samdb, str(contact_dn))
662 full_new_contact_dn.remove_base_components(len(contact_dn) - 1)
663 full_new_contact_dn.add_base(full_new_parent_dn)
665 try:
666 samdb.rename(contact_dn, full_new_contact_dn)
667 except Exception as e:
668 raise CommandError('Failed to move contact "%s"' % contactname, e)
669 self.outf.write('Moved contact "%s" into "%s"\n' %
670 (contactname, full_new_parent_dn))
672 class cmd_rename(Command):
673 """Rename a contact and related attributes.
675 This command allows to set the contact's name related attributes.
676 The contact's new CN will be made up by combining the given-name, initials
677 and surname. A dot ('.') will be appended to the initials automatically, if
678 required.
679 Use the --force-new-cn option to specify the new CN manually and the
680 --reset-cn option to reset this changes.
682 Use an empty attribute value to remove the specified attribute.
684 The contactname specified on the command is the CN.
686 The command may be run locally from the root userid or another authorized
687 userid.
689 The -H or --URL= option can be used to execute the command against a remote
690 server.
692 Example1:
693 samba-tool contact rename "John Doe" --surname=Bloggs \\
694 --force-new-cn=John
696 Example1 shows how to change the surname ('sn' attribute) of a contact
697 'John Doe' to 'Bloggs' and change the CN to 'John' on the local server.
699 Example2:
700 samba-tool contact rename "J Doe" --given-name=John
701 -H ldap://samba.samdom.example.com -U administrator
703 Example2 shows how to rename the given name of a contact 'J Doe' to
704 'John'. The contact's cn will be renamed automatically, based on
705 the given name, initials and surname, if the previous CN is the
706 standard combination of the previous name attributes.
707 The -H parameter is used to specify the remote target server.
710 synopsis = "%prog <contactname> [options]"
712 takes_options = [
713 Option("-H", "--URL",
714 help="LDB URL for database or target server",
715 type=str, metavar="URL", dest="H"),
716 Option("--surname",
717 help="New surname",
718 type=str),
719 Option("--given-name",
720 help="New given name",
721 type=str),
722 Option("--initials",
723 help="New initials",
724 type=str),
725 Option("--force-new-cn",
726 help="Specify a new CN (RDN) instead of using a combination "
727 "of the given name, initials and surname.",
728 type=str, metavar="NEW_CN"),
729 Option("--reset-cn",
730 help="Set the CN (RDN) to the combination of the given name, "
731 "initials and surname. Use this option to reset "
732 "the changes made with the --force-new-cn option.",
733 action="store_true"),
734 Option("--display-name",
735 help="New display name",
736 type=str),
737 Option("--mail-address",
738 help="New email address",
739 type=str),
742 takes_args = ["contactname"]
743 takes_optiongroups = {
744 "sambaopts": options.SambaOptions,
745 "credopts": options.CredentialsOptions,
746 "versionopts": options.VersionOptions,
750 def run(self, contactname, credopts=None, sambaopts=None, versionopts=None,
751 H=None, surname=None, given_name=None, initials=None, force_new_cn=None,
752 display_name=None, mail_address=None, reset_cn=None):
753 # illegal options
754 if force_new_cn and reset_cn:
755 raise CommandError("It is not allowed to specify --force-new-cn "
756 "together with --reset-cn.")
757 if force_new_cn == "":
758 raise CommandError("Failed to rename contact - delete protected "
759 "attribute 'CN'")
761 lp = sambaopts.get_loadparm()
762 creds = credopts.get_credentials(lp, fallback_machine=True)
763 samdb = SamDB(url=H, session_info=system_session(),
764 credentials=creds, lp=lp)
765 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
767 filter = ("(&(objectClass=contact)(name=%s))" %
768 ldb.binary_encode(contactname))
769 try:
770 res = samdb.search(base=domain_dn,
771 scope=ldb.SCOPE_SUBTREE,
772 expression=filter,
773 attrs=["name",
774 "sn",
775 "givenName",
776 "cn",
777 "initials",
778 "displayName",
779 "mail"]
781 old_contact = res[0]
782 contact_dn = old_contact.dn
783 except IndexError:
784 raise CommandError('Unable to find contact "%s"' % (contactname))
786 contact_parent_dn = contact_dn.parent()
787 old_cn = old_contact["cn"][0]
789 if force_new_cn is not None:
790 new_cn = force_new_cn
791 else:
792 new_cn = samdb.fullname_from_names(old_attrs=old_contact,
793 given_name=given_name,
794 initials=initials,
795 surname=surname)
797 # change CN, if the new CN is different and the old CN is the
798 # standard CN or the change is forced with force-new-cn or reset-cn
799 excepted_cn = samdb.fullname_from_names(old_attrs=old_contact)
800 must_change_cn = str(old_cn) != str(new_cn) and \
801 (str(old_cn) == str(excepted_cn) or \
802 reset_cn or bool(force_new_cn))
804 new_contact_dn = ldb.Dn(samdb, "CN=%s" % new_cn)
805 new_contact_dn.add_base(contact_parent_dn)
807 if new_cn == "" and must_change_cn:
808 raise CommandError("Failed to rename contact '%s' - "
809 "can not set an empty CN "
810 "(please use --force-new-cn to specify a "
811 "different CN or --given-name, --initials or "
812 "--surname to set name attributes)" % old_cn)
814 # format given attributes
815 contact_attrs = ldb.Message()
816 contact_attrs.dn = contact_dn
817 samdb.prepare_attr_replace(contact_attrs, old_contact, "givenName", given_name)
818 samdb.prepare_attr_replace(contact_attrs, old_contact, "sn", surname)
819 samdb.prepare_attr_replace(contact_attrs, old_contact, "initials", initials)
820 samdb.prepare_attr_replace(contact_attrs, old_contact, "displayName", display_name)
821 samdb.prepare_attr_replace(contact_attrs, old_contact, "mail", mail_address)
823 contact_attributes_changed = len(contact_attrs) > 0
825 # update the contact with formatted attributes
826 samdb.transaction_start()
827 try:
828 if contact_attributes_changed == True:
829 samdb.modify(contact_attrs)
830 if must_change_cn:
831 samdb.rename(contact_dn, new_contact_dn)
832 except Exception as e:
833 samdb.transaction_cancel()
834 raise CommandError('Failed to rename contact "%s"' % contactname, e)
835 samdb.transaction_commit()
837 if must_change_cn:
838 self.outf.write('Renamed CN of contact "%s" from "%s" to "%s" '
839 'successfully\n' % (contactname, old_cn, new_cn))
841 if contact_attributes_changed:
842 self.outf.write('Following attributes of contact "%s" have been '
843 'changed successfully:\n' % (contactname))
844 for attr in contact_attrs.keys():
845 if attr == "dn":
846 continue
847 self.outf.write('%s: %s\n' % (attr, contact_attrs[attr]
848 if contact_attrs[attr] else '[removed]'))
850 class cmd_contact(SuperCommand):
851 """Contact management."""
853 subcommands = {}
854 subcommands["add"] = cmd_add()
855 subcommands["create"] = cmd_add()
856 subcommands["delete"] = cmd_delete()
857 subcommands["edit"] = cmd_edit()
858 subcommands["list"] = cmd_list()
859 subcommands["move"] = cmd_move()
860 subcommands["show"] = cmd_show()
861 subcommands["rename"] = cmd_rename()