Friday, May 16, 2014

Short C++ program for text replacement in a text file

Below is a simple program for searching and replacing substrings in a text file. It is based on boost library, uses memory mapped file to use boost::regex_replace function, which supports working with streams. Process result stored to the temporary file, which will replace original file after processing completion.

Source code can be compiled on any platform supported by boost library, including Windows and Unix.


#include <iostream>
#include <fstream>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

using namespace std;
namespace po = boost::program_options;


void Replace(const string& inFilePath, const string& searchStr, const string& replacement)
{
 boost::iostreams::mapped_file istm(inFilePath.c_str());
 if (!istm.is_open())
  return;

 string tempFilePath = inFilePath + ".tmp";
 ofstream ostm(tempFilePath.c_str(), std::ios::out | std::ios::binary);
 if (!ostm.is_open()) {
  istm.close();
  return;
 }

 boost::regex regexp(searchStr);
 ostreambuf_iterator<char> it(ostm);
 boost::regex_replace(it, istm.begin(), istm.end(), regexp, replacement, boost::match_default | boost::format_all);
 
 istm.close();
 ostm.close();

 boost::filesystem::rename(tempFilePath, inFilePath);
}

int main(int argc, char *argv[])
{
 try {
  po::options_description optsDescription("Allowed commands");
  optsDescription.add_options()
   ("file",    po::value<string>(), "Path to the file to search in")
   ("find",    po::value<string>(), "What to search (substring or regular expression)")
   ("replace", po::value<string>()->default_value(""), "Substring to replace founded results");

  po::variables_map options;
  po::store(po::parse_command_line(argc, argv, optsDescription), options);

  po::notify(options);

  if (options.empty() || !options.count("file") || !options.count("find")) {
   cout << "Usage:" << endl;
   cout << "replace.exe --file=<path to file> --find=<search string> --replace=<replacement string>" << endl << endl;
   cout << optsDescription << endl;
   return 1;
  }

  Replace(options["file"].as<string>(), options["find"].as<string>(), options["replace"].as<string>());
 } catch (const exception& ex) {
  cout << "Execution failed: " << ex.what() << endl;
 }

 return 0;
}

Wednesday, April 16, 2014

How to build log4cpp 1.0 using Visual Studio

Attempt to compile log4cpp with more modern release of Visual Studio, than VS 6.0, will fail with error:

Appender.sbr No such file or directory

To solve the issue open properties of file NTEventLogCategories.mc in Visual Studio, find General settings of Custom Build Steps. Check Command Line. There are wrong paths to the utilities mc.exe, rc.exe and link.exe. Fix them and compilation will success.

Compilation in Visual Studio 2012 can produce an error:
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\stdint.h(11): error C2632: '__int64' followed by '__int64' is illegal

To fix it edit file config-win32.h. Find and comment the following line:
#define int64_t __int64

Compiling DLL or linking static library with you project can produce liner errors like this:

5>log4cpp.lib(NTEventLogAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(RemoteSyslogAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(PatternLayout.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(FileAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(RollingFileAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(AbortAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(Win32DebugAppender.obj) : error LNK2001: unresolved external symbol "public: class std::_Tree_const_iterator,class std::allocator > const ,class std::basic_string,class std::allocator > > > > > __thiscall log4cpp::FactoryParams::find(class std::basic_string,class std::allocator > const &)const " (?find@FactoryParams@log4cpp@@QBE?AV?$_Tree_const_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@std@@@std@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@4@@Z)
5>log4cpp.lib(PatternLayout.obj) : error LNK2001: unresolved external symbol "void __cdecl log4cpp::localtime(__int64 const *,struct tm *)" (?localtime@log4cpp@@YAXPB_JPAUtm@@@Z)

Check log4cpp project and you will see not all cpp files are included. You need to add at least these files to success:

src/Localtime.cpp
src/FactoryParams.cpp

PS: log4cpp 1.1 have ready to use project files for modern compilers

Sunday, April 6, 2014

ptrace: Operation not permitted

Since Ubuntu 10.10 introduced feature ptrace_scope, which prohibits ptracing of non-child processes of non-root users. So for regular users ptracing is available from parent process only and you may see "ptrace: Operation not permitted" error while trying to attach some process to debug from gdb or trying to run debug session from your IDE, e.g. QTCreator.

You can temporary disable this security rule by calling the following command:

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

It will work untill next reboot. To disable it permanently edit file /etc/sysctl.d/10-ptrace.conf and change line:

kernel.yama.ptrace_scope = 1

to:

kernel.yama.ptrace_scope = 0

To get more information about ptrace_scope feature see the following Ubuntu Wiki note:
https://wiki.ubuntu.com/Security/Features#ptrace

Friday, April 4, 2014

Issue in implementation of read/write interface to FTP used libcurl multi interface

Libcurl example of I/O interface (fopen, read, fclose) works perfectly using libcurl 7.23.0, but may not work at least since libcurl 7.30.0 (and it still exists in the latest 7.36.0) if you use host name instead of IP in URL. Attempt to use code like in this example may do no real FTP file reading.
After calling
curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
from the function fill_buffer variable maxfd gets value -1 and no reading performed.

The reason of such behaviour is in the libcurl source code. It resolve host name to IP in separate thread (function getaddrinfo_thread from asyn-thread.c). But this thread has bad synchronization with main thread, where connection is performed. As a result race condition occurs and connection performed before resolve.

But if URL contains IP address instead of host name, resolving stage will be skipped and all will work as expected.

In real world application without patching libcurl you can perform resolve by yourself, e.g.:
1. Get host name and port (if available) from URL
2. Using function getaddrinfo (has the same signature on Unix and Windows) get list of IP addresses for the host name
3. Iterate IP list recieved testing connection using original URL, where hostname replaced with corresponding IP. Use CURLOPT_CONNECT_ONLY to perform only connection without any other actions
4. Add IP you successfully connected to the some cache, e.g. map of hostname and IP, to use it later

Such behaviour successfully used in one of projects I involved.

Another approach is to avoid using multi interface, but use only easy interface of libcurl. In such case you should implement read/write interface by implementing parallel threading by yourself. Such approach I implemented in other project I was involved and it still successfully used by tens of thousands peolpe.

Friday, March 14, 2014

Тех. поддержка сайта odnoklassniki.ru

Поменял пароль на свой аккаунт в odnoklassniki.ru на суперсложный и благополучно его забыл, потому что и не собирался запоминать, мой софт помнит его за меня, а если что случится, всегда можно восстановить, что я и делал успешно уже пару раз.

Тут мне пришлось реанимировать свой ноут после поломки, в ходе чего я потерял некоторые некритичные данные, типа сохраненных паролей и кук браузера. Дошло дело до Одноклассников. Пошел авторизовываться и понял, что пароль не помню. Ерунда, сейчас восстановим. Не тут-то было. Ребята поменяли алгоритм восстановления. Стали требовать номер телефона. Без него восстановление невозможно, а я его не указывал никогда. В итоге система пишет мне, что я сам дурак, что не указал телефона в своем профиле и помочь они ни чем не могут.

Я решил, что так не бывает, я же знаю, что я это я, а не кто-то другой. Написал в тех. поддержку. На следующий день пришло письмо следующего содержания:

.....

Пожалуйста, максимально подробно ответьте на следующие вопросы:
1. Логин, указанный в профиле.
2. Имя, фамилия и возраст, указанные в профиле.
3. Город и страна, указанные в профиле.
4. Дата создания профиля.
5. Ссылка на ваш профиль.
6. Когда вы заходили на Одноклассники в последний раз?
7. Когда вы последний раз изменяли личные данные в профиле? Какую информацию вы изменяли?
8. Номер телефона и e-mail, указанные вами при регистрации на сайте.
9. Приобретали ли вы платные услуги на Одноклассниках? Если да, то какие? Каким способом вы их оплачивали?
10. Указывали ли вы вторую половинку или кого-то из родственников? Если да, то кого (имя, фамилия, возраст и город или ссылка на профиль)?
11. Какие подарки (значки) вы получали или дарили? Кому (от кого) и когда?
12. Назовите пользователей, с которыми вы общались на сайте в последнее время?

Пожалуйста, постарайтесь вспомнить детали и ответить как можно подробнее! Если вы можете предоставить нам ещё какую-либо информацию по вашему профилю, то это ускорит процесс решения проблемы.

Нам также потребуется ваша фотография на фоне переписки со Службой поддержки. Для этого:
1. откройте переписку со Службой поддержки на вашем компьютере;
2. сядьте рядом с компьютером, на котором открыта переписка;
3. попросите сфотографировать вас (должен быть виден монитор компьютера и ваше лицо);
4. приложите полученную фотографию к этому электронному письму.

ОБРАТИТЕ ВНИМАНИЕ! Без фотографии на фоне переписки мы не сможем помочь вам в восстановлении профиля, т.к. это единственное доказательство того, что именно вы являетесь заявленным пользователем, а не мошенником, пытающимся получить доступ к вашему профилю.
.......

Написал. Через 2 дня ответа нет. Тогда написал еще одно письмо с просьбой рассказать как обстоят дела с моим обращением. И пошел проверить изменилось ли что-то в алгоритме восстановления пароля. Ведь с этой новой фичей наверняка не я один попал и возможно ПМы решили что-то поменять. И действительно я смог восстановить доступ к аккаунту так же легко как и раньше, без телефона.

Еще через 2 дня пришло письмо, что по их данным я уже успешно залогинился и помощь мне больше не нужна. Спасибо :)
Редко пользуюсь Одноклассниками, поэтому мне этот сайт совсем не критичен. А вот опыт работы чужого саппорта очень интересен. Интересно есть ли у ребят ранжирование тикетов по приоритету и как они отслеживают workflow и время отклика...

Friday, March 7, 2014

Regular expression to parse URL

To check URL structure or extract its parts the following regular expression can be used:
^(?<scheme>[a-zA-Z]*):\/\/
 (?:(?<login>[^:\/]*)(?::(?<password>[^\/]*))?@)?
 (?<host>[^\/:@]+)
 (?::(?<port>[0-9]+))?
 (?:\/?(?<path>.*?)(?<file>[^\/]*?))
 (?:\?(?<query>.*))?$

This expression has limitation. It can not extract password from URL, if this password contains slash character.

Below is a sample Perl code with tests.

#!/usr/bin/perl
use strict;
use warnings;

sub parseUrl
{
  my $url = shift;
  
  if ($url =~ /^(?<scheme>[a-zA-Z]*):\/\/
                (?:(?<login>[^:\/]*)(?::(?<password>[^\/]*))?@)?
                (?<host>[^\/:@]+)
                (?::(?<port>[0-9]+))?
                (?:\/?(?<path>.*?)(?<file>[^\/]*?))
                (?:\?(?<query>.*))?
                $/x)
  {
    my %parts;
    $parts{'scheme'} = (defined $1) ? $1 : "";
    $parts{'login'} = (defined $2) ? $2 : "";
    $parts{'password'} = (defined $3) ? $3 : "";
    $parts{'host'} = (defined $4) ? $4 : "";
    $parts{'port'} = (defined $5) ? $5 : "";
    $parts{'path'} = (defined $6) ? $6 : "";
    $parts{'file'} = (defined $7) ? $7 : "";
    $parts{'query'} = (defined $8) ? $8 : ""; 
    
    return \%parts;
  }
}

sub test
{
  my ($url, $scheme, $login, $password, $host, $port, $path, $file, $query) = @_;
  
  print "-------------------\n$url\n";
  my $parts = parseUrl($url);
  
  if (!$parts) {
    print "[FAILED] Invalid URL\n";
    return;
  }
  
  if ($parts->{'scheme'}   eq $scheme &&
      $parts->{'login'}    eq $login &&
      $parts->{'password'} eq $password &&
      $parts->{'host'}     eq $host &&
      $parts->{'port'}     eq $port &&
      $parts->{'path'}     eq $path &&
      $parts->{'file'}     eq $file &&
      $parts->{'query'}    eq $query)
  {
    print "[OK]\n";
  } else {
    print "[FAILED] Parsed results are:\n";
    print "scheme   = $parts->{'scheme'}\n";
    print "login    = $parts->{'login'}\n";
    print "password = $parts->{'password'}\n";
    print "host     = $parts->{'host'}\n";
    print "port     = $parts->{'port'}\n";
    print "path     = $parts->{'path'}\n";
    print "file     = $parts->{'file'}\n";
    print "query    = $parts->{'query'}\n";
  }
}

sub main
{
  # Posotove tests
  test("ftp://d1:qwe:qwe\@172.168.1.1/bkp1.tar",  "ftp", "d1", "qwe:qwe", "172.168.1.1", "", "", "bkp1.tar", "");
  test("ftp://d1:qwe:qwe\@172.168.1.1/dir:dir/subdir\@subdir/bkp1.tar",     "ftp", "d1", "qwe:qwe", "172.168.1.1", "", "dir:dir/subdir\@subdir/", "bkp1.tar", "");
  test("ftp://172.168.1.1/dirdir/subdir\@subdir/bkp1.tar",  "ftp", "", "", "172.168.1.1", "", "dirdir/subdir\@subdir/", "bkp1.tar", "");
  test("ftp://172.168.1.1/dirdir/subdir/bkp1.tar",          "ftp", "", "", "172.168.1.1", "", "dirdir/subdir/", "bkp1.tar", "");
  test("ftp://d1:qwe:qwe\@172.168.1.1:21/bkp1.tar",         "ftp", "d1", "qwe:qwe", "172.168.1.1", "21", "", "bkp1.tar", "");
  test("ftp://d1:qwe\@qwe\@172.168.1.1:21/bkp1.tar",         "ftp", "d1", "qwe\@qwe", "172.168.1.1", "21", "", "bkp1.tar", "");
  test("ftp://d1\@d1:qwe\@qwe\@172.168.1.1:21/bkp1.tar",     "ftp", "d1\@d1", "qwe\@qwe", "172.168.1.1", "21", "", "bkp1.tar", "");
  test("http://172.168.1.1/dir/index.php?var=value",         "http", "", "", "172.168.1.1", "", "dir/", "index.php", "var=value");
  
  # Invalid URL
  test("/var/www/mysite",  "", "", "", "", "", "", "", "");
  
  # Parsing error - regular expression can not parse URLs with password containing slash
  test("ftp://d1:qwe/qwe\@localhost/bkp1.tar",    "ftp", "d1", "qwe/qwe", "localhost", "", "", "bkp1.tar", "");
}

main();

You can use this expression with other programming languages. In the example to simplify reading expression written on several lines. To use it as is you need to enable "Ignore pattern whitespace" option for regex parser or rewrite this expression in single line.

Friday, February 21, 2014

Решение проблемы отображения сайтов Microsoft

Майкрософт перенесли CDN с домена microsoft.com на aspnetcdn.com (http://blogs.msdn.com/b/scothu/archive/2010/11/16/updates-to-the-microsoft-cdn.aspx). Я на работе после этого столкнулся с проблемой, что некоторые сайты Майкрософт работают некорректно. Проявляется это в том, что загрузка контента никак не может завершиться и отображается просто белая страница. Независимо от браузера. В статусной строке можно увидеть сообщение об ожидании загрузки файлов с ajax.aspnetcdn.com.

Причина здесь в DNS и проще всего исправить ошибку дома или на рабочем месте можно, прописав в качестве DNS-сервера DNS Google или, например, Яндекс. На практике DNS сервера Google работают надёжнее, так что я рекомендую использовать его: 8.8.8.8.

Подробнее о публичном DNS Google http://ru.wikipedia.org/wiki/Google_Public_DNS

Если просто заменить в настройках сетевого подключения свой DNS на DNS Google, то может оказаться, что какие-то локальные сервисы станут недоступны. Это особенно актуально для корпоративных сетей. Поэтому лучше всего добавить DNS Google в качестве альтернативного DNS. Если у вас включена опция "Получать адрес DNS сервера автоматически" (Obtain DNS server address automatically) нужно сначала определить реальный адрес (или адреса) DNS серверов, чтобы прописать их явно. В этой статье хорошо написано как определить текущие настройки DNS на Windows и Linux http://www.cyberciti.biz/faq/how-to-find-out-what-my-dns-servers-address-is/