Jump to content

Help with Script Filter from bash script

Recommended Posts

My goal for my workflow is to run a bash command that uses ldapsearch to search my university's directory and return a list of contacts that operates similar to Alfred's contact search. I've got my bash script running fine, but I'm just unclear on how to format the returned information in the best way. Any guidance on script filtering and bash script returning would be great! I am relatively new at this, so excuse my poor terminology.

Link to comment

I have, I'm just not sure how to go about connecting the dots..I'm pretty green at all of this. Here's my output from the terminal (using generic parameters instead of specific for privacy) : 

command: ldapsearch -h ldap.server.com -x -b "dc=company,dc=com" "uid=*username*" "telephoneNumber" "mail" "uid"

# extended LDIF 
# LDAPv3 
# base with scope subtree 
# filter: uid=*username* 
# requesting: telephoneNumber mail uid 

# username, people, ex.com 
dn: uid=username, ou=people, dc=company, dc=com 
telephoneNumber: +1 111 111-1111 
mail: email 
uid: username 
# search result 
search: 2 
result: 0 Success 
# numResponses: 2 
# numEntries: 1 

 I'd like to be able to return the telephonNumber, mail, and uid values back to alfred similar to the way it does for the built-in contacts. Is this possible? Even better would be to be able to trigger an email from the mail value.

Edited by chrishumphr1es
Link to comment

You have to parse the command output to extract the name, username, email etc. and then reformat each entry as XML that Alfred understands.


If you're new to programming, I'd suggest you try something like Python, PHP or Ruby, which are a lot easier than bash. There are also great libraries that can help you generate results in the format Alfred needs.


Regarding sending an email, make the email the arg value in the output and send it to another script that opens the URL mailto:{query}. {query} is how Alfred inserts the arg into your script.

Link to comment

Thank you for your comments, they have been very helpful.


I actually found a ldap python library online and a few helpful scripts which helped me write a python script that returns the info I need, my only confusion now is reformatting the xml file into the correct alfred format.


Here's my script:


import ldap, sys

if __name__ == "__main__":
	username = "username"
	password= "password"
	# the following is the user_dn format provided by the ldap server
	user_dn = "uid="+username+",ou=people,dc=ex,dc=com"
	# adjust this to your base dn for searching
	base_dn = "dc=ex,dc=com"
	connect = ldap.open(ldap_server)
	search_filter = "uid="+username
		#if authentication successful, get the full user data
		retrieveAttributes = ["mail", "telephoneNumber", "cn"]
		result = connect.search_s(base_dn,ldap.SCOPE_SUBTREE,search_filter,retrieveAttributes)
		# return all user data results
		sys.stdout = open('/tmp/ldapres.xml', 'r+')
		print result
	except ldap.LDAPError:
		print "authentication error"

and here is my xml file:

[('uid=username, ou=people, dc=ex, dc=com', {'telephoneNumber': ['+1 111 111 1111'], 'mail': ['email'], 'cn': ['full name']})]

Any ideas on how to get this into the format alfred needs to display it correctly would be super helpful.


I've replaced my specific login info and output with generics



Edited by chrishumphr1es
Link to comment

See here for info on the file format Alfred requires. See here for how to generate XML with Python. To see a concrete example, look at the Workflow.send_feedback (line 501) and Item.elem (line 70) methods here.


And change this, or it will not work (you need to write your XML output to sys.stdout).

sys.stdout = open('/tmp/ldapres.xml', 'r+')
print result

If you want to write to a file, you do it like this:

open('/path/to/file.xml', 'wb').write(text.encode('utf-8'))

You shouldn't rebind sys.stdout, and if you do, be sure to change it right back again.

Link to comment

Ok great, I think I've fixed it.


My xmlfile looks like this:

<?xml version="1.0"?>
<items><item arg="email" uid="username" valid="YES"><title title="name" /><subtitle subtitle="telephoneNumber :+1 111 111 1111 mail : mail" /></item></items>

Will this work for the script filter? How do I tell the script filter to call the file? I've been reading all of the docs in the forums, and am still a bit stuck.

Link to comment

The XML should look like this:

<?xml version="1.0"?>
    <item arg="email@example.com" uid="username" valid="yes">
        <subtitle>telephoneNumber :+1 111 111 1111 mail : mail</subtitle>
    <item arg="email2@example.com" uid="username2" valid="yes">
        <title>another name</title>
        <subtitle>telephoneNumber :+1 111 111 1111 mail : mail</subtitle>

You have double <items> tags and the contents of <title> and <subtitle> as attributes instead of text between the tags.


If you post the code you're using to generate that output, I might be able to help a bit more.

Edited by deanishe
Link to comment

Ok, thanks so much. Does this go in my script filter? I know my responses are probably frustrating but I am new at this and just trying to figure it out. Please feel free to just not respond if you'd rather not, but you've been helpful so far and I really appreciate it. 


Would my script filter be as follows: sys.stdout.write('/tmp/ldapres.xml'.encode('utf-8')) ?

Link to comment

No, that won't work. That will just send the filename to Alfred, not its contents.


I'm afraid I don't understand exactly what you're doing.


Did you just run your script to generate the list of people once and save the results? Or do you want it to run every time you search with Alfred?


Assuming the former, and that /tmp/ldapres.xml is in the right format for Alfred, you would instead use bash (not Python) and your script filter would be:


cat /tmp/ldapres.xml


However, this won't allow you to search the contacts in Alfred. To do that, your script filter needs to accept Alfred's {query} and use that to filter its list of contacts.

Link to comment

Ok, thanks so much. Does this go in my script filter? I know my responses are probably frustrating but I am new at this and just trying to figure it out. Please feel free to just not respond if you'd rather not, but you've been helpful so far and I really appreciate it. 


Would my script filter be as follows: sys.stdout.write('/tmp/ldapres.xml'.encode('utf-8')) ?


No, that would literally output "/tmp/ldapres.xml".


Just off the top of my head, this should work (assuming /tmp/ldapres.xml is in the Alfred-friendly format @deanishe explained):

sys.stdout.write( open('/tmp/ldapres.xml').read() )

If /tmp/ldapres.xml IS NOT in Alfred's XML format, you would need to load the XML, process into Alfred's format, and return that instead. For example (after you've processed it and saved your variable as an XML string to alfred_xml):

sys.stdout.write( alfred_xml.encode('utf-8') )
Edited by Tyler Eich
Link to comment

I think I've confused everyone pretty well for all of this, haha. Thank you guys for what you've done. I'm just going to post my workflow so I'll can show you what I've got.


My workflow: https://www.dropbox.com/s/ujyop86u2ohercu/LDAPSearch.alfredworkflow - I've removed my login info and just have generic data in place.


My hope is that i could

1. Search my company's ldap database

2. output the data I need into a file alfred can read

3. Be able to list the data similar to how alfred does with spotlighted contacts

4. be able to create a new email message from the listing.


Any feedback you guys have would be great.

Link to comment

Yeah, that's not really how script filters are supposed to work.


I'm afraid I have to go to bed now (it's 1 a.m.), but I'll have a look over it in the morning and rejigger it a bit.


How long does it take to run the script that caches the data from the LDAP server?

Edited by deanishe
Link to comment

So, this should do more or less what you want.


You'll have to edit the ldap_search.py file inside it to add your server address, username, password etc. near the top of the file before it will work.


I've put loads of comments in the Script Filter and source file, so you should be able to see what's going on and alter it to your needs.


Seeing as your server takes several seconds to respond, I created 2 separate actions. Run ldapupdate to download and cache a list of all your contacts on the LDAP server, then ldap [query] (where query is part of a name, email or phone number) to search the cached contacts.

Edited by deanishe
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...