dynamic yad lists possible? - zenity

I wonder if anyone more familiar with yad (yet another dialogue) knows if dynamic lists are possible and if so how might one be implemented using the contents of a bash array assigning FALSE to each list item?
I have a script that manages multiple LUKS partitions, and so when user selects to mount a partition they are presented with a list of partitions not yet mounted from which to make their next selection. So the upshot is that they cannot select a mount point already mounted.
This is currently managed by presenting the user the list in a terminal, but I would like to try using a yad list dialogue if dynamic lists are possible.
From the scant pages detailing yad usage online I could not find anything regarding dynamic lists, leading me to believe this is will quite likely not be possible.

Taken from zenity example here you could do:
devsToMnt=$(for f in "${part_list_array[#]}" ; do echo FALSE ; echo "$f" ; done | yad --list --center --height="400" --checklist --column="Select" --column="Device to mount:" --print-column="2" --multiple --separator=" ")
and whatever partitions the user chooses will be available in $devsToMnt

Based on the example on the wiki, I would presume that something to the effect of
mounts=( "foo" "bar" "baz" )
mp=$(yad --width 300 --entry --title "Mount?" \
--button="Mount it." \
--text "Choose mountpoint:" \
--entry-text \
"${mounts[#]}")
echo "let's mount $mp"
should work.

Related

ZSH completion - Multiple items per argument

I am attempting to write a completion plugin for YunoHost. I am struggling with the following case, where an argument, optional or not, can take multiple values:
ynh app addaccess apps [apps ...] [-u [USERS [USERS ...]]]
Typical usage:
ynh app addaccess foo-app1 bar-app2 -u barfoo1 foobar2
I've managed to get suggestions for those two parameters apps and USERS with the following code but I can't have a behavior coherent with what the command can handle.
(_ynh_app_list and _ynh_users_list are calls to compadd)
_yunohost_app_addaccess() {
_arguments -s -C \
'1:apps:_ynh_app_list' \
'*'{-u,--users}'[users]:users:_ynh_users_list'
}
The code above kinda works, except:
after entering a single app name, it switches to the users.
-u takes a single user (multiple -u instances are valid though)
I tried *:apps:_ynh_app_list, but ynh app addaccess foo-app1 -u user1 <TAB> calls _ynh_app_list instead of _ynh_users_list
What I would like to get is:
ynh app addaccess <TAB> shows the completions provided by __ynh_app_list
ynh app addaccess foo-app1 <TAB> still shows the completions provided by __ynh_app_list
As soon as -u is entered, and whatever the number of words after -u, all completion suggestions should come from __ynh_users_list: yunohost app addaccess foo-app1 bar-app2 -u barfoo1 foobar2 <TAB> still completes with a username form __ynh_users_list
Is it possible to achieve this, at least the last item ([-u USER [USER...]])? Thanks a lot! :)
To use a list of exclusion option/argument functionality could rescue:
_yunohost_app_addaccess() {
_arguments -s -C \
{1,'*'}:apps:_ynh_app_list \
'(*)'{-u,--users}'[users]:*:users:_ynh_users_list'
}
The ynh addaccess app [APPS ...] portion could be done by the normal argument SPECs; 1:MESSAGE:ACTION and *:MESSAGE:ACTION.
Shorthanded by a brace expansion: {1,'*'}:apps:_ynh_app_list
The [-u USER [USER...]] portion could be done by the OPTSPECs with a preceded list of exclusions and :*PATTERN:MESSAGE:ACTION with empty PATTERN; '(*)-OPTNAME[EXPLANATION]:*:MESSAGE:ACTION'.
Here is the zsh manual for the list of exclusions for a reference:
_arguments ...
...
Each of the forms above may be preceded by a list in parentheses of option names and argument numbers. If the given option is on the command line, the options and arguments indicated in parentheses will not be offered. For example, (-two -three 1)-one:... completes the option -one; if this appears on the command line, the options -two and -three and the first ordinary argument will not be completed after it. (-foo):... specifies an ordinary argument completion; -foo will not be completed if that argument is already present.
Other items may appear in the list of excluded options to indicate various other items that should not be applied when the current specification is matched: a single star (*) for the rest arguments (i.e. a specification of the form *:...); a colon (:) for all normal (non-option-) arguments; and a hyphen (-) for all options. For example, if (*) appears before an option and the option appears on the command line, the list of remaining arguments (those shown in the above table beginning with *:) will not be completed.
--- zshcompsys(1), _arguments, zshcompsys - zsh completion system

Any way to filter email dynamically by taking 'from' and matching it with database ? (Using procmail or Virtualmin or Webmin)

I basically want to check the incoming 'From' in the email received and then either
Keep it and make it deliver to the intended mailbox if the email matches a Specified MySQL/PostgreSQL
Database User (eg. select email from users where exists ('from email address') )
If the 'From' address is blank or it is not found in the database, the email should be discarded
Any way I can achieve this before the e-mail is delivered to the intended mailbox?
I am using Procmail + Virtualmin + Webmin + PostgreSQL
PS: I want to apply this filter not to the wole server but to some specified mailboxes/users (i'm assuming 1 user = 1 mailbox here)
Procmail can easily run an external command in a condition and react to its exit status. How exactly to make your particular SQL client set its exit code will depend on which one you are using; perhaps its man page will reveal an option to make it exit with an error when a query produces an empty result set, for example? Or else write a shell wrapper to look for empty output.
A complication is that Procmail (or rather, the companion utility formail) can easily extract a string from e.g. the From: header; but you want to reduce this to just the email terminus. This is a common enough task that it's easy to find a canned solution - generate a reply and then extract the To: address (sic!) from that.
FROM=`formail -rtzxTo:`
:0
* FROM ?? ^(one#example\.com|two#site\.example\.net|third#example\.org)$
{
:0
* ? yoursql --no-headers --fail-if-empty-result \
--batch --query databasename \
--eval "select yada yada where address = '$FROM'"
{ }
:0E
/dev/null
}
The first condition examines the variable and succeeds if it contains one of the addresses (my original answer simply had the regex . which matches if the string contains at least one character, any character; I'm not convinced this is actually necessary or useful; there should be no way for From: to be empty). If it is true, Procmail enters the braces; if not, they will be skipped.
The first recipe inside the braces runs an external command and examines its exit code. I'm imagining your SQL client is called yoursql and that it has options to turn off human-friendly formatting (table headers etc) and for running a query directly from the command line on a specific database. We use double quotes so that the shell will interpolate the variable FROM before running this command (maybe there is a safer way to pass string variables which might contain SQL injection attempts with something like --variable from="$FROM" and then use that variable in the query? See below.)
If there is no option to directly set the exit code, but you can make sure standard output is completely empty in the case of no result, piping the command to grep -q . will produce the correct exit code. In a more complex case, maybe write a simple Awk script to identify an empty result set and set its exit status accordingly.
Scraping together information from
https://www.postgresql.org/docs/current/app-psql.html,
How do you use script variables in psql?,
Making an empty output from psql,
and from your question, I end up with the following attempt to implement this in psql; but as I don't have a Postgres instance to test with, or any information about your database schema, this is still approximate at best.
* ? psql --no-align --tuples-only --quiet \
--dbname=databasename --username=something --no-password \
--variable=from="$FROM" \
--command="select email from users where email = :'from'" \
| grep -q .
(We still can't use single quotes around the SQL query, to completely protect it from the shell, because Postgres insists on single quotes around :'from', and the shell offers no facility for embedding literal single quotes inside single quotes.)
The surrounding Procmail code should be reasonably self-explanatory, but here goes anyway. In the first recipe inside the braces, if the condition is true, the empty braces in its action line are a no-op; the E flag on the next recipe is a condition which is true only if any of the conditions on the previous recipe failed. This is a common idiom to avoid having to use a lot of negations; perhaps look up "de Morgan's law". The net result is that we discard the message by delivering it to /dev/null if either condition in the first recipe failed; and otherwise, we simply pass it through, and Procmail will eventually deliver it to its default destination.
The recipe was refactored in response to updates to your question; perhaps now it would make more sense to just negate the exit code from psql with a ! in front:
FROM=`formail -rtzxTo:`
:0
* FROM ?? ^(one#example\.com|two#site\.example\.net|third#example\.org)$
* ! ? psql --no-align --tuples-only --quiet \
--dbname=databasename --username=something --no-password \
--variable=from="$FROM" \
--command="select email from users where email = :'from'" \
| grep -q .
/dev/null
Tangentially, perhaps notice how Procmail's syntax exploits the fact that a leading ? or a doubled ?? are not valid in regular expressions. So the parser can unambiguously tell that these conditions are not regular expressions; they compare a variable to the regex after ??, or examine the exit status of an external command, respectively. There are a few other special conditions like this in Procmail; arguably, all of them are rather obscure.
Newcomers to shell scripting should also notice that each shell command pipeline has two distinct results: whatever is being printed on standard output, and, completely separate from that, an exit code which reveals whether or not the command completed successfully. (Conventionally, a zero exit status signals success, and anything else is an error. For example, the exit status from grep is 0 if it finds at least one match, 1 if it doesn't, and usually some other nonzero exit code if you passed in an invalid regular expression, or you don't have permission to read the input file, etc.)
For further details, perhaps see also http://www.iki.fi/era/procmail/ which has an old "mini-FAQ" which covers several of the topics here, and a "quick reference" for looking up details of the syntax.
I'm not familiar with Virtualmin but https://docs.virtualmin.com/Webmin/PostgreSQL_Database_Server shows how to set up Postgres and as per https://docs.virtualmin.com/Webmin/Procmail_Mail_Filter I guess you will want to use the option to put this code in an include file.

cURL filling up an HTML form with tcl

I need to make a Tcl program that logs into a web page and i need to fill up some information and get some information.
The page has lots of forms with diferent types of input, radio/check buttons, entry strings etc the usual.
i can log into the page no problem and fill up the forms without a problem but i have to fill EVERY input for that particular form or else it will be save as empty (the things i didnt specify)
Heres an example:
this is the form:
--- FORM report. Uses POST to URL "/goform/FormUpdateBridgeConfiguration"
Input: NAME="management_ipaddr" (TEXT)
Input: NAME="management_mask" (TEXT)
Input: NAME="upstr_addr_type" VALUE="DHCP" (RADIO)
Input: NAME="upstr_addr_type" VALUE="STATIC" (RADIO)
--- end of FORM
and this is the command i use to fill it up
eval exec curl $params -d upstr_addr_type=STATIC https://$MIP/goform/FormUpdateBridgeConfiguration -o /dev/null 2> /dev/null
where params is:
"\--noproxy $MIP \--connect-timeout 5 \-m 5 \-k \-S \-s \-d \-L \-b Data/curl_cookie_file "
yes i know is horrible but it is what it is .
In this case i want to change the value of upstr_addr_type to STATIC but when i sumit it i lose the info from management_ipaddr and management_mask.
This is a small example, i have to do this for every form and a gizillion more variables so its a real problem for me.
i figure its concept problem or something like that, i look and look and look some more, try -F -X GET -GET -almost every thing on cURL manual, can someone guide me here
If you know what the values of management_ipaddr and management_mask should be, you can just supply them as extra -d arguments. It probably makes sense to wrap this in a procedure
proc UpdateBridgeConfiguration {management_ipaddr management_mask upstr_addr_type} {
global MIP params
eval exec curl $params \
-d management_ipaddr=$management_ipaddr \
-d management_mask=$management_mask \
-d upstr_addr_type=$upstr_addr_type \
"https://$MIP/goform/FormUpdateBridgeConfiguration" \
-o /dev/null 2> /dev/null
# You ought to replace the first line of the above call with:
# exec curl {*}$params \
# Provided you're not on Tcl 8.4 or before...
}
Like that, you'll find it much easier to get the call correct. (You shouldn't need to specify -X POST for this; it's default behaviour when -d is provided.)
To get the existing values, you'll need to GET them from the right URL (which I can't guess for you) and extract them from the resulting HTML. Which might involve using a regular expression against the retrieved document. This is pretty awful, but it's what you're stuck with sometimes. (You can use tDOM to parse HTML properly — provided it isn't too ill-formed — and then use its XPath support to query for the values correctly, but that's rather more complex and introduces a dependency on an external package.) Knowing what the right RE to use is can be tricky, but it is likely to involve grabbing a copy of the form and doing something vaguely like this:
regexp -nocase {<input type="text" name="management_ipaddr" value="([^<"">]*)"} $formSource -> management_ipaddr
regexp -nocase {<input type="text" name="management_mask" value="([^<"">]*)"} $formSource -> management_mask
While in general it could be encoded all sorts of ways, that's very unlikely for IP addresses or masks! On the other hand, the order of the attributes can vary; you have to customize your RE to what you're really dealing with, not merely what it might be…
The curl invokation will be something like
set formSource [exec curl "http://$MIP/the/right/url/here" {*}$params 2>/dev/null]
It's much simpler when you're not having to send data up and you want to consume the result.

How do you search by DN in LDAP?

I'm pulling information about a user from LDAP. This includes directReports, which is in the full CN=cnBlah, OU=ouBlah, DC=dcBlah form. I'm trying to do another lookup to find info about the reportee.
So far the only way I've been able to actually find said user is to break out the CN= and set the remainder of the string as the base.
Is this the proper way of doing it? Or is there a way to search for an entry given the full DN?
Use the DN as the base object in the search and set the scope of the search to base.
Calling ldapsearch with the -f option would do pretty much what you want.
Save your first search results to a file, with only the value of the cn attribute. For example, your file would look like this :
users.txt:
user1
user2
cnBlah
john
jim
user883
Then call ldapsearch with a base that is high enough to encompass all users. This could be -b dc=users,dc=example,dc=com.
So if you saved your user list to a file named users.txt, your ldapsearch command line would look like this :
#I removed the hostname, port and authentification for clarity
ldapsearch -b "dc=users,dc=example,dc=com" -s sub "cn=%s" -f users.txt -LLL
Long lines will wrap at ~76 characters. Nothing that a pipe through perl -p00e 's/\r?\n //g' can't fix. (Or just add option -o ldif-wrap=no to your ldapsearch commandline.)
Closing the loop on this question, courtesy of https://www.openldap.org/lists/openldap-software/200503/msg00520.html
When you know the DN of an entry, there is no need to "search" for it all, just retrieve the entry directly:
ldapsearch -x -LLL -b "uid=droy,ou=people,dc=eclipse,dc=org"
So that answers the "how do you use ldapsearch to lookup() an item rather than search for it"

how to get list of POSIX group members in Perl

Is there any way to get a list of all the members of a POSIX group in Perl?
I can't use getgrent() and similar because it returns the list as a space delimited string, and some usernames can have spaces in them.
I have to handle spaces in user and group names, because I'm working in an AD environment that other organizations can create users and groups in, so I'm trying to account for possible edge cases.
I'd say just use getgrent() and don't worry about spaces.
It may be possible to create a user name with one or more spaces in it, perhaps by manually editing /etc/passwd, but it's going to cause other problems as well. For example, ~foo is foo's home directory, but ~foo bar isn't foo bar's home directory.
On Linux, the useradd and adduser commands don't even permit spaces in file names. On Linux Mint 14 (based on Ubuntu 12.10):
$ sudo adduser 'foo bar'
adduser: To avoid problems, the username should consist only of
letters, digits, underscores, periods, at signs and dashes, and not start with
a dash (as defined by IEEE Std 1003.1-2001). For compatibility with Samba
machine accounts $ is also supported at the end of the username
$ sudo useradd !$
sudo useradd 'foo bar'
useradd: invalid user name 'foo bar'
$
Do you actually have user names with spaces on your system?
UPDATE: I've found that it actually is possible to create user names with spaces. useradd and adduser don't allow it (and you should be using one of those commands, or something similar, to create new accounts). But if I manually edit /etc/passwd using sudo vipw, I can create a user named foo bar, and I can do:
su - 'foo bar'
ssh 'foo bar#localhost'
etc. But it's a Really Bad Idea. Perl's getgr*() cannot tell whether a group contains one entry for foo bar or two entries for foo and bar (which is what you're asking about), and I can't use the shell's ~name syntax to refer to the account's home directory. I could use other methods to get both pieces of information, but it's much easier to avoid creating such an account in the first place.
If you're seriously concerned about some admin being foolish enough to create such an account, then you can use some of the alternative methods that have been discussed. But as I said, I don't think it's worth the effort.
(Perl could have avoided this problem by delimiting the list with : characters rather than spaces, since those are actually incompatible with the format of /etc/passwd and /etc/group, which the system depends on. But it's too late to change it now.)
UPDATE 2:
As you say in a comment (which I've edited into your question):
I have to handle spaces in user and group names, because I'm working in an AD environment that other organizations can create users and groups in, so I'm trying to account for possible edge cases.
Your solution from the same comment:
map((getgrgrid($_))[0], split(/ /, `id -G $username`))
is probably the best workaround. (id -G prints numeric group ids; which obviously can't contain spaces.)
It's probably also worth checking whether you actually have user or group names with spaces in them (though of course that doesn't guard against such names being added in the future). I wonder how your POSIX system actually deals with such names. I wouldn't be astonished if they're automatically translates them somehow. Even so, your id -G solution will still work.
If I have /etc/group:
...
postgres:x:26:
fsniper:x:481:
clamupdate:x:480:
some spacey group:x:482:saml, some spacey user
I can use the following commands to see this group's members:
% getent group
...
postgres:x:26:
fsniper:x:481:
clamupdate:x:480:
some spacey group:x:482:saml,some spacey user
Or if you know the specific group that you're interested in:
% getent group "some spacey group"
some spacey group:x:482:saml,some spacey user
These could be wrapped inside of a Perl script like this:
#!/usr/bin/perl
use feature qw(say);
chomp (my $getent = `getent group "some spacey group" | sed 's/.*://'`);
my #users = split(/,/, $getent);
foreach my $i (#users) { say $i; }
Running it:
% ./b.pl
saml
some spacey user
Resources
How to list all users in a Linux group?