System Center 2012 Operations Manager + SP1 от архитектора Microsoft!

Сегодня секретами SCOM 2012 делится архитектор Microsoft Максим Гауфман

План вебинара:

  • Что нового в Service Pack 1
  • Global Service Monitor
  • Пулы серверов в Operations Manager 2012
  • Мониторинг сертификатов на серверах
  • Audit Collection Services как инструмент администратора


Для желающих смотреть или слушать вебинар OffLine (в машине, в фитнесе, в душе и т.п. 🙂 Спасибо за идею foxnet) файлы вы найдете у нас на сайте по ссылке.
Пользуясь случаем, хочу пригласить 16 мая на прямую трансляцию вебинара Hyper-V Replica Workshop Windows Server 2012, который проведёт Алексей Кибкало.

ссылка на оригинал статьи http://habrahabr.ru/company/stars_s/blog/178381/

Как я научил родителей качать турецкие сериалы одним щелчком

Добрый день!
У многих из нас есть родители, которые не очень дружат с техникой, и дружить приходится за них. Моя семья в полном составе смотрит сериал "Великолепный век", серии которого выходят раз в неделю. Вроде не так часто, но субботу за субботой слышать «Иди проверь, нет ли серии», искать раздачу, запускать торрент и так далее стало утомительно, и я решил переложить это на цифровые плечи четвертого .NetFramework-а.
Что нам понадобится:
Visual Studio 2010 или выше
— Консольный торрент-клиент Aria2c.

Осторожно, местами присутствует индусский код! Я предупреждал.

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

Для начала нам нужно понять, какую серию мы хотим скачать. Сериал выходит в Турции по средам, у нас с переводом в субботу-воскресение. На днях вышла 96 серия. Сейчас по календарю идет 18 неделя. Значит, к номеру текущей недели надо прибавить 78, если программу запускают в будний день, и 79, если в выходные.
Выглядеть это будет так:

        DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;         DateTime date1 = DateTime.Now;         Calendar cal = dfi.Calendar;         var week = cal.GetWeekOfYear(date1, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);         var day = (int)cal.GetDayOfWeek(date1) == 0 ? 6 : (int)cal.GetDayOfWeek(date1) - 1; //В США недели начинаются с воскресения, поэтому метод GetDayOfWeek возвращает 0 для воскресения, расставим  дни в привычном порядке. GetWeekOfYear же возвращаем правильное значание, так как применена локаль         var epnumber = week + 79 - ((day < 5) ? 1 : 0); 

Теперь, когда нам известен номер серии, можем идти на треккер.
Сперва нужно авторизоваться, для этого создаем POST-запрос на страничку login.php, и передаем ему пару логин-пароль.

byte[] buffer = Encoding.ASCII.GetBytes("login_username=ВАШ_ЛОГИН&login_password=ВАШ_ПАРОЛЬ&login=%C2%F5%EE%E4");         HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create("http://login.rutracker.org/forum/login.php");//Строка для Post-запроса         var cc = new CookieContainer();         WebReq.CookieContainer = cc;//включаем куки         WebReq.Method = "POST";         WebReq.ContentType = "application/x-www-form-urlencoded";         WebReq.ContentLength = buffer.Length;         HttpWebResponse WebResp;          try         {             Stream PostData = WebReq.GetRequestStream();             PostData.Write(buffer, 0, buffer.Length);             PostData.Close();             WebResp = (HttpWebResponse)WebReq.GetResponse();         }         catch (Exception e)         {             MessageBox.Show("Ошибка сети", " ", MessageBoxButtons.OK, MessageBoxIcon.Error);             return;         } 

Сервер в ответ плюнул в нас кукой авторизации, которая хранится у нас в cc. Теперь с этой кукой можно сделать запрос на поиск серии:

        var url = @"http://rutracker.org/forum/tracker.php?nm=%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%BB%D0%B5%D0%BF%D0%BD%D1%8B%D0%B9%20%D0%B2%D0%B5%D0%BA%20sub%20"+epnumber;  WebReq = (HttpWebRequest)WebRequest.Create(url);         WebReq.CookieContainer = cc;         WebReq.Method = "GET";         WebReq.ContentType = "application/x-www-form-urlencoded";         WebResp = (HttpWebResponse)WebReq.GetResponse();          string result;         Encoding responseEncoding = Encoding.GetEncoding(WebResp.CharacterSet);         try         {             using (StreamReader sr = new StreamReader(WebResp.GetResponseStream(), responseEncoding))             {                 result = sr.ReadToEnd();             }         }         catch (Exception e)         {             MessageBox.Show("Ошибка сети", "Заголовок сообщения", MessageBoxButtons.OK, MessageBoxIcon.Error);             return;         } 

Страницу поиска парсим на наличие ссылок на торрент файлы вида dl.rutracker.org/forum/dl.php?12345:

 string pattern = @"http://dl.rutracker.org/forum/dl.php\?t=\d+";         Regex regex = new Regex(pattern);         Match match = regex.Match(result);         if (match.Length == 0)         {              MessageBox.Show("Новая серия еще не вышла!", "", MessageBoxButtons.OK, MessageBoxIcon.Error);             return;         }         else            AutoClosingMessageBox.Show("Новая серия вышла! Начинаем закачку.", "Caption", 3000); 

Торрент-файл найден, сохраняем его на жесткий диск:

 try         {             WebReq = (HttpWebRequest)WebRequest.Create(match.ToString());             WebReq.CookieContainer = cc;             WebReq.AllowAutoRedirect = false;             WebReq.Method = "POST";             WebReq.Referer = url;             WebReq.ContentType = "application/x-www-form-urlencoded";              /*Пишем его в файл*/             Stream ReceiveStream = WebReq.GetResponse().GetResponseStream();             string filename = @"C:\123.torrent";             byte[] buffer1 = new byte[1024];             FileStream outFile = new FileStream(filename, FileMode.Create);             int bytesRead;             while ((bytesRead = ReceiveStream.Read(buffer1, 0, buffer.Length)) != 0)                 outFile.Write(buffer1, 0, bytesRead);             outFile.Close();             ReceiveStream.Close();                       }         catch (Exception e)         {             MessageBox.Show("Ошибка при скачке торрент-файла!", "", MessageBoxButtons.OK, MessageBoxIcon.Error);             return;         } 

Теперь проверяем, не забыли ли незадачливые родственники вставить флешку, и достаточно ли на ней места:

string letter = "";         foreach (DriveInfo i in System.IO.DriveInfo.GetDrives())         {               try             {                 if (i.DriveType.ToString() == "Removable" && i.ToString() != "A:\\")                 {                     if (i.TotalFreeSpace < 3000000000)                     {                         MessageBox.Show("На флешке мало место, нужно 3гб!", "", MessageBoxButtons.OK, MessageBoxIcon.Error);                         return;                     }                      letter = String.Copy(i.ToString());                     break;                 }                 //Console.WriteLine(i.DriveType);              }             catch (Exception E)             {                 return;             }         }         if (letter == "")         {             MessageBox.Show("Вставь флешку!", "", MessageBoxButtons.OK, MessageBoxIcon.Error);             return;         } 

Наконец, все готово к скачке, можно приступать!

 string par = @"--seed-time=0 -d " + letter + " --select-file="+(epnumber-3)+ @" ""C:\123.torrent"" "; //--seed-time=0 - выход сразу после загрузки, не сидируя. Нужно для сигнализации о завершения загрузки, да простят меня личи. //-d - указывает в качестве папки загрузки найденную ранее свободную флешку //--select-file - номер файла, нужного для скачки. В раздаче три серии пропущены, поэтому уменьшаем индекс на 3.          Process P = Process.Start(@"C:\aria2-1.17.0-win-32bit-build1\aria2-1.17.0-win-32bit-build1\aria2c.exe", par);         P.WaitForExit();         int result1 = P.ExitCode;         Console.WriteLine(result1);         if (result1 == 0)         {             MessageBox.Show("Фильм скачан! Можно вытаскивать флешку!", "", MessageBoxButtons.OK, MessageBoxIcon.Information);         }         else         {             MessageBox.Show("Неизвестная ошибка!", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);         } 

Все, сериал скачан и ждет просмотра.
Полный текст скрипта на http://pastebin.com/8L03vkJg.
Берегите ваших родителей.

P.S. Да, понятно, что на Per/PHP/Python/… можно уменьшить количество строчек в несколько раз. Код написан с целью самообразования.

ссылка на оригинал статьи http://habrahabr.ru/post/178493/

Защита Citrix Web Interface от brute-force атак с помощью CAPTCHA

Программный продукт Web Interface от Citrix используется как для XenApp так и для XenDesktop и представляет собой набор web скриптов, а также, консоль для управления.
Web Interface обеспечивает безопасный доступ к ресурсам XenApp и XenDesktop из любой точки при помощи любого устройства, оснащенного браузером.

Web Interface принимает имя пользователя и пароль на форме сайта и напрямую передает системе аутентификации, в большинстве случаев это Microsoft Active Directory. При настройках по умолчанию, после нескольких попыток с неправильно введенным паролем, учетная запись пользователя блокируется на определенный промежуток времени (обычно от 10 до 30 минут), что довольно надежно защищает от атак типа “brute-force”. Проблема в другом — получается, что зная адрес Web Interface и имя пользователя, можно обычными HTTP запросами, повторяющимися каждые 10-15 минут, полностью блокировать учётную запись.

Решение проблемы очевидно — captcha, однако на данный момент стандартных способов для активации captcha на Web Interface Citrix не предоставляет и придется редактировать код скриптов ручками. К счастью, ничего сложного в этом нет.

1. Создаем скрипт для генерации картинки

имя файла: captcha-image.aspx
папка: auth (по умолчанию C:/inetpub/wwwroot/Citrix/XenApp/auth )

<%@ page language="C#" %> <%@Import Namespace="System.Drawing" %>  <script runat="server">  private void Page_Load() {  Bitmap objBMP =new System.Drawing.Bitmap(60,20); Graphics objGraphics = System.Drawing.Graphics.FromImage(objBMP); objGraphics.Clear(Color.Green);   Font objFont = new Font("Chiller", 11, FontStyle.Bold); string randomStr=""; int[] myIntArray = new int[5] ; int x; Random autoRand = new Random(); for (x=0;x<5;x++) { myIntArray[x] = System.Convert.ToInt32 (autoRand.Next(0,9)); randomStr+= (myIntArray[x].ToString ()); } //Save generated Rnd in a Session  Session.Contents["CaptchaStr"]=randomStr; objGraphics.DrawString(randomStr, objFont, Brushes.White, 3, 3); Response.ContentType = "image/GIF"; objBMP.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Gif);  objFont.Dispose(); objGraphics.Dispose(); objBMP.Dispose();  } </script> 

Чтобы разрешить незалогиненным юзерам запускать этот скрипт, его надо внести в список разрешенных страниц «AUTH:UNPROTECTED_PAGES».
Для этого:
— Откройте файл web.config в корне сайта
— Найдите строку appSettings -> add key=«AUTH:UNPROTECTED_PAGES» и добавьте «captcha-image.aspx» в value.
— Должно получиться что-то вроде этого— value="/rade.aspx,/auth/style.aspx,/auth/captcha-image.aspx,/auth/javascript.aspx,/auth…

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

2. Редактируем код login-формы, добавляем картинку и поле ввода числа с картинки
Откройте файл loginMainForm.inc в папке include ( C:/inetpub/wwwroot/Citrix/XenApp/app_data/include) и внесите следующие изменения

Непосредственно перед строкой <table class=«loginForm»

<script> document.cookie='captcha=0000'; </script> 

Далее, сразу после } // End Domain

%> <tr><td align=right>Captcha:</td><td>  <img src=captcha-image.aspx border=0 width=60 height=20>  <input size=6 type=text id=captcha onKeyUp="document.cookie='captcha='+document.getElementById('captcha').value;" > </td></tr> <%  

После этих изменений, форма входа должна выглядеть приблизительно так:

3. Редактируем login.aspx — добавляем проверку введенного кода captcha в первую очередь

Редактируем файл login.aspx в папке auth (по умолчанию C:/inetpub/wwwroot/Citrix/XenApp/auth ) таким образом — найдите строку
layout.PageView = «loginView.ascx»;
и сразу после нее добавьте нижеследующий код:

string strData =Request.ServerVariables["HTTP_COOKIE"]; if (Session.Contents["CaptchaStr"] == null ) { Session.Contents["CaptchaStr"] = ""; } if ( Session.Contents["CaptchaStr"].ToString().Length != 0 && Request.ServerVariables["HTTP_REFERER"].Contains("login.aspx") ) { string captcha = "0000"; string[] separator = new string[] { ";" }; string[] strSplitArr = strData.Split( separator, StringSplitOptions.RemoveEmptyEntries); foreach (string arrStr in strSplitArr) { string[] arrFind = arrStr.Split(new Char[] {'='}, 3); if (arrFind[0].Contains("captcha") && arrFind[1].Length != 0 ) { captcha = arrFind[1]; } } if( captcha.ToString()== Session.Contents["CaptchaStr"].ToString() ) { Response.Write(captcha+"-"+Session.Contents["CaptchaStr"]); } else { Session.Remove("CaptchaStr"); Response.Redirect("~/html/serverErrorCaptcha.html"); Response.End(); } } 

После редактирования login.aspx должен иметь вид

login.aspx

<%@ Register TagPrefix="wi" TagName="Layout" Src="~/app_data/include/layout.ascx" %> <% // login.aspx // Copyright (c) 2002 - 2010 Citrix Systems, Inc. All Rights Reserved. // Web Interface 5.3.0.0 		 %> <%@ Import Namespace="com.citrix.wi.clientdetect" %> <!--#include file="~/app_data/serverscripts/include.aspxf"--> <script runat="server">     void Page_Load(object sender, System.EventArgs e) {         layout.PageClientScript = "~/auth/clientscripts/loginClientScript.ascx";         layout.PageView = "loginView.ascx";  string strData =Request.ServerVariables["HTTP_COOKIE"];   if (Session.Contents["CaptchaStr"] == null ) { Session.Contents["CaptchaStr"] = ""; } if   (  Session.Contents["CaptchaStr"].ToString().Length != 0 && Request.ServerVariables["HTTP_REFERER"].Contains("login.aspx") ) {     string captcha = "0000";  string[] separator = new string[] { ";" }; string[] strSplitArr = strData.Split( separator, StringSplitOptions.RemoveEmptyEntries); foreach (string arrStr in strSplitArr) {   string[] arrFind = arrStr.Split(new Char[] {'='}, 3);   if (arrFind[0].Contains("captcha") && arrFind[1].Length != 0  ) {   captcha = arrFind[1]; } }   if(  captcha.ToString()== Session.Contents["CaptchaStr"].ToString()    )  {     Response.Write(captcha+"-"+Session.Contents["CaptchaStr"]);   } else {  Session.Remove("CaptchaStr");    Response.Redirect("~/html/serverErrorCaptcha.html");    Response.End();   }     }      } </script> <% if(new com.citrix.wi.pages.auth.LoginASP(wiContext).perform()) { %> <wi:Layout id="layout" runat="server" /> <% } %> 

4. Страница ошибки ввода
При неправильно введенном коде каптчи, скрипт перенавляет на страницу ~/html/serverErrorCaptcha.html. Не забудем создать соответсвующий файл в папке C:/inetpub/wwwroot/Citrix/XenApp/html/. Можете использовать пример

~/html/serverErrorCaptcha.html

 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <!--   serverError.html   Copyright (c) 2002 - 2010 Citrix Systems, Inc. All Rights Reserved.   Web Interface 5.3.0.0 --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  <meta http-equiv="REFRESH" content="2; URL=../auth/login.aspx"> <meta name="ROBOTS" content="NOINDEX, NOFOLLOW, NOARCHIVE"> <meta http-equiv="X-UA-Compatible" content="IE=7"> <title>Internal Error</title> <script> document.cookie='captcha=0000'; </script> <script type="text/javascript"> <!-- var names = new Array(     'timeoutFrameWI_',     'reconnectFrameWI_',     'retryPopulatorFrameWI_',     'launchFrameWI_' ); function getTopFrame(w) {     for (var n in names) {         if(w.name.indexOf(names[n]) == 0) {             if(w.parent) {                 return w.parent;             }         }     }     return null; } function redirectTop() {     var topFr = getTopFrame(window);     if (topFr != null) {         topFr.location.href = window.location.href;     } } // --> </script> <link rel="stylesheet" type="text/css" href="../html/styles/basicStyle.css"> </head> <body onLoad="redirectTop()" dir="ltr">   <div id="overallWrapper">     <div id="leftShadow">       <div id="rightShadow">           <div id="pageContent">             <div id="header">                   <img src="../media/CitrixLogoHeader.gif" alt="Heading image">             </div>             <div class="mainPane">               <div id="commonBoxTop"><!-- --></div>               <div id="commonBox">                 <h3 class="error">Wrong security code</h3>                 <p>Enter the code shown on the image </p>                           </div>               <div id="commonBoxFoot"><!-- --></div>             </div>           </div>      </div>     </div>   </div> </body> </html> 

Вот, в принципе и все. После всез модификации описанных выше, на вашем WI сайте, должна работать защита captcha. Обратите внимание, что captcha проверка выполняется в первую очередь, и без правильно введенного кода никаких обращений к системе аутентификации не выполняется, чем и достигается отсутсквия риска lockout-а

ссылка на оригинал статьи http://habrahabr.ru/post/178489/

«Терминализация» sms-спама

В наши дни одним из самых популярных и удобных способов оплаты услуг мобильной связи и интернета являются платежные терминалы, которые окружают нас повсюду. Типичная модель использования терминала подразумевает собой нехитрую последовательность действий в виде выбора пункта оплаты услуги и её оператора, введения номера лицевого счёта, внесения денег и подтверждения самого платежа. Современно, разумно и, самое главное, быстро. Именно на этом и играют их владельцы, собирая номера мобильных телефонов для своих легализованных спам-баз.


Рассмотрим самые популярные, на мой взгляд, терминалы QIWI. Процесс оплаты выглядит следующим образом:

Фотографии

После проведения этих нехитрых действий, приходит сообщение об успешной оплате. Приходит оно не ради того, что бы оповестить пользователя об успешной оплате, это как правило делает сам оператор, а ради второй рекламной части сообщения. И что самое печальное, реклама на этом не ограничивается. Через некоторое время, с периодичностью раз в месяц, на номер приходят похожие, но уже полностью рекламные сообщения с, мягко говоря, «некачественной» рекламой о кредитах и займах, в которых есть свои большие подводные камни, но это уже другая история:

image

Вернемся к процессу оплаты. На тех фотографиях, к сожалению, нигде не говорится ни о моём согласии на приём рекламных сообщений, ни о хитром договоре-оферте, в котором кроется вся суть, и прочитать его можно лишь нажав на неприметную на общем фоне кнопку «информация», либо на веб сайте (та самая «дополнительная информация о платеже» на последней фотографии, о которой упоминается по факту оплаты).

Сам договор состоит из двух оферт и доступен по этой(1) и этой(2) ссылке. Что примечательно, первая оферта была найдена на стороннем сайте и немного отличается от той, что находится в самом интерфейсе терминала. Сами фотографии оферты из терминала вы можете скачать отсюда.

И хотя я не имею юридического образования, мне такая схема кажется не совсем честной. В самом начале мною была упомянута типичная модель использования этой «шайтан-коробки», теперь попробуйте добавить в неё обязательное чтение этого договора, примерно как при установке практически любой программы. Согласитесь, выглядит немного нелепо, особенно если за терминалом выстроилась очередь?

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

Ну и самое главное: отписаться от этого спама гарантированно можно лишь по телефонам: 8-800-333-00-59, +7 495 626-52-52 или, по неофициальной информации, написав на help@qiwi.ru.
Будьте бдительны и пользуйтесь альтернативными методами оплаты!

ссылка на оригинал статьи http://habrahabr.ru/post/178423/

edn: extensible data notation

В этой статье я хочу рассказать про edn. edn — формат данных, появившийся из языка clojure. Он похож на JSON, но предоставляет некоторые возможности, отсутствующие в JSON. Особенности edn описаны далее. Пример для затравки:

{:name "edn"  :implementations #{"clojure" "java" "ruby" "python" "c" "javascript" "haskell" "erlang"}  :related "clojure"  :encoding :UTF-8} 

Появление

История появления edn похожа на появление JSON: сначала появился язык программирования, а потом из него выделили подмножество и стали использовать в качестве формата данных. Если для JSON язык прародитель — JavaScript, то для edn — это Clojure.

Как я говорил ранее edn и JSON очень похожи и учитывая то, что JSON является сейчас наиболее известным, простым и популярным форматом данных, я буду рассказывать про edn через его отличия от JSON.

В edn поддерживаются все простые типы, присутствующие в JSON: строки, числа, булевские значения. Так же присутствуют новые:

nil

В качестве нулевого значения в edn используется значение nil. В JSON используется null.

знаки (characters)

edn поддерживает знаки для указания отдельных символов. Они начинаются с обратного слеша: \e, \d, \n. Можно использовать формат UTF8: \u2603. Специальные знаки прописываются полностью: \newline, \return, \space, \tab.
В JSON отдельные знаки обычно представляются в виде строки длины 1.
Я не называю знаки символами, потому что в edn есть отдельный тип symbol, который будет описан ниже.

ключевые символы (keywords)

Мне сложно сформулировать, что такое ключевые символы. Я бы сказал, что это смесь строк с перечислениями. Их удобно использовать, когда есть конечное фиксированное множество возможных значений. Эти значения и можно задавать ключевыми словами. Так же принято в качестве ключей отображений использовать ключевые символы. Ключевые символы начинаются с двоеточия: :name, :lastname, :female, :green. Те, кто работал с руби должны узнать в них символы, похожие типы присутствуют и в других языках, например common lisp.

Пример использования ключевых слов в отображении и сравнение с JSON версией:

edn JSON
{:name "Jack"  :lastname "Brown"  :gender :male} 

{"name": "Jack",  "lastname": "Brown",  "gender": "male"} 

числа

edn разделяет 2 типа чисел: целые и вещественные. Также он поддерживает числа произвольной длинны используя суффикс N для целых и M для вещественных:

[12345678891231231231232133N, 123.123123123123123213213M] 
вектора

JSON’овский массив в edn называется вектором: последовательность значений, для которых поддерживается операция произвольного доступа. В отличие от JSON элементы не должны разделяться запятыми. Их можно опустить:

[1 2 3 "Hello" "World"] 
отображения (maps)

В JSON они называются объектами. В edn ключ от значения не отделяется двоеточием (двоеточие — ошибка). Запятые тоже можно опустить. В качестве ключей может выступать любой другой тип, например числа или ключевые слова:

{:name "Jack"  :lastname "Brown"  :gender :male  42 54}

Здесь задаётся отображение с ключами :name, :lastname, :gender и 42, значения соответственно "Jack", "Brown", :male, 54.

множества

edn поддерживает тип данных множество. Оно задаётся в формате #{val1 val2 val3}. Порядок в множестве не важен и парсеры не гарантируют никакого конкретного порядка. В принципе парсеры должны преобразовать в стандартный для языка программирования тип, например HashSet для java, PersistentHashSet для clojure и аналогично для других языков. А в этих типах данных порядок никакой и не гарантируется. Пример: зададим очень полезное отображение содержащее времена года и 3 цвета:

{:seasons #{:winter :spring :summer :autumn}  :colors #{[255 0 0] [0 255 0] [0 0 255]}} 
списки

edn кроме вектора поддерживает списки. Список отличается от вектора тем, что отсутствует случайных доступ. Хотя это уже дело парсера в какой реальный тип он преобразует список. Вообще сложно придумать, когда может быть удобнее использовать список, а не вектор. Так что вектор в подавляющем числе случаев и используется. Пример списка:

(1 2 3 4 5) 
символы (symbols)

В clojure символы используются для обозначений переменных. Т.е. похожи на идентификаторы в обычных языках программирования: a, b, i, hello, persons. Символ из нескольких слов принято разделять дефисом: prime-numbers, visited-nodes. Они могут содержать кроме чисел и букв следующие знаки: . * + ! - _ ? $ % & =. Мне сложно придумать способ применения симвоволов в edn, когда есть строки и ключевые символы. Это уже зависит от вашего воображения. В datomic они используются для передачи запросов, например:

[:find ?x :where [?x :foo]]  

?x — символ.

элементы с тегами (tagged elements)

edn поддерживает возможность расширений при помощи тегов. Тег — идентификатор, который начинается с #, за которым идёт символ: #inst, #uuid, #myapp/Person. Особенность таких элементов состоит в том, что парсер, когда встречает такой элемент читает его и следующий за ним элемент, передаёт специальному обработчику, который должен преобразовать входные агргументы в нужный тип и вернуть. Примеры:

#myapp/Person {:first "Fred" :last "Mertz"} 

Тут в парсере должен быть зарегистрирован обработчик тега #myapp/Person, который принимает отображение и преобразует его в объект класса myapp.Person (если в языке есть классы), или что-то подобное.

#inst "1985-04-12T23:20:50.52Z" 

Обработчик принимает строку в формате RFC-3339 и преобразует в соответствующую дату.

#uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" 

Обработчик преобразует UUID строку в соответствующий объект.

Последние 2 тега являются встроенными и должны работать из коробки. Также накладывается ограничение, что пользовательские теги должны всегда иметь namespace в начале как в случае с #myapp/Person: тут myapp — namespace. Теги без namespace (например #inst, #uuid) зарезервированы для стандартных обработчиков.

комментарии

Для комментариев используется ;. С помощью него можно закомментить строку:

{  :red [255 0 0] ; Red 255, green 0, blue 0  :orange [255 127 0] ; Red 255, green 127, blue 0 } 

Более цельный пример edn

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

[{:name "Jack"   :lastname "Brown"   :roles #{:admin :operator}   :last-visited #inst "2013-04-12T23:20:50.52Z"   :id #uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"}  {:name "John"   :lastname "Black"   :roles #{:user}   :last-visited #inst "2013-04-12T20:20:50.52Z"   :id #uuid "b371b600-b175-11e2-9e96-0800200c9a66"}] 
Когда использовать

Это субъективный вопрос. Все вышеперечисленные особенности можно реализовать и в JSON вводя свои собственные конструкции, но это требует более сложной логики для преобразование в/из JSON. В edn они есть из коробки, что весьма удобно. Если вы работаете с clojure, то edn является естественным выбором. Так же может вам надоел скучный JSON и хочется поработать с более гибким и настраиваемым форматом, в чём могут помочь теги. Наличие «стандартного» типа для даты тоже приятная особенность. Можно сказать, что edn — JSON на стероидах.

Ссылки

Оффициальное описание формата: github.com/edn-format/edn
Реализации для java, ruby, python, javascript, haskell и других языков: github.com/edn-format/edn/wiki/Implementations
Обсуждение на Hacker News: news.ycombinator.com/item?id=4487462 Там как раз и ругаются по поводу «Зачем edn, если есть JSON?»

ссылка на оригинал статьи http://habrahabr.ru/post/178473/