Walutowa karta prepaid Cinkciarz.pl

Dolarową kartę przedpłaconą kupiłem do cinkciarza, żeby płacić w amerykańskich sklepach (głównie Amazon.com) unikając podwójnego przewalutowania i bankowych kursów walut. Chociaż cel ten osiągnąłem to jednak karta nie do końca spełniła moje oczekiwania.

Największym rozczarowaniem okazało się zasilanie rachunku karty. Założyłem, że skoro karta jest sygnowana logiem Cinkciarz.pl to zasilenie rachunku karty z tegoż serwisu będzie się odbywać natychmiastowo. Nic bardziej mylnego. Od złożenia zlecenia do zaksięgowania wpłaty w mBanku, który obsługuje moją kartę, może minąć do 24 godzin. W praktyce oznacza to, że zakupy trzeba planować z wyprzedzeniem albo stale trzymać na rachunku karty odpowiednia kwotę. To trochę kłóci się z moim wyobrażeniem o kartach prepaid, których największą zaletą jest to, że nie da się z nich nic ukraść, bo doładowuje się je na konkretny zakup.

Konsekwencją czasu księgowania wpłat jest to, że z karty nie można skorzystać od razu po jej otrzymaniu. Karta jest bowiem aktywowana dopiero po zaksięgowaniu pierwszego zasilenia. Ja wybrałem się na zakupy od razu po rozpakowaniu karty. Wprawdzie wysłałem środki na rachunek karty, ale nie zostały one zaksięgowane na czas i Amazon poinformował mnie, że są problemy z płatnością wybrana kartą. Jak sądzę, wielu właścicieli tych kart mogło popełnić podobny błąd, bo przy wykonywaniu zasilenia nie ma żadnego komunikatu jasno określającego kiedy operacja zostanie wykonana.

Ostatnią wadą związaną z obsługą rachunku karty jest to, że nie ma prostego sposobu na przelanie dolarów z karty na moje konto. Można skorzystać z polskiego bankomatu żeby wypłacić złotówki, co jest oczywiście bez sensu; można skorzystać z zagranicznego bankomatu, który wypłaca dolary, co ma więcej sensu, ale i tak obciążone jest prowizją; w końcu można poczekać na wygaśniecie karty – wtedy pozostałe środki zostaną przelane na wskazane przy rejestracji karty konto. Żaden z tych sposobów nie nadaje się do częstego manewrowania saldem karty.

Drobiazgiem, który również może być zaklasyfikowany jako mankament jest fakt, że ważność karty to niecałe dwa lata. W moim wypadku różnica wynosi trzy miesiące, ale jeśli mBank lub Cinkciarz.pl zamawiają karty na zapas to komuś może trafić się karta z jeszcze krótszym terminem przydatności.

Oczywiście karta walutowa Cinkciarz.pl ma również zalety. Przede wszystkim transakcje walutowe wykonywane są bez przewalutowań. Walutę na rachunek karty można kupować po korzystniejszym niż bankowy kursie. Karta przedpłacona od cinkciarza może być także tańsza w eksploatacji niż bankowa karta do rachunku walutowego ponieważ płaci się za nią jednorazowo tylko 15 zł i przez kolejne (niecałe) dwa lata nie ma innych opłat (rocznych, miesięcznych, za brak wymaganego obrotu albo za brak wymaganej liczby transakcji).

W moim wypadku zalety cinkciarskiej karty przeważają nad jej wadami, jednak dla innych klientów z innymi priorytetami bilans może wyglądać inaczej. Dlatego warto znać zarówno wady jak i zalety, żeby podjąć odpowiednią decyzję. Mam nadzieję, że ten wpis to ułatwi.

Routing Symfony w skryptach JavaScript

FOSJsRoutingBundle to paczka, która rozwiązuje problem odwoływania się do aplikacji Symfony z poziomu skryptów JavaScript, np. przy definiowaniu akcji AJAX-owych. Problem może nie jest wielki, bo oczywiście można URL-e zapisać na sztywno, ale w takim wypadku nie można korzystać z dobrodziejstw środowiska dev.

Dzięki wspomnianej paczce URL do akcji w Symfony można wygenerować podobnie jak w PHP:

var url = Routing.generate('my_route_to_expose', { id: 10, foo: "bar" });

FOSJsRoutingBundle umożliwia zarówno dynamiczne jak i statyczne generowanie tras. W tym pierwszym wypadku aplikacja Symfony jest dynamicznie odpytywana o trasy, dzięki czemu zmiany w aplikacji od razu są widoczne w JavaScriptcie. Ten tryb warto wybrać w czasie developmentu albo w zastosowaniach, gdzie nieco mniejsza wydajność nie będzie problemem. W trybie statycznym należy poleceniem z konsoli wygenerować plik zawierający definicję routingu dla JS.

Poniżej opiszę instalację paczki w Symfony i konfigurację w trybie statycznym.

Tradycyjnie zacząłem od instalacji paczki composerem:

composer require friendsofsymfony/jsrouting-bundle

Po zainstalowaniu paczki dodałem ją do app/AppKernel.php:

public function registerBundles()
{
    $bundles = array(
        // ...
        new FOS\JsRoutingBundle\FOSJsRoutingBundle()
    );
}

Kolejnym krokiem jest skopiowanie statycznych plików do katalogu web:

app/console assets:install

Połączenie pomiędzy Symfony i JavaScriptem stanowią dwa skrypty, które dodałem do podstawowego szablonu strony:

<script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
<script src="{{ asset('js/fos_js_routes.js') }}"></script>

Następnie należy w definicji routingu wskazać, które trasy mają być dostępne w JavaScriptcie. Dokumentacja pokazuje konfigurację YAML-ową, ja zaś używam annotacji:

/**
 * @Route("/sugeruj-produkt", name="suggest_product", options={"expose": true})
 */

Ostatnim krokiem jest wygenerowanie pliku z routingiem:

app/console fos:js-routing:dump

Teraz wszystko będzie grać i buczeć.

Testowe adresy email

Dzisiaj kilka słów o mailach w kontekście programowania (lub testowania) aplikacji internetowych. Najprościej rzecz ujmując, warto mieć pod ręką duuużo adresów email, co przydaje się przy np. testowaniu rejestracji albo mailingu. Skąd je wziąć?

W Internecie jest całe mnóstwo darmowych kont pocztowych, ale zakładanie dziesiątek kont, pamiętanie do nich haseł i logowanie się na każde z nich byłoby uciążliwe. Na szczęście Dobrzy Ludzie stworzyli różne usługi i narzędzia, które ten problem rozwiązują.

Tymczasowe konta

Najprostsze w użyciu są usługi w rodzaju niepodam.pl i migmail.pl. Wystarczy podać dowolny adres w domenie niepodam.pl albo migmail.pl, np. michal@niepodam.pl, a następnie wejść na taką stronę, wpisać wybrany login i już można czytać maile wysłane na ten adres.

migmail

Ponieważ takich adresów nie trzeba rejestrować można je równie dobrze generować automatycznie, co przydaje się np. przy testowaniu mailingu. Ja najczęściej korzystam z nich w środowiskach, które wysyłają „prawdziwe” maile np. UAT i produkcyjnym.

Wadą tych rozwiązań jest to, że wyświetlanie wiadomości w formacie HTML nie zawsze działa jak powinno, np. linki są obcięte albo nieklikalne, a elementy graficzne źle rozmieszczone. Dlatego słabo nadają się do testowania wyglądu maili.

Warto też pamiętać, że są to tymczasowe konta i wiadomości są usuwane automatycznie po kilku godzinach lub dniach.

GMail

Każde konto GMail ma dwie cechy, które przydają się przy mnożeniu aliasów:

  • Wszystkie występujące w nazwie użytkownika kropki są ignorowane.
  • Po nazwie użytkownika można postawić znak „plus” i wpisać dowolny tekst, który także zostanie zignorowany.

W efekcie dysponując kontem imienazwisko@gmail.com możemy generować unikalne adresy do woli, np. imie.nazwisko@gmail.com, i.m.i.e.nazwisko@gmail.com, imienazwisko+test1@gmail.com, imie.nazwisko+test2@gmail.com itd.

MailCatcher

MailCatcher to prosty demon SMTP, który całą korespondecję, która przez niego przechodzi pozwala przeglądać za pomocą prostego interfejsu webowego. Demon nie sprawdza poprawności adresów, więc można używać dowolnych loginów i domen, np. qwerty@aplikacja.dev. Najlepiej spisuje się w środowisku deweloperskim, które niekoniecznie ma wyjście do Internetu.

MailCatcher jest napisany w Ruby, więc o ile to środowisko uruchomieniowe tego języka nie jest zainstalowane na serwerze trzeba wykonać następujące polecenie:

aptitude install ruby ruby-dev libsqlite3-dev build-essential

Kiedy Ruby już działa to można zainstalować MailCatchera:

gem install mailcatcher

Uruchomienie MailCatchera z konsoli:

mailcatcher --foreground --http-ip=0.0.0.0

Domyślnie SMTP nasłuchuje na porcie 1025, a interfejs webowy jest dostępny na porcie 1080. Oczywiście takie niestandardowe parametry SMTP należy podać w konfiguracji PHP albo aplikacji.

W Symfony2 wystarczy dodać parametr port w konfiguracji SwiftMailera (w standardowej konfiguracji go nie ma). W pliku app/config/config.yml dodajemy:

swiftmailer:
    transport: "%mailer_transport%"
    host:      "%mailer_host%"
    username:  "%mailer_user%"
    password:  "%mailer_password%"
    port:      "%mailer_port%"

Zaś w app/config/parameters.yml:

parameters:
    # ...
    mailer_transport: smtp
    mailer_host: 127.0.0.1
    mailer_user: null
    mailer_password: null
    mailer_port: 1025
    # ...

Bezprzewodowe głośniki w Linuksie

Da się? Da się! Tak w skrócie można podsumować moje podejście do połączenia laptopa z Linuksem do głośników bez użycia kabli. Poszło łatwiej niż sądziłem, prawie plug & play.

W sumie wszystko co musiałem jest elegancko opisanie w debianowej wiki. Na początek należy się upewnić, że wymagane pakiety są zainstalowane:

aptitude install pulseaudio pulseaudio-module-bluetooth pavucontrol bluez-firmware

U mnie ich brakowało, ale mój system instalowałem z wersji minimalnej, nie lubię mieć w systemie rzeczy, których nie używam. Potem warto zrestartować usługę bluetooth i dźwięku:

service bluetooth restart
killall pulseaudio

Ostatnim krokiem jest sparowanie głośników z komputerem podobnie jak każdego innego urządzenia.

a2dp_connection

Gdy urządzenie jest sparowane wystarczy przełączyć wyjście na głośniki bluetooth.

a2dp_settings

Dzięki przenośnym głośnikom mogę cieszyć się dźwiękiem z laptopa nie tylko przez słuchawki (te wbudowane pierdziawki nie nadają się prawie do niczego). Wiem też, że mój następny amplituner powinien być wyposażony w bluetooth. Gdy go kupię dźwiękiem z mojego laptopa będą się również mogli cieszyć sąsiedzi. ;-)

Diagnozowanie i naprawianie problemu X-ami

Kiedy po aktualizacji pakietów system nie wstaje to wiedz, że coś się dzieje. W moim przypadku uruchamianie zdychało przy uruchamianiu powłoki graficznej. W tym wpisie pokażę jak można zdiagnozować przyczynę i skutecznie naprawić system w jednym z takich wypadków.

Pierwszym krokiem przy diagnozowaniu problemów z X-ami jest sprawdzenie logów.

# grep '(EE)' /var/log/Xorg.0.log

[    13.384] (EE) Failed to load /usr/lib/xorg/modules/extensions/libglx.so: libGL.so.1: cannot open shared object file: No such file or directory
[    13.384] (EE) Failed to load module "glx" (loader failed, 7)

Ewidentnie w systemie brakowało biblioteki libglx.so. Należy więc sprawdzić jej zależności.

# ldd /usr/lib/xorg/modules/extensions/libglx.so 
	linux-vdso.so.1 (0x00007fffad3fd000)
	libGL.so.1 => not found
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb41bf2f000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb41bd2b000)
	libaudit.so.1 => /lib/x86_64-linux-gnu/libaudit.so.1 (0x00007fb41bb05000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb41b804000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb41b45b000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007fb41b232000)
	libglapi.so.0 => /usr/lib/x86_64-linux-gnu/libglapi.so.0 (0x00007fb41b007000)
	libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007fb41adf5000)
	libXdamage.so.1 => /usr/lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007fb41abf2000)
	libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007fb41a9ec000)
	libX11-xcb.so.1 => /usr/lib/x86_64-linux-gnu/libX11-xcb.so.1 (0x00007fb41a7ea000)
	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007fb41a4a7000)
	libxcb-glx.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-glx.so.0 (0x00007fb41a28e000)
	libxcb-dri2.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-dri2.so.0 (0x00007fb41a089000)
	libxcb-dri3.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0 (0x00007fb419e86000)
	libxcb-present.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-present.so.0 (0x00007fb419c83000)
	libxcb-sync.so.1 => /usr/lib/x86_64-linux-gnu/libxcb-sync.so.1 (0x00007fb419a7c000)
	libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007fb41985a000)
	libxshmfence.so.1 => /usr/lib/x86_64-linux-gnu/libxshmfence.so.1 (0x00007fb419657000)
	libXxf86vm.so.1 => /usr/lib/x86_64-linux-gnu/libXxf86vm.so.1 (0x00007fb419451000)
	libdrm.so.2 => /usr/lib/x86_64-linux-gnu/libdrm.so.2 (0x00007fb419243000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fb41c62f000)
	libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007fb41903f000)
	libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007fb418e39000)

Dzięki ldd wiedziałem że brakuje pliku libGL.so.1. Sprawdziłem, że powinien znajdować się w pakiecie libgl1-mesa-glx, więc sensownym posunięciem wydaje się ponowna instalacja tego pakietu.

# aptitude reinstall libgl1-mesa-glx

Niestety, to nie pomogło. Archiwum z pakietem zawierało potrzebny plik, ale instalator z nieznanych mi względów go nie kopiował. Zrobiłem to za niego, skopiowałem z:

/var/cache/apt/archives/libgl1-mesa-glx_10.5.9-2_amd64.deb

do:

/usr/lib/x86_64-linux-gnu/libGL.so.1
/usr/lib/x86_64-linux-gnu/libGL.so.1.2.0

Instalacja Debiana: nie wykryto napędu CD-ROM

Od dawna instaluję Debiana z pendrive’a. Zawsze używałem programu UNetbootin do nagrywania obrazu płyty instalacyjnej na pendrive’ie. Zazwyczaj instalacja zajmuje mi jakieś 30 minut. Ale nie tym razem. Tym razem męczyłem się z instalacją jakieś trzy godziny.

Zaczęło się dobrze: laptop uruchomił się z pendrive’a, pokazał menu instalatora, wybrałem instalację graficzną. W kolejnych krokach wybrałem język polski i polski układ klawiatury, a potem… instalator oświadczył, że nie wykrył CD-ROMu i zapytał czy chcę załadować sterowniki z innego nośnika.

Pominę pełen opis prób i błędów, które popełniłem, wymienię je tylko w telegraficznym skrócie:

  • To błąd w instalatorze, należy spróbować innego obrazu płyty.
  • Ponoć pendrive sformatowany na FAT32 może sprawiać problemy. Lepiej użyć FAT16.
  • Pendrive należy włożyć w port USB 2, porty USB 3 mogą sprawiać problemy.
  • Po wyświetleniu komunikatu należy wyjąć i ponownie włożyć pendrive.
  • W menu BIOS/UEFI należy zmienić opcję obsługi dysków na AHCI.

Tymczasem rozwiązaniem okazało się nagranie obrazu płyty na pendrive’a poleceniem dd:

dd bs=4M if=/home/michal/Pobrane/debian-testing-amd64-netinst.iso of=/dev/sdb && sync

PS. Laptop to był Dell Inspiron z serii 3000, nic fikuśnego. Niestety standardowo miał zainstalowanego Windowsa.

Tryb recovery w Motoroli Moto G z Androidem 5

Aktualizacja mojej Motorolki Moto G (pierwszej generacji) do Androida 5.0.2 była słabym pomysłem. Telefon z czasem zaczyna działać coraz wolniej, czasami zamyśla się na kilkanaście sekund przy prostych operacjach, odczuwalna jakość interakcji z urządzeniem jest wyraźnie gorsza. Kit Kat był w tym modelu o wiele lepiej dopracowany.

Jednym z pomysłów na poprawę sytuacji było wyczyszczenie partycji /cache urządzenia, co wymaga uruchomienia telefonu w trybie recovery. Wyszukiwarka usłużnie podaje liczne instrukcje, z których większość jest… błędna. Okazuje się, że przy Androidzie 5 wywołanie trybu recovery jest inne niż w telefonie z Kit Katem. Zanim do tego doszedłem przez blisko 10 minut bezowocnie międliłem klawisze. Poniżej działająca procedura.

  1. Wyłączyć telefon.
  2. Przytrzymać przez kilka sekund jednocześnie przyciski zwiększania głośności, zmniejszania głośności i włącznik.
  3. Używając przycisku zmniejszania głośności w menu należy podświetlić opcję Recovery, a następnie zatwierdzić wybór przyciskiem zwiększania głośności.
  4. Gdy na ekranie pojawi się leżący na plecach android oraz napis „no command” należy przytrzymać włącznik i nacisnąć przycisk zwiększania głośności.
  5. Na ekranie objawi się menu trybu Recovery, z którego można wybrać między innymi czyszczenie partycji cache. Wybór zatwierdza się przyciskiem włączania telefonu.

Skuteczność tej operacji ociera się o efekt placebo; wydaje mi się, że jest ciut lepiej, ale na pewno nie jest dobrze. Z utęsknieniem czekam na Androida 5.1.

Aktualizacja nginx do 1.8.0 i problem z PHP

Dzisiaj zespół dotdeb.org wypuścił nginxa 1.8.0 na Debiana, a ja niezwłocznie dokonałem aktualizacji z wersji 1.6.3. Początkową radość szybko zastąpiła panika, kiedy okazało się, że wszystkie strony oparte o PHP przestały działać, a konkretnie zaczęły zwracać pustą treść. W logach nginxa, PHP-FPM ani nigdzie indziej nie znalazłem błędów, w konfiguracji nginxa zmiany były tylko kosmetyczne, konfiguracja PHP w ogóle nie była ruszana, po prostu czeski film.

Rozwiązanie okazało się trywialne: w pliku /etc/nginx/fastcgi_params należy dodać linijkę:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

Potem już tylko przeładowanie konfiguracji nginxa i wszystko powinno działać.

Tytułem uzupełnienia dodam, że na właściwy trop trafiłem, kiedy spróbowałem połaczyć się z PHP-FPM bezpośrednio przez FastCGI. Wedle przepisu z tej strony w konsoli zapodałem:

SCRIPT_NAME=/index.php \
SCRIPT_FILENAME=/index.php \
REQUEST_METHOD=GET \
DOCUMENT_ROOT=/var/www/anime.com.pl/ \
cgi-fcgi -bind -connect /var/run/php5-fpm.sock

W odpowiedzi otrzymałem:

Primary script unknown
Status: 404 Not Found
Content-type: text/html

File not found.

Dalej już było z górki. Ciekawe tylko dlaczego to się nigdzie nie zalogowało?

Jako lekturę uzupełniającą polecam wpis z bloga Martina Fjordvalda, który tłumaczy czym różni się plik fastcgi_params od fastcgi.conf i skąd się ta różnica wzięła.

Wyłączanie filtrów w Doctrine2

Filtry w Doctrine to poniekąd pożyteczna funkcja, na filtrach jest oparte m.in miękkie kasowanie (ang. soft delete), które można podpiąć z paczki gedmo/doctrine-extensions. Filtry przeważnie są włączane na stałe w konfiguracji ORM-a w pliku config.yml, ale w razie potrzeby można je włączać i wyłączać w kodzie. Obiekt FilterCollection pobieramy z entity managera, a na nim można wykonać m.in. poniższe metody:

// sprawdzenie czy filtr jest zainstalowany
$em->getFilters()->has("soft-deleteable");

// sprawdzenie czy filtr jest włączony
$em->getFilters()->isEnabled("soft-deleteable");

// włączenie filtra
$em->getFilters()->enable("soft-deleteable");

// wyłączenie filtra
$em->getFilters()->disable("soft-deleteable");

Cała ta notka wzięła się stąd, że dłużej niż powinienem szukałem błędu w kodzie, a jego źródłem było właśnie miękkie kasowanie. Zapytanie, które powinno zwracać encję zwracało NULL, a taki przypadek nie był obsłużony w kodzie.

Roundcube przez HTTPS czyli nginx i self-signed certificate

Powinienem to zrobić dawno temu, ale dopiero teraz zabrałem się za zabezpieczenie SSL-em mojego webmaila opartego na Roundcube. Ponieważ poza mną nikt z niego nie korzysta, a sam sobie zazwyczaj ufam, to zdecydowałem się nie korzystać z żadnego centrum certyfikacji i wygenerować certyfikat własnoręcznie.

W sieci nie brakuje instrukcji generowania certyfikatów, ja staram się robić wszystko po debianowemu, więc kierowałem się opisem z debianowej wiki. Dlatego certyfikat wylądował w katalogu /etc/ssl/localcerts, który musiałem stworzyć:

mkdir -p /etc/ssl/localcerts

Polecenie generowania klucza i certyfikatu:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/localcerts/mail.komitywa.net.key -out /etc/ssl/localcerts/mail.komitywa.net.crt

Przed wygenerowaniem certyfikatu zostaniemy zapytani o dane certyfikowanego podmiotu:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:PL
State or Province Name (full name) [Some-State]:Mazowieckie
Locality Name (eg, city) []:Warsaw
Organization Name (eg, company) [Internet Widgits Pty Ltd]:komitywa.net
Organizational Unit Name (eg, section) []:.
Common Name (eg, YOUR name) []:mail.komitywa.net
Email Address []:moj-adres-email@komitywa.net

Na koniec warto ograniczyć dostęp do wygenerowanych plików:

chmod 600 /etc/ssl/localcerts/mail.komitywa.net.*

Teraz można już SSL do konfiguracji nginx’a. Do działającego wirtualnego hosta dodałem:

listen 443 ssl;
ssl_certificate /etc/ssl/localcerts/mail.komitywa.net.crt;
ssl_certificate_key /etc/ssl/localcerts/mail.komitywa.net.key;

Po przeładowaniu nginx’a i przetestowaniu, że połaczenie po HTTPS działa postanowiłem jeszcze dodać przekierowanie z HTTP na HTTPS:

server {
       listen 80;
       server_name mail.komitywa.net;
       return 301 https://mail.komitywa.net$request_uri;
}

Wygenerowany certyfikat jest ważny przez rok, więc w kwietniu 2016 będę musiał wygenerować nowy certyfikat.