Коллекции значков. Флаги всех стран.

Коллекция #1

Коллекция флагов стран в двух исполнениях
Flat — обычные плоские значки и Shiny — слегка выпуклые, с отблеском

Разрешения:
-16×16
-24×24
-32×32
-48×48
-64×64

Форматы:
-PNG
-ICO
-ICNS

Наименования отдельно:
-По названию страны
-По коду ISO

Дополнительно:
Есть флаги некоторых непризнанных республик и международных организаций.

Скачать с Mega.nz
Скачать с Google.Drive

Коллекция #2

Иконки плоские

Разрешения:
-32×32
-128×128

Форматы:
-PNG

Наименования:
-По коду ISO

Скачать с Mega.nz
Скачать c Google.Drive

Calyx.net. Еще один бесплатный VPN

На этот раз от некоей научной конторы Calyx Institute, изначально заточенный под использование клиента Bitmask, но ключи и сертификаты также можно выкачать скриптом из предыдущей заметки (копия)

Конфиг

На PasteBin

Архив с конфигом и сертификатами

На Mega.nz
На Google.Drive

Ссылки

Calyx.net
Calyx Institute

Скрипт для обновления ключей для VPN RiseupVPN без Bitmask и клиента Riseup

Преамбула

Единственная проблема, которая возникает с Riseup VPN — пользовательские ключи периодически протухают (они обновляются примерно раз в 3-4 месяца). Уж не знаю, для чего это сделано, но факт остается фактом. Клиент Bitmask, понятно что, обновляет ключи автоматом, но работает только на астероиде с конкретным давлением и гравитацией. А нам такое не надо.

Сам клиент Bitmask на самом деле реализует SRP-авторизацию для последующего получения пользовательского сертификата VPN, однако, админ сервера может выдавать и анонимный ключ, без использования авторизации. С riseup.net несказанно повезло, что гайдам от разработчиков Bitmask, они все-таки (пока) последовали не до конца, и спокойно выдают ключ без авторизации и регистрации.

Так что Bonafide можно реализовать частично, опустив шаги регистрации пользователя и всю работу с SRP-авторизацией. Соответственно, остальное без проблем делается с помощью средств shell/bash и нескольких дополнительных утилит.

Ремарка про сам Bitmask

Такое ощущение, что проект медленно дохнет. Из 4 провайдеров, работающих через клиент Bitmask, осталось только два: Riseup, который даже сделал свой отдельный клиент по образу и подобию, и calyx.net, не очень понятный проект от какой-то мелкой американской научной конторы https://www.calyxinstitute.org/, впрочем, тоже предоставляющий бесплатный VPN. А вот колумбийский VPN (всегда мечтал попробовать что-нибудь колумбийское) и единственный платный Codigosur сдохли. И самое печальное, что сдох демо-сервер от самого Bitmask. Который позволялось использовать для отладки своих клиентов под Bitmask-протокол.

Немного про алгоритм скрипта

1. Нам необходимо выкачать из корня сайта провайдера файл provider.json. Там содержится основная информация о провайдере.
2. Далее вытаскиваем из provider.json "api_uri» и "api_version". Два этих значения (api_uri + api_version) составляют API_BASE, т.е., например, для Calyx "api_uri": "https://api.calyx.net:4430" и "api_version": "1", соответственно API_BASE будет https://api.calyx.net:4430/1
3. Выгружаем себе корневой сертификат провайдера (самоподписанный) из «ca_cert_uri» (для Calyx будет https://calyx.net/ca.crt)
4. Получаем его fingerprint из "ca_cert_fingerprint". Отпечаток сертификата SHA256 (у всех двух виденных именно такой)
5. Считаем отпечаток скачанного сертификата с помощью openssl:

openssl x509 -in $WORKDIR"/"$CACRTFILE -noout -fingerprint -sha256 -inform pem

— делаем необходимые строковые преобразования:

openssl x509 -in $WORKDIR"/"$CACRTFILE -noout -fingerprint -sha256 -inform pem|awk -F = '{print tolower($2)}'|tr -d ':'

и сравниваем то, что получили из сертификата с прочитанным из provider.json должно совпадать.

6. Теоретически, с этого момента вся работа с дальнейшим API должна вестись с этим сертификатом, но для простоты можно все запросы делать curl с параметром --insecure.

7. Можно выкачать список конфигов сервиса, оформив GET-запрос на адрес API_BASE/configs.json. Список выглядит примерно так:

{
  "services":{
    "soledad":"/1/configs/soledad-service.json",
    "eip":"/1/configs/eip-service.json",
    "smtp":"/1/configs/smtp-service.json"
  }
}

8. На самом деле из интересного здесь только параметр "eip", это ссылка на конфиги VPN. Обычно перечисляются сервера и какие-то критичные параметры конфига. Так, например, выглядит текущий eip-service.json для Riseup:

На PasteBin

9. Теперь пора получать пользовательский сертификат (и ключ), который будет использоваться для авторизации в VPN, и для этого вообще-то надо логиниться по SRP, но два оставшихся провайдера — Riseup и Calyx, позволяют запрос сделать так.

Делаем POST-запрос (пустой) к API_BASE/cert.

10. Сохраняем результат.

Для парсинга JSON из консоли используется jq, установите для своего дистрибутива, если у вас еще нет.

Параметры запуска скрипта

Use bminfo <-p> <provider> [KEYS]

Обязательных параметра 2 — первый ключ (-p) и провайдер VPN (riseup.net, например).

Остальные параметры ([KEYS]):

--getinfo — получить только provider.json
--clear — очистить рабочий каталог
--getconfigs — получить provider.json, configs.json и eip config
--providerkey — получить корневой сертификат провайдера VPN (cacert.pem)
--userkey — получить сертификат пользователя (openvpn.pem)
--copy — скопировать сертификаты и файлы json в каталог из $OUTDIR/<provider>
--check — скопировать только файлы, которые были изменены

Можно использовать в сочетании друг с другом все ключи, кроме --clear.

Примеры:
Получение всей информации и всех ключей

bminfo -p calyx.net --getconfigs --providerkey --userkey

Получение только пользовательского ключа:

bminfo -p calyx.net --userkey

Код скрипта

На GitHub

Полезные ссылки

riseup.net
SRP-6: аутентификация без передачи пароля
Bonafide. Secure user registration, authentication, and provider discovery.

Бесплатный VPN RiseupVPN, обновление.

Новости

Новости, правда, не первой свежести, но все же. О бесплатном анархо-VPN от riseup.net мы писали ранее копия.

С тех пор многое поменялось.

+ Клиент для Windows. Да, пользователи 7 и 10 Windows могут радоваться. Никакого шаманства, настроек и ужасов. Скачал клиент и пользуйся. Клиент, на самом деле, форк проекта Bitmask, все настраивает сам, скачивает необходимые ключи, запускает Openvpn, которую даже скачивать отдельно не надо, обрезанная версия идет в комплекте и ставится вместе с клиентом. Регистрация тоже не нужна.




Британия вместо Франции потому что я себе БД SxGeo не обновил, у них там в связи с брекзитом черт ногу сломит

+ Добавилось серверов. Кроме старых голландского, канадского и штатовского сервера добавились 4 французских, еще один штатовский и гонконгский.

В общем, проект живет и развивается, здоровья ребятам и творческих успехов.

На момент написания заметки голландский сервер что-то глючил. Соединение есть, а толку нет (да, я перепроверил конфиги и настройки раз 10 — остальные работают, этот соединяется, но ни бе ни ме ни кукареку). И да, он взял и сменил IP
Опять обидели пользователей других дистрибутивов линукс. RiseupVPN is currently tested on the Ubuntu LTS and Debian Stable. If you have a different release, it may or may not work. Хотя, может и не обидели. Поскольку у «клиента» внутри один хрен неонка OpenVPN, то главное — написать правильные конфиги и скачать ключи, благо качаются они без всякого геморроя, в следующей заметке будет скрипт по выкачиванию и обновлению сертификатов.
Немного сменились конфиги. Дело поправимое.
У пользователей официального клиента нет возможности выбора конкретного сервера. А алгоритм выбора сервера от OpenVPN оставляет желать лучшего (ИМХО, его там нет вообще).

Конфигурационные файлы для OpenVPN

На mega.nz
На Google.Drive

Калькулятор пропорций

Писал вместе со знакомым школьником, и для его же задания по информатике. А ведь сам давно хотел подобную программу, ибо пропорции последнее время приходится считать часто (химия и проценты всяческие), а с арифметикой я абсолютно не дружу, то и подвигло к освоению компьютера — ему объяснил, как считать, и самому можно не заморачиваться.

Особо каких-то отдельных вещей в программе нет (ввод чисел в TextBox объяснял ранее). Но тем не менее, может еще кому пригодится.

Скриншоты



+3

Исходники

Репозиторий на GitHub

Скачать

Готовую программу
Установщик

C# Ввод отрицательных чисел в TextBox

Преамбула

Продолжаем разговор о вводе чисел в TextBox. Сегодня будем вводить отрицательные числа, тобишь к числу (точнее строке) надо будет пририсовывать знак «минус».

Главное

Раз уж мы будем что-то в TextBox добавлять, то изменится длина строки в TextBox, а, соответственно текстовый курсор (|) перепрыгнет с позиции, на которой он находился, на следующую или предыдущую, что создаст неудобство пользователю. Это надо побороть.

Позицию курсора можно вытащить из свойства TextBox.SelectionStart, посему сохраним его перед началом всех действий с текстом в отдельную переменную:

int pos = txt.SelectionStart;

Теперь можно вводить знак числа.

Ввод знака числа

Смотрим, какой символ был введен. Если минус, то проверяем, был ли в начале строки минус. Был — убираем, не было — добавляем. В зависимости от того, убрали или добавили символ, корректируем местоположение текстового курсора, добавляя или удаляя позицию, если, соответственно, убрали или добавили символ.

//ввод минуса
if (e.KeyChar == '-')
{                
    if (txt.Text.StartsWith("-"))
    {
        txt.Text = txt.Text.Substring(1);
        txt.SelectionStart = pos - 1;
    }
    else
    {
        txt.Text = "-" + txt.Text;
        txt.SelectionStart = pos + 1;
    }

    e.Handled = true;
    return;
}

Дополнительно. Добавление лидирующего ноля.

Таким же образом можно добавлять лидирующий ноль, если пользователь начал ввод дробного числа с точки:

//ввод точки (запятой)
if ((txt.Text.StartsWith(".")) || (txt.Text.StartsWith(",")))
{
    // добавление лидирующего ноля
    txt.Text = "0" + txt.Text;
    txt.SelectionStart = pos + 1;
}

if ((e.KeyChar == '.') || (e.KeyChar == ','))
{                
    if (txt.Text.Contains(".") || txt.Text.Contains(","))
    {
        e.Handled = true;
        return;
    }                

    return;
}

Вся функция целиком

На PasteBin

Дополнительно

1. Ввод в текстовое поле только цифр Копия
2. Ввод дробных чисел в текстовое поле Копия

Общий пример для всех заметок

— Ввод цифр
— Ввод чисел с дробной частью
— Ввод отрицательных чисел с дробной частью и добавление лидирующего ноля
— Дополнительно — пример конверсии вводимых строк в числа, обработка ошибок при конвертации.

На GitHub

Устранение глюка с find

Преамбула

Оказалось, в моей функции create_list() копия был обнаружен малозаметный и посему дико поганый баг. А баг был с командой find. Например, если запустить ее в любом каталоге с параметрами

find /home/smallwolfie/openvpn/configs -maxdepth 1 -iname *.ovpn, то она найдет все файлы *.ovpn

Но вот если команду запустить в каталоге с файлами (/home/smallwolfie/openvpn/configs), то она упадет:

cd /home/smallwolfie/openvpn/configs
find /home/smallwolfie/openvpn/configs -maxdepth 1 -iname *.ovpn

Вывод:

find: paths must precede expression: riseup-nl.ovpn
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

Решение

Заключить маску файла в кавычки:

find /home/smallwolfie/openvpn/configs -maxdepth 1 -iname "*.ovpn"

Вывод:

/home/smallwolfie/openvpn/configs/vpnbook-pl226-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-de4-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-us1-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-us2-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-ca222-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-ca198-tcp80.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-fr1-tcp80.ovpn
/home/smallwolfie/openvpn/configs/riseup-ca.ovpn
/home/smallwolfie/openvpn/configs/riseup-nl.ovpn
/home/smallwolfie/openvpn/configs/riseup-us.ovpn
/home/smallwolfie/openvpn/configs/vpnbook-fr8-tcp80.ovpn

Обновленная функция целиком

Пример из файла скрипта для соединения с определенным VPN-сервером

create_list() #$1 - dir, $2 - file mask
{
    FOUNDLST=""
    echo "Find in $1"
    for FLE in $(find $1 -maxdepth 1 -iname "$2"|sort); do
	if [ -n "$FLE" ]; then
	    FOUNDLST="$FOUNDLST"`basename $FLE`"\n"
	fi
    done
}

Обновленный скрипт

На GitHub

C#. Ввод в TextBox чисел с дробной частью.

Т.е. нам необходимо ограничить ввод в текстовое поле (TextBox) только цифрами и одной точкой (и/или одной запятой). Далее пример кода (обработчика событий KeyPress), который позволяет вводить цифры и одну точку (или одну запятую).

private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
    //ввод только цифр с одной точкой (запятой)
    if ((e.KeyChar == '.') || (e.KeyChar == ','))
    {
        TextBox txt = (TextBox)sender;
        if (txt.Text.Contains(".") || txt.Text.Contains(","))
        {
            e.Handled = true;
        }
        return;
    }

    if (!(Char.IsDigit(e.KeyChar)))
    {
        if ((e.KeyChar != (char)Keys.Back))
        {
            e.Handled = true;
        }
    }
}

На PasteBin

Дополнительно

Чтобы не заморачиваться, точка у вас или запятая, в строке, содержащей число с дробной частью, можно делать так:

using System.Globalization;
//...

public static double ToDouble(string Number)
{
    Number = Number.Replace(',', '.');
    NumberFormatInfo format = new NumberFormatInfo();
    format.NumberDecimalSeparator = ".";
    return Convert.ToDouble(Number, format);
}

На PasteBin

Класс Convert, к сожалению, зависит от языковых настроек системы, и если в качестве разделителя дробной и целой части в системе указана запятая, а в числе будет точка (или наоборот), то Convert.ToDouble(<число>) свалится с ошибкой.

Всех сопричастных с Днем программиста (и с Пятницей 13)!

Установка PHP в любой каталог, а не только в C:\php

Заметка от склероза, как обычно.

1. Качаем PHP и распаковываем архив в нужный каталог. Пусть для примера будет D:\Software\php
2. Копируем свой php.ini в этот каталог, или php.ini-development переименовываем в php.ini
3. Находим в секции [PHP] параметр extension_dir и меняем его значение на D:\Software\php\ext
4. Создаем в подкаталоге php каталог tmp, для временных файлов PHP
5. В секции [PHP] находим параметр sys_temp_dir и меняем значение на D:\Software\php\tmp

extension_dir = "D:\Software\php\ext"
sys_temp_dir = "D:\Software\php\tmp"

PHP для Windows слэши в путях (прямой или обратный) не важны, правильно переваривается и тот и тот:

extension_dir = "D:/Software/php/ext"
sys_temp_dir = "D:/Software/php/tmp"

6. Раскомментируем нужные расширения, например:

extension=bz2
extension=fileinfo
extension=gd2
extension=gettext

7. Если в проекте планируется использовать cURL, то добавляем в переменную окружения PATH каталоги с php.ini (D:\Software\php) экзешником curl.exe и библиотекой php_curl.dll

Источники

1. Установка Apache 2.4VC14 + PHP 7 на Windows 7 ­- 10
2. Настройка curl php на Windows

Автоматическое получение конфигов и пароля к VPN от vpnbook, теперь и для Windows.

По многочисленным просьбам зрителей, сделал римейк собственных недавних скриптов (копия копия), только теперь для Windows.
Написал небольшую программулину на C#, которая делает то же самое, что и вышеописанные скрипты.
— для распаковки ZIP-архивов использовал библиотеку DotNetZip, она же Ionic.Zip (копия)
— а для распознавания пароля на картинке, не мудрствуя лукаво, вызвал tesseract, естественно, версию под Windows.
Она в архиве с готовыми бинарниками самой программы, единственное, что может потребоваться, это поставить VCRedist для Visual C++ 2015

Скриншоты


Получение пароля

Остальные под катом

Скачать

Программу
— Библиотеки для работы tesseract:
Visual C++ Redistributable for Visual Studio 2015 (c сайта Microsoft)
vc_redist.x86.exe
vc_redist.x64.exe

Исходники

На GitHub

C#. Простой парсинг HTML Regerp’ом.

Преамбула

Да, предваряя камни, которые в меня полетят. Так делать нельзя, неправильно и вообще некузяво. Но что я буду делать, если нормальных парсеров под .NET Framework 2.0 уже net, а задача маленькая — найти все теги <a> или <img> и выдрать из них, соответственно, значение атрибутов href или src.

Решение

public List<string> ParseTags(string Tag, string Property)
{
    List<string> listBuf = new List<string>();

    Regex reHref = new Regex(@"(?inx)
                                <" + Tag + @" \s [^>]*" +
                                    Property + @"\s* = \s*"+
                                        @"(?<q> ['""] )"+
                                            @"(?<url> [^""]+ )"+
                                         @"\k<q>"+
                                 @"[^>]* >");

    foreach (Match match in reHref.Matches(HTMLPage))
    {
        listBuf.Add(match.Groups["url"].ToString());
    }

    return listBuf;
}

Это немного видоизмененное решение (добавил возможность подставлять в регулярное выражение тег и атрибут) из одной статьи на Хабре. Заметка, как обычно, от склероза.

Плюс добавлена функция выбора из найденного, уже без всяких регулярных выражений, с помощью string.Contains(<строка>).

public List<string> Select(string Pattern, List<string> inputList)
{
    List<string> selectList = new List<string>();

    foreach (string s in inputList)
    {
        if (s.Contains(Pattern))
        {
            selectList.Add(s);
        }
    }

    return selectList;
}

C# Windows Forms. Использование ListView для логов, автоматическая прокрутка ListView, избавление от дрожания.

Преамбула

Иногда надо отображать пользователю ход процесса, т.е., что в данный момент наша программа делает. Лучше всего, на мой взгляд, для такого подходит компонент ListView.

В него будем писать лог наших действий. Замутим тестовое приложение:

— Создадим форму с ListView и кнопкой «Начать»
— По нажатию кнопки «Начать» запустим отдельный поток, который будет выводить нам числа от 1 до 100, и генерировать событие.

Подготовка ListView

В коде или в конструкторе установим основные опции для ListView. Нам нужно, чтобы он отображал все, как список. Ну так, как выводится лог в консоль. Устанавливаем соответствующие свойства:

View = Details

Теперь идем в конструктор, ищем опцию Columns, и добавляем единственную колонку:
В появившемся окне все удаляем из поля Text, жмем OK, смотрим на размер (Size) ListWiev, возвращаемся в редактирование колонок, и правим свойство Width. Устанавливаем чуть меньше, чем размер самого ListWiev.

Отладили, посмотрели чтоб было красиво? Ставим

HeaderStyle = None (чтоб не отображался заголовок колонки, заголовки были убраны).

Первый тест

Несмотря на то, что все действия происходят в отдельном потоке, ListView дергается и дрожит:

https://youtu.be/6Un0TSmyw38

На GitHub

Избавляемся от дергания и дрожания

Для этого делаем новый контрол, наследник от ListView и в коде нового класса подправляем параметр отображения:

class MyListView:ListView
{
    public MyListView()
    {            
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
}


Используем новый контрол вместо ListView. Ничего не дрожит и не дергается.

Автоматическая прокрутка ListView

При добавлении нового элемента:

Достаточно при добавлении в ListView устанавливать свойство TopItem в значение последнего элемента. Тогда у ListView появится автоматическая прокрутка:

lvOut.TopItem = lvOut.Items[lvOut.Items.Count - 1];

Результат


https://youtu.be/IzPIf5X8zQQ

Пример

На GitHub

C#. Работа с .ZIP архивами (подходит для старых .NET Framework’ов 3.5, 2.0)

Когда-то давно какой-то хороший человек написал библиотеку для работы с ZIP (а еще и Bzip2) архивами.

Приведу только простой пример использования — распаковка ZIP-архива в каталог:

public static bool UnzipToDir(string FileName,string UnzipDir)
{
    ZipFile zip = null;

    try
    {
        zip = ZipFile.Read(FileName);
        foreach (ZipEntry e in zip)
        {
            e.Extract(UnzipDir, 
                ExtractExistingFileAction.OverwriteSilently); 
                // перезаписывать существующие
        }
    }
    catch (Exception ex)
    {
        ErrorMessage = ex.Message;
        return false;
    }
    return true;
}

До использования, естественно, библиотеку надо подключить в References‘ах и прописать using:

using Ionic.Zip;

Источник (более подробное описание)

Работа с zip-архивами в .NET Framework 3.5 на C# Копия в PDF

Библиотека

1. Ссылка на Codeplex Archive
2. Скачать библиотеку с codernotes.ru
3. Копия архива Codeplex на nega.nz
4. Библиотека на Mega.nz

C#. Определить каталог пользователя.

Он же папка профиля пользователя, т.е., C:\Users\<имя пользователя>, например C:\Users\Tolik для пользователя Tolik.

Решение

Проще всего посмотреть в переменную окружения USERPROFILE:

Environment.GetEnvironmentVariable("USERPROFILE");

Для .NET Framework 4 и выше, путь к каталогу профиля пользователя добавлен в перечисление Environment.SpecialFolder под именем UserProfile. Таким образом, получить папку пользователя можно вот так:

Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

Пример

static void Main()
{
    string UserProfile = Environment.GetEnvironmentVariable("USERPROFILE");

    MessageBox.Show(UserProfile, "User profile folder path",
        MessageBoxButtons.OK, MessageBoxIcon.Information);
}

Пример на GitHub

Хаос-магический программный визуализатор, исходники.

Когда-то давно написали такую вот ритуальномагическую программу. Леша захотел до-/перепилить, или добавить в программу новые объекты, попросил исходники. Еле нашел, но вроде все в кучу собрал.

Исходники

Скачать готовое

Portable (ZIP-архив)
Установщик

Другой хаомагический софт

Нуль-отправитель

Интерактивный скрипт для переключения VPN’ок

Старая версия скрипта Че-то решил переделать.

1. Заводим переменную для хранения каталога с конфигами:

CONFIGDIR="~/openvpn/configs"

2. Добавляем функции create_list() и ask_list() по этому вот рецепту

create_list() #$1 - dir, $2 - file mask
{
    FOUNDLST=""
    for FLE in $(find $1 -maxdepth 1 -iname $2|sort); do
	if [ -n "$FLE" ]; then
	    FOUNDLST="$FOUNDLST"`basename $FLE`"\n"
	fi
    done
}

ask_list() #$1 - list #$2 - header
{
    LIST_BUF=$1
    LIST_BUF="$LIST_BUF""Exit "
    LIST_BUF="$LIST_BUF""Down"
    LIST_BUF=`echo -e "$LIST_BUF"|sed 's/\n/ /'`
    
    PS3=$2
    
    echo
    select LIST_RET in $LIST_BUF; do
	if [ -n "$LIST_RET" ];then
	    break
	fi
    done
    
}

Единственное что, в create_list() добавляем сортировку (sort), а в ask_list() два пункта — для выхода из скрипта и для разрыва соединения.

3. Делаем функцию для разрыва соединения:

ovpn_down()
{
    echo -n "Down connection..."
    CTR=0
    pkill openvpn
    while [ "$CTR" -ne "1" ];do
	echo -n "."
	CTR=`ps ax|grep -c "openvpn"`
	sleep 1
    done
    echo
}

4. Основной скрипт.
4.1. Создаем список из файлов конфигов
4.2. Выводим список на экран и ожидаем ответа пользователя.
4.3. Выводим пользователю, что он выбрал.
4.5. Проверяем, не выбран ли выход — если выбран, завершаем работу скрипта.
4.6. Если выбран пункт Down, то вызываем функцию разрыва соединения и выходим.

create_list $CONFIGDIR "*.ovpn"
ask_list $FOUNDLST "Select config: "
echo "Selected: $LIST_RET"

if [[ "$LIST_RET" == "Exit" ]]; then
    echo "Exitting..."
    exit 0
fi

if [[ "$LIST_RET" == "Down" ]]; then
    ovpn_down
    exit 0
fi

4.7. Если до этого не вышли, завершаем прошлое соединение, если оно установлено:

ovpn_down

4.8. Поднимаем новое соединение (да, тут отправляю лог Openvpn на третий терминал):

echo "Up connection..."
openvpn --config "$CONFIGDIR/$LIST_RET" >/dev/tty3 &

4.9. Запускаю скрипт waiter на 10 секунд

~/scripts/waiter 10

Скрипт целиком

1. На PasteBin
2. На GitHub