directoryassistant-2.0/ 0000755 0001750 0001750 00000000000 10346660730 016462 5 ustar olivier olivier 0000000 0000000 directoryassistant-2.0/Makefile 0000644 0001750 0001750 00000001130 10346653223 020114 0 ustar olivier olivier 0000000 0000000 prefix = /usr
INSTALL = install
directoryassistant.1.gz:
python -c 'import ldap'
python -c 'import gobject'
cat directoryassistant.1 | gzip -9 > directoryassistant.1.gz
install: directoryassistant.1.gz
${INSTALL} -d -m 755 ${prefix}/share/man/man1/
${INSTALL} -d -m 755 ${prefix}/bin/
${INSTALL} -g 0 -o root -m 0644 directoryassistant.1.gz ${prefix}/share/man/man1/
${INSTALL} -g 0 -o root -m 0755 directoryassistant ${prefix}/bin/
uninstall:
rm -f ${prefix}/share/man/man1/directoryassistant.1.gz
rm -f ${prefix}/bin/directoryassistant
clean:
rm -f *.gz
rm -f *~
distclean: clean
directoryassistant-2.0/directoryassistant 0000755 0001750 0001750 00000044535 10346660725 022365 0 ustar olivier olivier 0000000 0000000 #!/usr/bin/python
#
# Directory Assistant 2.0 - Copyright 2003,2004,2005 Olivier Sessink
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted as stated in the included COPYRIGHT file.
#
VERSIONSTRING = '2.0'
import gtk
import gobject
import ldap
import string
import ConfigParser
import os
class field:
def __init__(self,name,title='',editable=0,multiline=0):
self.name = name
self.title = title
self.editable = editable
self.multiline = multiline
LDAP_KEY_MAP = []
LDAP_KEY_MAP.append(field('cn','Common name',0,0))
LDAP_KEY_MAP.append(field('sn','Surname',1,0))
LDAP_KEY_MAP.append(field('givenName','Given name',1,0))
LDAP_KEY_MAP.append(field('o','Organisation',1,0))
LDAP_KEY_MAP.append(field('mail','Email',1,0))
LDAP_KEY_MAP.append(field('telephoneNumber','Work phone number',1,0))
LDAP_KEY_MAP.append(field('facsimileTelephoneNumber','Fax number',1,0))
LDAP_KEY_MAP.append(field('mobile','Mobile phone number',1,0))
LDAP_KEY_MAP.append(field('street','Street',1,0))
LDAP_KEY_MAP.append(field('postalAddress','Postal address',1,1))
LDAP_KEY_MAP.append(field('postalCode','Postal code',1,0))
LDAP_KEY_MAP.append(field('l','Locality',1,0))
LDAP_KEY_MAP.append(field('homePhone','Home phone number',1,0))
LDAP_KEY_MAP.append(field('homePostalAddress','Home address',1,1))
LDAP_KEY_MAP.append(field('description','Description',1,1))
def all_keys():
ret = []
for field in LDAP_KEY_MAP:
ret = ret + [field.name]
return ret
def all_editable_keys():
ret = []
for field in LDAP_KEY_MAP:
if (field.editable):
ret = ret + [field.name]
return ret
def field_for_key(key):
for field in LDAP_KEY_MAP:
if (field.name == key):
return field
return None
class MyEntry:
widget = None
multiline = 0
def __init__(self,multiline=0):
self.multiline = multiline
if (self.multiline):
self.buffer = gtk.TextBuffer()
self.view = gtk.TextView(self.buffer)
self.widget = gtk.ScrolledWindow()
self.widget.add(self.view)
self.widget.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
self.widget.set_shadow_type(gtk.SHADOW_ETCHED_IN)
else:
self.widget = gtk.Entry()
def get_text(self):
if (self.multiline):
iters = self.buffer.get_bounds()
return self.buffer.get_text(iters[0],iters[1])
else:
return self.widget.get_text()
def set_text(self,text):
if (self.multiline):
self.buffer.set_text(text)
else:
self.widget.set_text(text)
class LdapBackend:
"This class will do all actual ldap communication"
def connect(self,debug=0):
print 'connect to ',self.ldapurl,'with version',self.ldapversion
self.ld = ldap.initialize(self.ldapurl,trace_level=debug)
if (self.ldapversion != None):
self.ld.set_option(ldap.OPT_PROTOCOL_VERSION,self.ldapversion)
if (self.binddn != None and self.bindpw != None):
print 'try user ',self.binddn
try:
self.ld.simple_bind_s(self.binddn, self.bindpw)
except ldap.INVALID_CREDENTIALS:
print 'Invalid ldap user or password'
def __init__(self,cfg,section,debug=0):
self.name = section
self.ldapurl = cfg.get(section, 'ldapurl')
self.baseDN = cfg.get(section, 'base_dn')
try:
self.binddn = cfg.get(section, 'bind_dn')
self.bindpw = cfg.get(section, 'bind_password')
except ConfigParser.NoOptionError:
self.binddn = None
self.bindpw = None
try:
self.ldapversion = int(cfg.get(section, 'ldapversion'))
except (ConfigParser.NoOptionError,ValueError):
self.ldapversion = None
try:
self.addDN = cfg.get(section,'add_dn')
except (ConfigParser.NoOptionError):
self.addDN = self.baseDN
self.connect(debug)
def get_address(self,dn):
filter = dn[:string.find(dn,',')]
try:
entry = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,filter,all_keys())[0][1]
except ldap.SERVER_DOWN:
self.connect(0)
entry = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,filter,all_keys())[0][1]
return entry
def get_dnlist_name(self,name):
if (len(name)>0):
pat = '(|(cn=*'+name+'*)(sn=*'+name+'*)(givenName=*'+name+'*))'
else:
pat = '(cn=*)'
print 'searching for '+pat+' in '+self.baseDN
try:
res = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,pat,['cn'])
except ldap.SERVER_DOWN:
self.connect(0)
res = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,pat,['cn'])
results = {}
i=len(res)-1
print 'found ',len(res),'results'
while (i >= 0):
dn = res[i][0]
cn = res[i][1]['cn'][0]
results[dn] = cn
i -= 1
return results
def get_dnlist_organisation(self,name):
if (len(name)>0):
pat = '(o=*'+name+'*)'
else:
# pat = '(&(cn=*)(o=*))'
pat = '(o=*)'
print 'searching for '+pat+' in '+self.baseDN
try:
res = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,pat,['o'])
except ldap.SERVER_DOWN:
self.connect(0)
res = self.ld.search_s(self.baseDN,ldap.SCOPE_SUBTREE,pat,['o'])
results = {}
i=len(res)-1
while (i >= 0):
dn = res[i][0]
cn = res[i][1]['o'][0]
results[dn] = cn
i -= 1
return results
def saveAddress(self,cn,newaddress):
modlist = []
for key in all_editable_keys():
if (len(newaddress[key]) > 0):
modlist.append((key, newaddress[key]))
modlist.append(('objectClass', 'inetOrgPerson'))
modlist.append(('cn', cn))
dn = 'cn='+cn+','+self.addDN
try:
self.ld.add_s(dn, modlist)
return 1
except ldap.SERVER_DOWN:
self.connect(0)
self.ld.add_s(dn, modlist)
return 1
except ldap.INSUFFICIENT_ACCESS:
return 0
def modifyAddress(self,dn,cn,oldaddress,newaddress):
modlist = []
for key in all_editable_keys():
if (len(newaddress[key]) == 0 and oldaddress.has_key(key) and len(oldaddress[key]) > 0):
modlist.append((ldap.MOD_DELETE,key,()))
else:
modlist.append((ldap.MOD_REPLACE, key, newaddress[key]))
# now check if the dn should be updated
pos = string.find(dn, ',')
if (pos > 0):
new_dn = 'cn='+cn+','+dn[pos+1:]
else:
new_dn = 'cn='+cn+','+self.addDN
try:
self.ld.modify_s(dn, modlist)
if (new_dn != dn):
new_rdn = ldap.explode_dn(new_dn)[0]
self.ld.modrdn_s(dn, new_rdn)
return 1
except ldap.INSUFFICIENT_ACCESS:
return 0
def deleteAddress(self,dn):
try:
self.ld.delete_s(dn)
except ldap.LDAPError, e:
print e
class EditGui:
"This class is the gtk gui for the editor"
def getlistforkey(self,key):
j = len(self.entry[key])
field = field_for_key(key)
lst = []
for i in range(0,j):
tmp = self.entry[key][i].get_text()
if (len(tmp)>0):
lst.append(tmp)
return lst
def getnewaddress(self):
newaddress = {}
for key in all_editable_keys():
newaddress[key] = self.getlistforkey(key)
return newaddress
def saveclicked(self,data):
newaddress = self.getnewaddress()
ret = 0
cn = self.entry['sn'][0].get_text()+ ' '+self.entry['givenName'][0].get_text()
try:
if (self.dn):
self.lb.modifyAddress(self.dn,cn,self.address,newaddress)
else:
self.lb.saveAddress(cn, newaddress)
self.window.destroy()
return
except ldap.SERVER_DOWN:
message = 'address server down'
except ldap.INSUFFICIENT_ACCESS:
message = 'Permission was denied while trying to save the address'
dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT,
type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK,message_format = message)
dialog.set_title('Error')
dialog.connect('response', lambda dialog, response: dialog.destroy())
dialog.show_all()
def plusclicked(self,widget,key):
field = field_for_key(key)
tmp = MyEntry(field.multiline)
self.entry[key].append(tmp)
self.vbox[key].pack_start(tmp.widget)
tmp.widget.show()
def cancelclicked(self,data):
self.window.destroy()
def addfieldentry(self,address,field,table,startat):
# print 'adding ',field.title,field.name,'at startat=',startat
table.attach(gtk.Label(field.title), 0,1,startat, startat+1,xoptions=gtk.FILL)
if (field.editable):
button = gtk.Button('+')
button.connect('clicked', self.plusclicked, field.name)
self.entry[field.name] = []
self.vbox[field.name] = gtk.VBox(True)
table.attach(self.vbox[field.name], 1,2,startat, startat+1,xoptions=gtk.EXPAND|gtk.FILL)
i=0
if (not address.has_key(field.name)):
self.entry[field.name].append(MyEntry(field.multiline))
self.vbox[field.name].pack_start(self.entry[field.name][i].widget)
else:
j = len(address[field.name])
for i in range(0,j):
self.entry[field.name].append(MyEntry(field.multiline))
self.entry[field.name][i].set_text(address[field.name][i])
self.vbox[field.name].pack_start(self.entry[field.name][i].widget)
table.attach(button, 2,3,startat, startat+1,xoptions=gtk.FILL,yoptions=0)
else:
if (address.has_key(field.name)):
table.attach(gtk.Label(address[field.name][0]),1,2,startat, startat+1,xoptions=gtk.FILL)
def __init__(self, lb, dn, address):
self.address = address
self.dn = dn
self.lb = lb
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Edit Address")
self.window.set_border_width(10)
vbox1 = gtk.VBox(False, True)
self.window.add(vbox1)
hbox1 = gtk.HBox(True, True)
vbox1.pack_start(hbox1)
numkeys = len(LDAP_KEY_MAP)
self.ltable = gtk.Table((numkeys/2)+1, 3, False)
self.ltable.set_row_spacings(6)
self.ltable.set_col_spacings(6)
hbox1.pack_start(self.ltable)
self.rtable = gtk.Table((numkeys/2)+1, 3, False)
self.rtable.set_row_spacings(6)
self.rtable.set_col_spacings(6)
hbox1.pack_start(self.rtable)
self.entry = {}
self.vbox = {}
i = 0
for field in LDAP_KEY_MAP:
print field.name
if (i > numkeys/2):
self.addfieldentry(address,field,self.rtable,i-numkeys/2)
else:
self.addfieldentry(address,field,self.ltable,i)
i = i + 1
bbox = gtk.HButtonBox()
bbox.set_layout(gtk.BUTTONBOX_END)
bbox.set_spacing(10)
vbox1.pack_start(bbox)
button = gtk.Button('gtk-cancel')
button.set_use_stock(1)
button.connect('clicked',self.cancelclicked)
bbox.add(button)
button = gtk.Button('gtk-save')
button.set_use_stock(1)
button.connect('clicked',self.saveclicked)
bbox.add(button)
self.window.show_all()
class AddressGui:
"This class is the GTK GUI for the main window"
dn = None
address = None
lb = None
cfg = None
def delete_event(self, widget, data):
return False
def destroy(self, widget, data=None):
gtk.main_quit()
def treevClicked(self,treev,event):
if (event.type == 4):
# 4 seems to be single-click, so we refresh
if (self.dn != None):
self.address = self.lb.get_address(self.dn)
self.set_address_label(self.address)
# 5 seems to be doubleclick, and 6 tripleclick
if (event.type >= 5):
if (self.dn):
ea = EditGui(self.lb,self.dn,self.address)
def newClicked(self,bla):
ea = EditGui(self.lb,'',{})
def deleteClicked(self,bla):
if (self.dn != None):
self.lb.deleteAddress(self.dn)
self.dn = None
self.address = None
self.searchClicked(None)
def get_all(self,entry,prefix,suffix):
str = ''
i=len(entry)-1
if (i >= 0):
while (i >= 0):
str += prefix+self.prepare(entry[i])+suffix
i -= 1
return str
def prepare(self,str):
str = string.replace(str,'&', '&')
str = string.replace(str,'<', '<')
str = string.replace(str,'>', '>')
return str
def set_address_label(self,entry):
print 'set_address_label to ',entry
if (entry.has_key('cn')):
str = ''+self.prepare(entry['cn'][0])+'\n'
if (entry.has_key('mail')):
str += self.get_all(entry['mail'],'','\n')
for key in all_keys():
if (not key in ['cn', 'sn', 'givenName', 'mail','o']):
if (entry.has_key(key)):
str += self.get_all(entry[key],'',' \n')
elif (entry.has_key('o')):
str = ''+self.prepare(entry['o'][0])+'\n'
for key in all_keys():
if (not key in ['o']):
if (entry.has_key(key)):
str += self.get_all(entry[key],'',' \n')
print 'set str to',str
self.label.set_markup(str)
def searchClicked(self, bla):
if (self.ldservers != None):
server = self.ldservers.get_active_text()
if (self.lb.name != server):
print 'switch to new server '+server
self.lb = LdapBackend(self.cfg,server,0)
self.listm.clear()
str = self.entry.get_text()
try:
tmp1 = self.lb.get_dnlist_name(str)
iter = None
for k, v in tmp1.iteritems():
iter = self.listm.prepend()
self.listm.set(iter, 0, v, 1, k)
# print '(cn) adding '+k+' with v='+v
tmp2 = self.lb.get_dnlist_organisation(str)
for k, v in tmp2.iteritems():
if (k not in tmp1):
iter = self.listm.prepend()
self.listm.set(iter, 0, v, 1, k)
print '(o) adding '+k+' with v='+v
else:
print '(o) NOT adding '+k+' with v='+v
if (iter != None):
selection = self.treev.get_selection()
selection.select_iter(iter)
self.treev.grab_focus()
except ldap.SERVER_DOWN:
self.label.set_markup('address server not available')
def closeClicked(self,bla):
self.window.destroy()
def selectionChanged(self, data):
store = data.get_selected()[0]
iter = data.get_selected()[1]
try:
self.dn = store.get_value(iter,1)
self.address = self.lb.get_address(self.dn)
self.set_address_label(self.address)
except:
self.dn = None
self.address = None
self.label.set_markup("no result")
def __init__(self, cfg):
self.cfg = cfg
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Directory Assistant "+VERSIONSTRING)
self.window.set_border_width(10)
try:
pixbuf = gtk.gdk.pixbuf_new_from_file('/usr/local/share/directoryassistant/directoryassistant.png')
self.window.set_icon(pixbuf)
except:
pass
self.window.connect('delete_event', self.delete_event)
self.window.connect('destroy', self.destroy)
self.window.set_border_width(10)
vbox = gtk.VBox(False,10)
self.window.add(vbox)
# the search toolbar
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True)
try:
image = gtk.Image()
image.set_from_file('/usr/local/share/directoryassistant/decoration.png')
hbox.pack_start(image)
except:
pass
self.table = gtk.Table(2, 2, False)
self.table.set_row_spacings(10)
self.table.set_col_spacings(10)
hbox.pack_start(self.table)
label = gtk.Label("Search for")
self.table.attach(label,0,1,0,1)
self.entry = gtk.Entry()
self.entry.connect("activate", self.searchClicked)
self.table.attach(self.entry,1,2,0,1)
if (cfg.numvalidsections >1):
label = gtk.Label("in address book")
self.table.attach(label,0,1,1,2)
self.ldservers = gtk.combo_box_new_text()
for sect in cfg.sections():
if (self.lb == None):
self.lb = LdapBackend(self.cfg,sect,0)
self.ldservers.prepend_text(sect)
self.ldservers.set_active(0)
self.table.attach(self.ldservers,1,2,1,2)
else:
self.ldservers = None
for sect in cfg.sections():
self.lb = LdapBackend(self.cfg,sect,0)
break
# the paned
paned = gtk.HPaned()
vbox.pack_start(paned, True, True)
#the left pane
scrolpane = gtk.ScrolledWindow()
self.listm = gtk.ListStore(str,str)
self.listm.set_sort_column_id(1, gtk.SORT_ASCENDING)
self.treev = gtk.TreeView(self.listm)
scrolpane.set_size_request(150,200)
rend = gtk.CellRendererText()
column = gtk.TreeViewColumn('Results', rend, text=0)
self.treev.append_column(column)
selection = self.treev.get_selection()
selection.set_mode(gtk.SELECTION_SINGLE)
selection.connect('changed', self.selectionChanged)
self.treev.connect('button_press_event',self.treevClicked)
scrolpane.add_with_viewport(self.treev)
paned.add1(scrolpane)
#the right pane
frame = gtk.Frame()
frame.set_shadow_type(gtk.SHADOW_IN)
self.label = gtk.Label()
self.label.set_size_request(250,200)
# self.label.set_justify(gtk.JUSTIFY_LEFT)
# self.label.set_alignment(xalign=0.5, yalign=0.5)
self.label.set_selectable(True)
self.label.set_markup("no results yet");
frame.add(self.label)
paned.add2(frame)
# buttonbox
bbox = gtk.HButtonBox()
bbox.set_layout(gtk.BUTTONBOX_END)
bbox.set_spacing(10)
button = gtk.Button('gtk-delete')
button.set_use_stock(1)
bbox.add(button)
button.connect('clicked', self.deleteClicked)
button = gtk.Button('gtk-new')
button.set_use_stock(1)
bbox.add(button)
button.connect('clicked', self.newClicked)
button = gtk.Button('gtk-close')
button.connect('clicked', self.closeClicked)
button.set_use_stock(1)
bbox.add(button)
button = gtk.Button('gtk-find')
button.set_use_stock(1)
button.connect('clicked',self.searchClicked)
button.set_flags(gtk.CAN_DEFAULT)
self.window.set_default(button)
bbox.add(button)
vbox.pack_start(bbox,False,True)
self.window.show_all()
try:
self.entry.set_text(cfg.get('main', 'startup_search'))
self.searchClicked(None)
except:
pass
self.entry.grab_focus()
def main(self):
gtk.main()
class ErrorMessage:
def quit(self, wid, data):
gtk.main_quit()
def __init__(self, message):
dialog = gtk.Dialog(title="ERROR", flags=gtk.DIALOG_MODAL, buttons=(gtk.STOCK_OK, 1))
label = gtk.Label()
label.set_markup(''+message+'')
dialog.vbox.pack_start(label, True, True, 0)
dialog.connect('close',self.quit)
dialog.connect('response',self.quit)
dialog.show_all()
gtk.main()
class MyConfigParser(ConfigParser.ConfigParser):
"This class will make sure the configfile is correct"
numvalidsections = 0
def section_is_correct(self,section):
if (self.has_option(section,'ldapurl') and self.has_option(section,'base_dn')):
return 1
return 0
def check(self):
filename = os.getenv('HOME')+'/.directoryassistant'
self.read((filename, '/etc/directoryassistant'))
for sect in self.sections():
if (self.section_is_correct(sect)):
self.numvalidsections+=1
else:
self.remove_section(sect)
if (self.numvalidsections >0):
return 1
if (os.path.exists(filename)):
message = 'Your configfile '+filename+' does not contain\nboth the required fields ldapurl and basedn.'
else:
message = 'Your configfile '+filename+' did not yet exist, an empty config file is created, but you have to set the correct values.'
fd = open(filename, 'w')
fd.write("[main]\n#ldapurl = ldap://myserver/\n#bind_dn = cn=myaccount,o=myorg\n#bind_password = mysecret\n#base_dn = ou=Mydepartment,o=myorg\n#startup_search=myname")
fd.close()
em = ErrorMessage(message)
return 0
if __name__ == "__main__":
cfg = MyConfigParser()
if (cfg.check()):
ag = AddressGui(cfg)
ag.main()
else:
print 'Please create '+os.getenv('HOME')+'/.directoryassistant file,\nfollowing the format below:'
print """
[My Address Book]
ldapurl = ldap://your.ldap.server.com/
bind_dn = cn=someaccount,o=someorg
bind_password = yourpassword'
base_dn = ou=department,ou=People,o=someorg"""
directoryassistant-2.0/README 0000644 0001750 0001750 00000002672 10346653223 017350 0 ustar olivier olivier 0000000 0000000 see http://olivier.sessink.nl/directoryassistant/
**About**
Directory Assistant is a small application for managing a LDAP
address book. The focus is to create a very easy to use program,
with only the few but necessary features. The target is novice
users that still need to keep their addresses in an LDAP server.
If you want an advanced application targeted at power users,
check out http://biot.com/gq/.
**Download & install**
Unpack the tarball, cd directoryassistant-1.0/ and run ./install.py.
It will show you the few files that are copied. (note: if it complains
about not having the gtk module installed, and you are sure you have
it, make sure you run the ./install.py script under X)
Configuration is done by config file. Since novice users anyway
don't know how to configure LDAP access, I decided to remove that
from the GUI. The config file is however very simple. An example is
shown below:
[My Ldap Server]
ldapurl = ldap://your.ldap.server.com/
bind_dn = cn=someaccount,o=someorg
bind_password = yourpassword
base_dn = ou=department,ou=People,o=someorg
add_dn = ou=myunit,ou=department,ou=People,o=someorg
ldapversion = 3
startup_search =
if you leave 'startup_search' empty, it will show all ldap entries
The config should be stored in $HOME/.directoryassistant or
/etc/direcoryassistant
**Contact**
I have to make an email adress for this project.. In the meanwhile
contact me at olivier (at) olivier dot pk dot wau dot nl
directoryassistant-2.0/directoryassistant.desktop 0000644 0001750 0001750 00000000432 10346653223 024011 0 ustar olivier olivier 0000000 0000000 [Desktop Entry]
Encoding=UTF-8
Name=Directory assistant
Name[nl]=Adresboek assistent
Comment=LDAP addressbook
Comment[en]=LDAP adresboek
Exec=directoryassistant
Icon=directoryassistant.png
Terminal=false
Type=Application
StartupNotify=false
Categories=GTK;GNOME;Application;Office;
directoryassistant-2.0/directoryassistant.png 0000644 0001750 0001750 00000003205 10346653223 023125 0 ustar olivier olivier 0000000 0000000 PNG
IHDR w= LIDATxmYlWsmgxqd
[Q*a@)E^pQZQ!!7\QqU P/BEBTQIMIJg4$v/x3.*'}:ߑ{=vxGq< B,ͬ;҆TA R
hH$R#QF`=aXR`@k4#IH!цD/cHg$f!C`i*9ڲ5#Va(SD!R|iE_YQhI!+ՋE0qP}bΣs(:E[GH(>џ&yy.fm_XF]fEF+N(VF1qs֯mU66n&3DgNQoQ7x[uvf*$% [͐ j4hZhncEԎ]'W^L1%2Su
K8oI&.ENW81-IP:wL*s/lT$m5H5*Ӏ7O<>P]lpt}mDwg'1"YZ!YT̈́Fu ܀x]~qڕׯڲFJ.w-|01?F/3Y[q]>7p{j4/rMl
z:Y?H*x'̆Wqsa6nBPht+Btvء'_>].2JS)?gm~蕓'9쾏5
(wٴf-_XPiZItPw}iCvB}jA;XHrԵ4x/0vϿ8",c&^4gο^?ѿQ~lYHfdK#]T~3r~$ -TʞB>Zq0Go9RJFh(Rphy%Of}ؕ8'X*c#]ۃضAۻh144Mb7pE&&n]>xᩃizk.(hMCN8.:;=L0U
]<~l?rv0:;]b0ٙ
{{bB;ܮDoA.G4_ep,0y{zEމ0">(Ĺ_>mv݈Fr YbHbLc>VQpߖ]yxi`&FJ4_}GR `mFc2Z!,Vyi:R=ȁU48¤ x^M ff\l!Fl`H),NA# y^8AY(B>@xƲR", !15=S&+$l"ޜ4 HN
x<`7RahEM<k).h
Ciɫcϗ";bITQp' KI
Uta!$6%L6C9a IENDB` directoryassistant-2.0/directoryassistant.1 0000644 0001750 0001750 00000002031 10346653223 022475 0 ustar olivier olivier 0000000 0000000 .TH directoryassistant 1 10-12-2005 directoryassistant directoryassistant
.SH NAME
directoryassistant \- user friendly ldap addressbook frontend
.SH DESCRIPTION
Directory Assistant is a small application for managing a LDAP
address book. The focus is to create a very easy to use program,
with only the few but necessary features. The target is novice
users that need to keep their addresses in an LDAP server.
.SH FILES
Configuration is done by config file. Since novice users anyway
don't know how to configure LDAP access, I decided to remove that
from the GUI. The config file should be at
.I /etc/directoryassistant
or
.I ~/.directoryassistant
and should have the format:
.nf
.sp
[My Ldap Server]
ldapurl = ldap://your.ldap.server.com/
base_dn = ou=department,ou=People,o=someorg
.fi
extended options are:
.nf
.sp
bind_dn = cn=someaccount,o=someorg
bind_password = yourpassword
add_dn = ou=myunit,ou=department,ou=People,o=someorg
ldapversion = 3
startup_search =
.fi
.SH AUTHOR
Directoryassistant is written by Olivier Sessink
directoryassistant-2.0/decoration.png 0000644 0001750 0001750 00000020622 10346653223 021320 0 ustar olivier olivier 0000000 0000000 PNG
IHDR B B T IDATxڭy,]?sNUm3cI!$$EH J@A(("D!B`'V `ϛͼ^UǩxFi[uw-w=woGɛΉs'^Wk
sCAU9"E*4(XTTUyk||[O?wapN'ǏO(rVXj TkjWxxB>BuE{ ~zTǃBb9ZI> wRUlgzT {/ME8!>R?9AbD0fقg`D;VDD ±EC@CƋ?