Protokół PolChat


Nieoficjalna dokumentacja protokołu POLCHAT 3.0

(cc) 2007 - 2016 - Marcin 'TheDoctor' Grzechowiak, egonfreeman@gmail.com

Rewizja: 0.4.0.1; data ostatniej modyfikacji: 3. września 2016r.





Wstęp do wstępu, dn. 3. września 2016 roku.


Polchat umarł. Śmierć zanotowano dn. 5. listopada 2012 roku, w/g serwisu antysop.info. W związku z tym faktem wiele projektów budujących na sukcesie serwisu Polchat umarło wraz z nim, wliczając w to P3, TheRaven i inne. Więc po co wskrzeszać zaginiony opis protokołu dla serwera, który na dzień publikacji tego wstępu jest pogrzebany od prawie 4 lat? Cóż – powody są dwa.

Po pierwsze – w końcu udało mi się odkopać w miarę aktualną (jeśli nie najnowszą) wersję tego dokumentu. Straciłem go wraz z wieloma innymi rzeczami w Wielkiej Awarii 2014, kiedy to padły mi zasadniczo wszystkie sprzęty. Chciałem, aby ten dokument gdzieś sobie wisiał i istniał – więc niech wisi i istnieje.

Po drugie, i chyba ważniejsze – zdążyłem już wiele zapomnieć, a ostatnio naszła mnie ochota na odświeżenie wiedzy i odkurzenie paru narzędzi z antycznych półek... a do tego celu potrzebny mi był w miarę kompletny opis protokołu. Na sieci nadal istnieje kilka 'klonów-córek' serwera Polchat (najczęściej w wersji 2.0/2.1), dla których nadal rozwijane jest oprogramowanie (np. klient czatowy na Androida) - jednym z takich serwisów jest Polfan.pl. Nie mam pojęcia co do ich legalności, ale w niczym nie przeszkadza to rozwojowi narzędzi na te platformy.

W każdym razie... nadszedł dzień wskrzeszenia tego dokumentu, co niniejszym czynię (korzystając przy tem z okazji na naniesienie kilku poprawek kosmetycznych). Nie przedłużając więc - zapraszam do lektury i budowania projektów w oparciu o protokół Polchat. Może i umarł, ale zombie zombie zombie. ;-)

— Autor

Wstęp


Nie miałem dotychczas okazji znaleźć kompletnego i niezwiązanego z konkretną implementacją opisu protokołu - dlatego postanowiłem napisać własny. Historia mojej 'współpracy' z Polchatem nie jest jakoś specjalnie długa - bardzo wiele pomogli mi tutaj inni, którzy mieli z nim więcej do czynienia, a także kilka osób równie ciekawych jego pracy jak i ja.

Osobiście jestem programistą PHP (tak, używam języka PHP do pisania programów wykonywalnych .exe), więc mogą pojawić się pewne naleciałości z tego języka - np. analiza pakietów jest dla mnie o niebo łatwiejsza w implementacji PHP, gdyż moje wysiłki wsparte są funkcją unpack().

Miej także, miły Czytelniku, na uwadze fakt, iż opis ten nie jest do końca sztywną specyfikacją, a raczej luźnym podejściem do zebrania i posegregowania wiadomości które - jako ogół czatowiczów - wyszukaliśmy, wyniuchaliśmy i zebraliśmy przez te parę lat pracy systemu. Większość wiadomości pochodzi z moich własnych prób rozgryzienia 'co i jak', lecz nie bałem się prosić o pomoc innych (patrz rozdział 'Podziękowania').

Jeśli zauważysz jakiś błąd, lub doszukasz się czegoś co przeoczyłem - nie wahaj się powiadomić mnie o tym! Mój adres email znajdziesz na górze tego dokumentu, oraz w dziale 'Pytania, korekty'. Pod adresem www http://kadtech.info/polchatproto/ znajduje się zawsze najnowsza wersja tego dokumentu - jeśli znajdziesz uchybienie, sprawdź najpierw, czy dokument który masz jest najświeższą rewizją.

Dokument niniejszy udostępniony jest na warunkach licencji Creative Commons 'ATTRIBUTION' (link otwiera się w nowym oknie) - co oznacza, że możesz go kopiować, rozpowszechniać, edytować, tworzyć zbiory zebrane wliczające ów dokument, a nawet sprzedać - pod warunkiem, że w jasny i czytelny sposób, w widocznym miejscu, zawrzesz informację o autorze tego dokumentu.

Przy okazji - słowo dla wszystkich tych, którzy uważają, iż publikacja protokołu Polchatu spowoduje nagły napływ flooderów, spammerów etc. - przykro mi to wam powiedzieć, ale Polchat jest już pełny tych programów, a ja mam nadzieję na to, że opis protokołu pomoże tym, którzy mają pomysły na np. dobrego bota...

UWAGA: Czytelnik powinien być dobrze zaznajomiony ze sposobem działania serwera Polchatu (od strony użytkownika / czatowicza) przed przystąpieniem do lektury. Nie jest to oczywiście absolutnie konieczne, ale bez tej wiedzy niektóre elementy niniejszego opisu będą wydawać się 'oderwane od siebie' i niespójne.



1 – Podstawowe informacje


1.1a – Słowo o formacie

Protokół Polchatu, ze względu na stosunkowo dużą ilość użytkowników na nim bytujących (a przynajmniej taki projekt) jest protokołem kompaktowym. Mam przez to na myśli fakt, iż używa własnego formatu ciasnego pakowania danych (w przeciwieństwie do np. protokołu Tlena, który używa przejrzystego - ale i bogatego w często zbędne elementy formatu - XMLa). W związku z tym przy komunikacji potrzebna jest, poza innymi, umiejętność 'rozpakowywania' pakietu do formatu zdatnego do obróbki.

Pomijając ciasne pakowanie pakietu, warto wspomnieć o jednej ważnej rzeczy: wszystkie ciągi tekstowe (STRINGi) w komunikacji z serwerem kodowane są do formatu UTF-8 - wypadałoby więc upewnić się, że mamy odpowiednie komponenty do obróbki tego formatu, zanim zaczniemy próbować nawiązać połączenie.

1.1b – Konwencje w tym dokumencie

Informatycy to cholernie leniwy naród - inaczej po co spędzalibyśmy niezliczone dni, aby zautomatyzować nasze 5-minutowe zadania? ;-) W związku z tym niniejszym przedstawiam konwencję nazewnictwa, której będę się trzymać w całym niniejszym `dokumencie`:

null — 8-bitowa wartość '0' (byte:0, char:0 - jak chcecie ;P)
string* — ciąg znaków o długości max. FF FF, rozpoczęty dwubajtową długością, zakończony NULLem**
char[] — tablica znaków, o wielkości równej ilości znaków (tylko tam, gdzie wyjaśniam stringi)
struktura — określa pewną zamkniętą całość, coś jak struct{}

* Joel Spolsky nazywa takie stringi 'fucked string', bo mają i długość, i NULL na końcu. Po co? Kto wie...
** NULL nie zalicza się do długości STRINGa; minimalny string ma podaną długość 0 i jest opisany trzema bajtami (długość + NULL)

Wszystkie struktury, pakiety i przykłady prezentować będę w tej czy innej formie pseudokodu - faktyczną implementację w dowolnym języku pozostawiam Czytelnikowi.

Kolega zwrócił mi uwagę na fakt, że użycie słowa 'int' dla wartości 16-bit i 'bigint' dla wartości 32-bit może być i jest mylące dla ludzi programujących głównie w C/C++. W związku z tym podmieniłem te słowa w całym dokumencie na konkretne wielkości bitowe, o które chodzi. Zauważ, Czytelniku, że w tekście wyjaśnień oraz w nazwach zmiennych wciąż używam słowa INT - jest to wykorzystanie tylko obrazowe, chodzi o INTy 16-bitowe (smallint / word).

1.2 – Budowa pakietu

Każdy pakiet przesyłany między serwerem a klientem charakteryzuje się sztywną konstrukcją - składa się, jak to zwykle bywa, z nagłówka oraz treści. Nagłówek pakietu ma długość 10 bajtów, a struktura jego jest następująca:

struktura naglowek_pakietu
{
   32-bit dlugosc; // długość pakietu, włącznie z nagłówkiem
   16-bit intnum;  // ilość INTów w pakiecie, włącznie z ID pakietu (min.: 1)
   16-bit strnum;  // ilość STRINGów w pakiecie, min. 0, max... ? ;)
   16-bit ID;      // ID pakietu (jest to jednocześnie pierwszy int w pakiecie)
};

Po nagłówku występują dane (treść), składające się z 16-bitowych liczb całkowitych, a po nich - ciągów znaków. Budowa 16-bitowego INTa nie jest specjalnie trudna - jest to wartość dwubajtowa, prezentowana w formie Big-Endian. Budowa ciągów tekstowych jest (dla pewności zrozumienia) następująca:

struktura string
{
   16-bit dlugosc; // długość ciągu znaków, dla np. 'abc' byłoby to 3
   char[] zawartosc;  // ciąg znaków
   null;  // zakończenie ciągu znaków
};

   Przykład - ciąg 'abc':
   00 03 61 62 63 00

Wynikowa struktura dowolnego pakietu obrazuje się mniej-więcej tak:

struktura pakiet
{
   32-bit Dlugosc;
   16-bit IloscIntow;
   16-bit IloscStringow;
   16-bit ID_Pakietu;

   16-bit Int1; // ilość INTów zależy od typu pakietu, mogą wcale nie wystąpić
   ...
   16-bit IntN;

   string Tekst1; // ilość STRINGów zależy od typu pakietu, mogą wcale nie wystąpić
   ...
   string TekstN;
}

   Przykład pakietu czatowego o treści 'Witaj' (ID pakietu: 610):
   [00 00 00 12] [00 01] [00 01] [02 62] [00 05 57 69 74 61 6A 00]

Zwróć uwagę, drogi Czytelniku, że choć tutaj prezentuję budowę nagłówka pakietu, nie będę (w przeciwieństwie do ekg.chmurka.net) prezentować w dalszej części tylko 'reszty' pakietu - z dwóch powodów: pierwszy - ponieważ nagłówek pakietu się zmienia (jego wartości, przynajmniej); po drugie - w opisie protokołu Gadu-Gadu długo nie mogłem wykombinować dlaczego serwer mnie rozłącza... Okazało się, że wskoczyłem od razu 'w środek' całości, i zbudowałem wszystkie struktury bez nagłówka, który opisany był tylko na początku dokumentu. Drugim powodem więc jest ułatwienie i logiczna całość.

1.3 – Bezpieczeństwo

System Polchat w aktualnej jego implementacji* ma jedną, poważną wadę: cała komunikacja z serwerem odbywa się w postaci niezaszyfrowanej (plain-text). W związku z tym Polchat nie jest odporny na ataki typu man-in-the-middle czy zwykły podsłuch. Połączenie jest szczególnie wrażliwe w czasie jego rozpoczęcia, kiedy w pierwszym pakiecie przesyłamy w czystym tekście nazwę użytkownika oraz hasło.

Cała treść rozmów, opisy pokoi etc. przesyłane są w protokole w postaci kodu HTML (podejrzewam, że w celu ułatwienia budowy appletu Java). Stwarza to możliwości wstrzykiwania kodu JavaScript (np. OnMouseOver). Problem został załatany w serwerze w wersji 3.0, ale wszystkie wcześniejsze wersje są podatne na tego typu ataki. Wiele klientów czatowych (ICeQ, NPCC, KCIbot, P3 etc.) rozwiązuje ten problem po swojej stronie, najczęściej za pomocą aktywnej filtracji wiadomości.

* W czasie pisania rewizji 0.4 tego dokumentu, w rozwoju był protokół Polchat 3.0 oraz 3.1 (rozszerzający 3.0), który w moim założeniu m.in. miał wprowadzić szyfrowanie połączeń metodą AES z wstępnym zabezpieczeniem wymiany współdzielonego hasła drogą RSA (podobnie do tego, jak robił to program Tlen.pl). Niestety, odejście głównego developera (de facto praktykanta) z zespołu skutecznie zatrzymało wszystkie prace związane i z 3.0, i z 3.1 - jedyne co docelowo znalazło się w rewizji 3.0 to różne usprawnienia administracyjne (m.in. komenda /mute, czy restrykcja kopania/banowania operatorów o wyższych niż własne uprawnieniach).

2 – Komunikacja z serwerem


2.1 – Połączenie i zalogowanie się

Połączenie z serwerem należy kierować bezpośrednio na adres i port serwera czatowego (historycznie było to s1.polchat.pl i port 14003). Typowym portem serwera nadal pozostaje 14003 (Polfan.pl w swoim "aplecie" twierdzi, że jest to 14080, ale jest to nieprawdą). Pierwszym pakietem który musimy wysłać - jest pakiet logowania. Ma on następującą strukturę:

struktura pakiet_logowania
{
   32-bit Dlugosc;
   16-bit IloscIntow = 1; // tylko ID pakietu
   16-bit IloscStringow = 8; // tak, trochę tych danych bedzie ;-)
   16-bit ID_Pakietu = 1400;

   string Uzytkownik; // nazwa czatowicza
   string Haslo; // hasło do nicka

   string UserCookie; // ten string jest PUSTY, nie widziałem aby COKOLWIEK go używało (00 00 00)

   string NazwaPokoju; // jest to pokój, do którego przejdziemy tuż po zalogowaniu
   string Referrer; // o tym poniżej

   string AdresSerwera; // nie wiem po co to, ale musi być podany adres serwera z którym się łączymy

   string Konfiguracja; // o tym też poniżej

   string NazwaNaszegoProgramu; // nazwa klienta, którego używamy (3.0 tego wymaga!)
}

ad. 'Referrer': jest to ciąg znaków identyfikujących stronę która wysłała nas do serwera - historycznie (w wersji 1.x i wczesne 2.0) było to używane w celu sprawdzenia, że klient loguje się z appletu Java danego serwera, a nie innego. Dla każdego serwera jest on inny, więc trzeba ręcznie 'wybadać sprawę' przed połączeniem. Dla wielu serwerów nie ma on już znaczenia, ale jeśli strona/applet go podaje, dla bezpieczeństwa należy go także podać. Historycznie dla głównego serwera Polchatu miał on wartość:

http://www.polchat.pl/chat/?room=nazwa_pokoju

Program ICeQ korzystał z 'http://www.polchat.pl/chat/?room=ikari', co idealnie obrazuje fakt, że pole to jest zasadniczo nieistotne (podana nazwa pokoju mogła być dowolna).

ad. 'Konfiguracja': jest to ciąg znaków określający nasze podejście do czatu ;-) Przykładowa jego wartość to np.:

nlst=1&nnum=1&jlmsg=true&ignprv=false

nlst - nick list (1/0) - czy chcemy dostawać userlistę (w ogóle)
nnum - nick num (1/0) - czy chcemy dostawać pakiet z ilością osób na pokoju (zamiast userlisty? ;P)
jlmsg - join/leave message (true/false) - czy chcemy otrzymywać '** Przychodzi...' i '** Wychodzi...'
ignprv - ignore priv (true/false) - czy chcemy otrzymywać wiadomości prywatne (tak czy nie)

Warto zwrócić tutaj uwagę, że ustawienie otrzymywania "Przychodzi..." i "Wychodzi..." (jlmsg) ograniczało się tylko do otrzymywania wiadomości TEKSTOWYCH o tych zdarzeniach. Wiadomości w protokole (skutkujące np. wykreśleniem wychodzącego użytkownika z listy czatujących) są konieczne i wysyłane niezależnie od powyższego ustawienia.

O wiele większy wpływ na czat ma czwarte pole, tj. ignprv. Ustawienie go na false zaskutkuje całkowitym zaprzestaniem wysyłania nam pakietów 611 (priv).

2.1.1 – Błędy połączenia / logowania

W przypadku gdy nasza próba logowania nie powiedzie się, otrzymamy od serwera pakiet zamykający połączenie, wraz z wyjaśnieniem przyczyny. Naszym obowiązkiem w tym momencie jest rozłączyć się. Pakiet błędu ma ID 65535 (FF FF).

Znane są następujące przyczyny błędu logowania. Przyczyna numer #1 jest banalna:

Wybrany identyfikator jest aktualnie w użyciu.

co oznacza tyle, że musimy dobrać sobie inny. ;-)

Przyczyna numer #2 już nie jest taka przyjazna:

Nieprawidłowy identyfikator lub hasło użytkownika.

... no i w tym momencie mamy zagwozdkę, ponieważ wyjaśnienie to opisuje trzy stany:
- przypadek kiedy identyfikator / nick zawiera niedozwolone znaki,
- przypadek kiedy wpiszemy złe hasło dla nicka który jest zarejestrowany, oraz
- przypadek kiedy wpiszemy jakiekolwiek hasło dla nicka który nie jest zarejestrowany.

Trzeci przypadek to już bezpardonowa bezczelność:

Dostęp do systemu z tego adresu IP jest aktualnie niemożliwy. Spróbuj później.

co oznacza ni mniej, ni więcej, tylko tyle, że dostaliśmy s-bana (zazwyczaj w wyniku floodowania, wtedy nazywa się to 'floodban').

Czwarty przypadek zdarza się rzadziej niż pozostałe, ale się zdarza:

Logowanie do systemu jest tymczasowo niemożliwe.

co oznacza tyle, że coś z serwerem do którego się łączymy jest poważnie 'nie tak' (najczęściej oznacza to, że nickserver leży). Można próbować połączyć się na nicku tymczasowym...

W ostateczności możemy jeszcze nie połączyć się w ogóle - co oznacza, że serwer po prostu leży jak długi... co się zdarza, rzadko - ale jednak.

Warto tutaj wspomnieć o przypadkach, kiedy otrzymujemy pakiet kończący połączenie od razu po połączeniu z serwerem. Jest to tożsame z wyłączonym ('leżącym') serwerem, może być jednak mylące - serwer Polchat do połączeń socketowych używa zewnętrznego socketa (wewnętrznie wszystko biegnie po named sockets lub pipes), i to on nas rozłącza stwierdzając, że nie ma nas dokąd wysłać... ;-)

Może także nastąpić sytuacja typu 'mogę wejść przez applet, ale nie przez klienta' - oznacza to tyle, że socket leży (tak, miewa tendencje do umierania czasem). W takim wypadku trzeba po prostu poczekać, aż go ktoś podniesie.

UWAGA: w przypadku s-bana przy próbie połączenia nie zawsze otrzymamy informację o tym! Miewałem przypadki, w których po prostu dostawałem informację o tym, że 'połączenie z serwerem zakończone porażką(?)' zamiast 'Dostęp z adresu IP Twojego komputera jest obecnie niemożliwy'. Nie wiem dlaczego tak się dzieje, ale się to zdarza.



2.2 – Pakiety powitalne

Jeśli jakimś cudem wszystko poszło dobrze, serwer odpowie nam przesyłając dwa pakiety powitalne - pakiet ID 626 oraz zwykły tekst "Witaj na czacie! :)" (o nim za chwilę). Pakiet ID 626 zawiera wiele informacji jednocześnie (składają się na niego dwa STRINGi). Przykład:

color_user=#000000&color_op=#FF0000&color_guest=#0000df #00bfbf #df00df #dfdf00 
#000000 #2080FF&password_protection=0&room_creation=1&conv_table=żŻźűśŚóĂ
“Ĺ„ĹƒĹ‚ĹÄ™Ä˜Ä‡Ä†Ä…Ä„Â zzzzssoonnlleeccaa 

/ /Regionalne/ /Regionalne/Polska/
 /Regionalne/Świat/ /Towarzyskie/ /Towarzyskie/Rówieśnicy/ /Hobby/ /Internet/ /K
omputer/ /Komputer/Gry/ /Motoryzacja/ /Muzyka/ /Polityka/ /Praca/ /Radio/ /Rozry
wka/ /Religia/ /Różne/ /Sport/ /Szkoła/ /Telewizja/ /Zdrowie/ /Erotyczne/

Pakiet ten najłatwiej analizować w dwóch kawałkach - drugi STRING w jasny (widoczny gołym okiem) sposób prezentuje listę kategorii pokoi, pierwszy zaś zawiera parametry potrzebne do współpracy. Kolejno są to:

color_user - kolor zwykłego użytkownika
color_op - kolor operatora
color_guest - lista kolorów (oddzielonych spacjami) możliwych do nadania za pomocą /guest (1..x)
password_protection - informacja dla appletu Java czy może wykonać procedurę zmiany/założenia hasła
room_creation - także nie jest mi specjalnie znany, domyślam się iż chodzi o tworzenie niezarej. pokoi
conv_table - tablica konwersji znaków - jakie znaki wymieniać na jakie ;-)

Tablica konwersji znaków zasługuje na sumaryczne olanie, gdyż nie znajduje zastosowania przy połączeniu oprogramowaniem obsługującym kodowanie UTF-8 (podejrzewam, że jej funkcja sprowadzała się do konwersji na ISO-8859-2 lub podobnych przypadków).

Nazwy kategorii (włącznie z nadrzędną '/') oddzielone są od siebie spacją.

UWAGA: od momentu, kiedy otrzymamy tablicę konwersji znaków, dalsze wiadomości tekstowe (włącznie z listą kategorii) przesyłane są w postaci UTF-8!

Drugi pakiet powitalny to tekst "Witaj na czacie! :)", przesyłany zwykłym pakietem o ID 610. UWAGA: w protokole 3.0 nastąpiła zmiana także tego pakietu, więc polecam poczytać o nim poniżej. Pakiet ten, jak wszystkie teksty pojawiające się w zakładce systemowej, ma pusty string nazwy pokoju.



2.3 – Pokoje

Cała idea systemu Polchat opiera się na wieloużytkownikowych pokojach, w których użytkownicy mogą brać udział we wspólnej dyskusji (rozmowy prywatne są tylko dwuosobowe). Czas na garść informacji o tychże.

2.3.1 – Wejście do pokoju

Zaraz po poprawnym zalogowaniu się i odebraniu pakietów powitalnych, zostaniemy wrzuceni do pokoju który określiliśmy w pakiecie log-in. Procedura wejścia do pokoju jest... bardziej skomplikowana niż inne. Może nawet trochę za bardzo. Ogólna procedura jest następująca:

Krok #1: otrzymujemy pakiet o numerku 630, o typowej zawartości 'Wchodzisz do pokoju...' + nazwa
             pokoju, w którym to nastąpiło
Krok #2: otrzymujemy pakiecik 625, zawierający dwa STRINGi: nazwę pokoju, oraz jego opis
Krok #3: otrzymujemy listę użytkowników (ID pakietu: 619) - o niej w rozdz. 2.5.1 'Lista użytkowników')
Krok #4: wszyscy obecni (w tym i my) (w zależności od ustawienia jlmsg) dostają pakiet (610) o
             typowej treści 'Przychodzi...'
Krok #5: wszyscy obecni (także my) otrzymują pakiet 615 (join) z informacją o tym, że dołączamy
Krok #6: tylko my - otrzymujemy pakiet 618, informujący nas że my... to my! ;-)

Kilka słów wyjaśnienia:

nie ma co ukrywać: pakiet 610 jest pakietem służącym do wyświetlenia informacji na
ekranie (np. wypowiedzi czatowicza) - nic więcej ;P

pakiet 615 jest rozsyłany do wszystkich obecnych w pokoju użytkowników, jest to pakiet
informujący ich o tym, że mają dopisać nowy nick do listy obecności; zawiera także dwie
flagi stanów (globalną i lokalną) - o nich w rozdziale 2.5

pakiet 618 jest wysyłany tylko do osoby która dołącza do pokoju - jest to informacja dla
appletu Javy (i dowolnego innego klienta), iż ma nasz własny nick wyróżnić innym kolorem;
składa się z jednego INTa o wartości 4, i STRINGa - naszego nicka


Nowe pakiety:
= 630 - składa się z dwóch STRINGów - wiadomości, oraz nazwy pokoju
struktura WchodziszDoPokoju
{
   32-bit dlugosc;
   16-bit ilosc_intow = 1;
   16-bit ilosc_stringow = 2;
   16-bit ID = 630;

   string Wiadomosc; // 'Wchodzisz do pokoju [nazwa]...'
   string Pokoj; // nazwa pokoju, do którego wchodzisz (służy do umieszczenia w odp. zakładce)
}

= 625 - składa się z dwóch STRINGów - nazwa_pokoju, opis_pokoju:
struktura Opis_Pokoju
{
   32-bit dlugosc;
   16-bit ilosc_intow = 2;
   16-bit ilosc_stringow = 2;
   16-bit ID = 625;
   16-bit Nieznany = 0; // niektórzy pytali mi się o ten INT, nie wiem co to jest ^^

   string NazwaPokoju; // 'Nastolatek'
   string OpisPokoju; // 'http://nastolatek.pl'
}

= 619 - userlista, o niej później

= 610 - pakiet służący do wyświetlenia wiadomości tekstowej w oknie czata:
// !!! przypominam -- jeśli wyłączyliśmy (jlmsg) odbieranie informacji przychodzi/wychodzi,
//      nie otrzymamy tego pakietu!
struktura Wiadomosc_Do_Wyswietlenia
{
   32-bit dlugosc;
   16-bit ilosc_intow = 1;
   16-bit ilosc_stringow = 2;
   16-bit ID = 610;

   string Wiadomosc; // 'Przychodzi [nick]...'
   MODAL string Pokoj; // służy do umieszczenia we właściwej zakładce

   // UWAGA!!! string Pokoj w pakiecie 610 jest dosc... ciekawy :)
   // dlatego MODAL - bo w pewnym sensie jest modalny:
   // jesli podasz, wiadomosc zawedruje do tego pokoju
   // jesli NIE podasz, zawedruje na zakladke systemowa
   // w ICeQ 5.10 i nowszym - jesli podasz PUSTY,
   // wiadomosc poleci na KAZDA zakladke ;-)
}

= 615 - JOIN - składa się z jednego INTa (stan_globalny), oraz trzech STRINGów - nick nowej osoby,
  pokój w którym to się odbyło, oraz nazwę jego klienta (programu)
struktura Join
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 2;
   16-bit Ilosc_Stringow = 3;
   16-bit ID = 615;

   16-bit StanGlobalny;
   string Nick;
   string Pokoj;
   string Program_Klienta; // tutaj pojawia sie informacja o tym, jakiego programu koleś używa ;)
}

= 618 - składa się z jednego INTa (wartość: 4) i jednego STRINGa - naszego nicka
struktura LocalStatusChange
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 2;
   16-bit Ilosc_Stringow = 1;
   16-bit ID = 618;

   16-bit StanLokalny = 4;
   string Nick;
}

Należy pamiętać, że do pierwszego pokoju wysyłani jesteśmy bez naszej interwencji - do wszystkich innych musimy wyrazić tę chęć sami (wydając polecenie /join nazwa_pokoju).

UWAGA: czasami nie zostaniemy przeniesieni do pokoju. Objawia się to tym, że po połączeniu i otrzymaniu pakietów konfiguracyjnych następuje... cisza. Dzieje się to tylko wtedy, gdy jako pokój startowy podamy pokój nieistniejący ('tymczasowy') lub pokój utworzony przez użytkownika. Należy wtedy spróbować wejść do jakiegokolwiek pokoju głównego (polchat, erotyczny, 40-lat etc.). Są duże szanse, że akurat trafiliśmy na moment, kiedy wystąpił na serwerze błąd uniemożliwiający a) tworzenie 'tymczasowych' pokoi, i b) dostęp do pokoi użytkowników (nie-głównych). Zdarza się to zupełnie losowo, i najczęściej nie zostaniemy o tym w żaden sposób poinformowani -- /join [nazwapokoju] po prostu nie przyniesie jakiegokolwiek skutku (czasami otrzymujemy wiadomość "** przejście do (innego?) pokoju jest tymczasowo niemożliwe"). Należy tę sytuację po prostu przeczekać. Powyższa sytuacja nie dotyczy pokoi głównych - do nich można wchodzić zawsze.

2.3.2 – Zmiana pokoju / wyjście z pokoju

Kiedy mówię o zmianie pokoju, mam na myśli sytuację kiedy jesteśmy już w pokoju, i chcemy go zmienić. W przypadku kiedy nie jesteśmy w żadnym pokoju (/part), przejście do dowolnego pokoju ma identyczny przebieg jak napisałem wyżej (2.3.1).

Właściwie tutaj należałoby tylko dodać informację o opuszczaniu pokoju - jest to procedura dwu-/trzystopniowa, w zależności od tego kto wychodzi - my, czy ktoś inny:

Krok #1: wszyscy w pokoju otrzymują pakiet 610 o treści 'Wychodzi...'
// !!! przypominam -- jeśli wyłączyliśmy (jlmsg) odbieranie informacji przychodzi/wychodzi,
//      nie otrzymamy tego pakietu!

Krok #2: jako jedyny, wychodzący otrzymuje pakiet 631 o treści 'Opuszczasz pokój...'

Krok #3: wszyscy pozostali otrzymują pakiet 616 (leave), informujący ich że trzeba wykreślić nicka z listy


Nowe pakiety:
= 631 - składa się z dwóch STRINGów, typowo '** Opuszczasz pokój [nazwa pokoju]' + pokój
struktura WychodziszZPokoju
{
   32-bit dlugosc;
   16-bit ilosc_intow = 1;
   16-bit ilosc_stringow = 2;
   16-bit ID = 631;

   string Wiadomosc; // 'Opuszczasz pokoj [nazwa]...'
   string Pokoj;
}

= 616 - składa się z dwóch STRINGów - nicka osoby, która wyszła + pokój
struktura Leave
{
   32-bit dlugosc;
   16-bit ilosc_intow = 1;
   16-bit ilosc_stringow = 2;
   16-bit ID = 616;

   string Nick;
   string Pokoj;
}

Chęć wyjścia z pokoju sygnalizuje się wydanie polecenia /part [opcjonalne pożegnanie]. Chęć wejścia do kolejnego pokoju sygnalizuje się poleceniem /join [nazwa pokoju]. Jeśli zmieniamy pokój, musimy najpierw wydać /part i obsłużyć procedurę powyżej, i bezpośrednio po niej /join i obsłużyć ponownie procedurę z pkt. 2.3.1. Jeśli tylko opuszczamy pokój, to obsługujemy tylko to co powyżej.

Powyższy tekst wydawać się może niejasny dla użytkowników Polchat 2.0 - tam można było przebywać tylko w jednym pokoju naraz. W Polchat 3.0 (obecny obowiązujący) można przebywać w wielu jednocześnie, więc wydanie polecenia /join nie skutkuje naszym wyjściem z dotychczasowego pokoju.



2.4 – Podtrzymanie połączenia (ping)

Pakiety ping-pong Polchatu są pakietami 'pustymi', praktycznie dosłownie. Ping-pong działa na prostej zasadzie: serwer Polchat nas pinguje, po czym my mu odpowiadamy. Struktury:

struktura Ping // tu Ryba, tu Ryba, wzywam cie Akwarium... >XD
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 0;
   16-bit ID = 1;
}

(czyli 00 00 00 0A 00 01 00 00 00 01)


struktura Pong // czyli nasza odpowiedz
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 0;
   16-bit Ilosc_Stringow = 0;
}

(czyli 00 00 00 08 00 00 00 00)

UWAGA: pakiet PONG jest, z tego co mi wiadomo, JEDYNYM pakietem który odchodzi od konwencji 'minimum 1 INT - ID pakietu'.



2.5 – Użytkownicy i stany


Czymże byłby system czatowy bez swoich użytkowników? Niestety, programy wchodzące na Polchat (w tym także, a może przede wszystkim, applet Java) nie wiedzą 'automagicznie' jacy użytkownicy przebywają w pokoju. Wygląd pakietu JOIN już opisałem, pakiet LEAVE także. Czas zakasać rękawy i zabrać się za to, czego tygryski nie trawią w większości implementacji - userlistę.

2.5.1 – Lista użytkowników

Userlista ma to do siebie, że ktoś kto nie jest obeznany ze znaczeniem kolejnych elementów potrafi tygodniami siedzieć i kombinować o co z tym pakietem chodzi. Struktura userlisty jest całkiem logiczna (co nie znaczy 'banalna'):

struktura Naglowek_Userlisty
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 5 + (2 * Ilosc_Uzytkownikow);
   16-bit Ilosc_Stringow = 1 + (2 * Ilosc_Uzytkownikow);

   16-bit ID = 619;

   16-bit NumGlobaInt = 1;
   16-bit NumLocalInt = 1;
   16-bit NumGlobalStr = 1; // zmiana w 3.0 - globalny string usera to nazwa jego klienta
   16-bit NumLocalStr = 0; // w momencie kiedy 'wywalimy z drogi' pierwsze cztery INTy, reszta = łatwizna
}

Może ja od razu wyjaśnię co i jak: userlista, w rdzeniu, zbudowana jest z dwóch rzeczy: listy nicków, oraz ich właściwości. Jak zwykle, ciągi tekstowe umieszczone są na końcu pakietu, co może przyprawiać niektórych o ból głowy... ;-)

Te cztery INTy - NumGlobalInt, NumLocalInt itd. - informują nas, ile elementów 'na łebka' będzie upakowane. W przypadku Polchatu 2.0 jest to odpowiednio 1,1,0,0 - jeden INT globalny, jeden INT lokalny, i zero stringów lokalnych/globalnych. Dla Polchatu 3.0, jest to 1,1,1,0 - jeden INT globalny, jeden INT lokalny, jeden STRING globalny i zero STRINGów lokalnych. Zawartość i znaczenie tych dwóch INTów opiszę poniżej - teraz tylko dokończę pakiet userlisty:

struktura Tresc_Userlisty
{
   16-bit GlobalIntNick1;
   16-bit LocalIntNick1;
   16-bit GlobalIntNick2;
   16-bit LocalIntNick2;
   ...
   16-bit GlobalIntNickN;
   16-bit LocalIntNickN;

   string Pokoj; // informuje nas, do którego pokoju userlistę otrzymaliśmy

   string Nick1;
   string localStrNick1;
   string Nick2;
   string localStrNick2;
   ...
   string NickN;
   string localStrNickN;

}

Dla końcowego wyjaśnienia przypominam tylko, że kiedy otrzymujemy userlistę, naszego nicka NIE MA na niej - od tego jest pakiet JOIN.

W przypadku gdy wchodzimy do pustego pokoju, możemy otrzymać 'pustą' userlistę, wygląda to tak:

struktura UserlistaPustegoPokoju // tak, to cala userlista dla pustego pokoju ;)
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 5;
   16-bit Ilosc_Stringow = 1;
   16-bit ID = 619;

   16-bit NumGlobInt = 1;
   16-bit NumLocInt = 1;
   16-bit NumGlobStr = 1;
   16-bit NumLocStr = 0;

   string Pokoj;
}

2.5.2 – Stany globalne, stany lokalne

W punkcie powyżej poruszyłem szalenie istotny temat - dwa INTy stanów. Jest on istotny dlatego, że bez pełnego zrozumienia funkcjonowania tych elementów nie jesteśmy w stanie dowiedzieć się czegokolwiek o stanie użytkowników na pokoju - bez tego jesteśmy ślepi od początku do końca.

Zajmę się najpierw stanem globalnym, gdyż jest łatwiejszy do zrozumienia - a także przez to, że stan globalny użytkownika prezentuje się dla wszystkich czatowiczów tak samo. W tym miejscu muszę, niestety, wprowadzić termin operacji bitowych: bitowe AND, oraz bitowe OR. Krótkie wyjaśnienie:

Operacje bitowe: AND

Operację bitową AND wykonuje się ZAZWYCZAJ za pomocą znaczka '&' (ampersand):

&      0    1
0      0    0
1      0    1

Przykład działania operatora AND: przyjmijmy 2 & 3:
2 = 00000010
3 = 00000011
& -----------
w = 00000010

Jak widać, za pomocą tego operatora możemy sprawdzić, czy wartość 3 'zawiera w sobie' wartość 2 - tzn. jeśli 2 & 3 = 2, to 3 zawiera 2. Ta konkluzja jest szalenie istotna przy odczytywaniu tzw. flag (którymi te INTy w rzeczywistości są). Może w takim zapisie nie jest to najczytelniejsze, ale roważny następujący przykład:

przyjmijmy, że stan 'StanOpa' jest równy 2. W takim przypadku łatwo nam to sprawdzić za pomocą
operacji:

jesli ( StanGlobalnyUzytkownika & StanOpa = StanOpa ) to wtedy
{
   uzytkownik ma opa
}
inaczej
{
   uzytkownik nie ma opa
}

Druga operacja to logiczne OR:

Operacje bitowe: OR

Operację bitową OR wykonuje się ZAZWYCZAJ za pomocą znaczka '|' (taka laska/kreska, shift+backslash):

|      0    1
0      0    1
1      1    1

Przykład działania operatora OR: przyjmijmy 2 | 3:
2 = 00000010
3 = 00000011
| -----------
w = 00000011

Co sprytniejsi zauważyli, że ta operacja jest operacją 'przeciwną' do bitowego AND w tym sensie, że o ile bitowe AND pozwala 'odczytywać' flagi, o tyle bitowe OR pozwala je TWORZYĆ. Rozważmy następujący przykład:

przyjmijmy, że stan 'StanOpa' jest równy 2. W takim przypadku łatwo nam nadać komuś opa:

StanGlobalnyUzytkownika = StanGlobalnyUzytkownika | StanOpa;
// w efekcie tej operacj 'zapalany' jest drugi najmniej znaczacy bit, oznaczajacy opa

Wszyscy, którzy zrozumieją powyższe wyjaśnienie, nie będą mieli problemów ze zrozumieniem tego co napiszę w następnych podrozdziałach.

2.5.3 – Kolory, privy

Kolory oraz privy 'nanoszone' są za pomocą bitowego OR na Stan Globalny użytkownika. Priv jest znaczony na najmniej znaczącym bicie tegoż INTa, tj.:

00000001 - otwarty priv
00000000 - zamknięty priv

(liczby binarne odczytujemy od prawej do lewej, od tzw. najmniej znaczącego bita, do najbardziej
znaczącego bita - im bardziej na lewo, tym większą wielokrotność liczby 2 symbolizuje dany bit))

Kolor już nie jest taką prostą sprawą, gdyż kolejne kolory (max. możliwych kolorów to ilość wartości zapisywalnych na najbardziej znaczących 12 bitach 16-bitowego stanu globalnego czyli 4095 kolorów + brak koloru, zakładając że nic nie skorzysta z tej przestrzeni w przyszłości) przeliczane są formułą NumerKoloru * 16. Przykład:

Przyjmijmy, że chcielibyśmy nadać komuś kolor różowy (3). Musimy wtedy wykonać następującą operację:

StanGlobalnyUzytkownika = StanGlobalnyUzytkownika | Kolor;

W naszym przypadku Kolor to 3 * 16 = 48, w zapisie binarnym: 00110000 (32 + 16).
Tę wartość nanosimy na Stan Globalny np. użytkownika bez koloru, z otwartym PRIVem: 00000001

00000001
00110000 |
--------
00110001 -- wynik: użytkownik z otwartym PRIVem, mający kolor numer 3 (różowy)


Dla programisty przykład w C++:

user_GlobalInt |= HAS_PRIV_OPEN; // HAS_PRIV_OPEN = 1
color = USER_COLOR_PINK; // USER_COLOR_PINK = 3
color <<= 4;
user_GlobalInt |= color; // wynik: 00110001

Najłatwiej odczytać kolor użytkownika przesuwając wartość o 4 bity w prawo. ;) (google bitshifting)

Zmiana stanu użytkownika (np. po operacji /guest nadającej kolor, lub /op nadającej opa) sygnalizowana jest za pomocą pakietu GlobalStatusChange:

struktura GlobalStatusChange
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 2;
   16-bit Ilosc_Stringow = 2;
   16-bit ID = 617;

   16-bit StanGlobalnyUzytkownika;
   string NickUzytkownika;

   string Pokoj; // GDZIE nastapiła zmiana?
}

Dodam jeszcze tylko, że struktura zmiany Stanu Globalnego rozsyłana jest do WSZYSTKICH obecnych w pokoju.

2.5.4 – Operatorzy

Właściwie wyjaśnienie stanu operatora znajduje się w obu punktach powyżej, ale dla porządku: stan operatora określany jest na drugim najmniej znaczącym bicie Stanu Globalnego danego użytkownka (drugi bit od prawej), więc wykonuje się operację bitowego OR z liczbą 2 aby go nadać (przykładowy op: 00000010).

S-operatorzy (operatorzy serwera/sopy) funkcjonują na takiej samej zasadzie. Nie, nie ma dla nich nic wyjątkowego w protokole. Oczywiście, s-operatorzy mają status operatora w każdym pokoju, ale w zależności od typu (sfull, shalf) nadawany jest im odpowiedni typ operatora lokalnego (full, half) po wejściu do danego pokoju — jak każdemu innemu operatorowi.

Różnorakie typy uprawnień operatora nie mają wpływu na protokół.

2.5.5 – Przyjaciele, ignorowani

Stan Globalny opisałem w paragrafach powyżej. Czas na Stan Lokalny - który także jest flagą, ale odpowiada za obecność użytkownika na liście przyjaciół, ignorowanych, lub za stan 'ja sam'. UWAGA: stan lokalny jest dla każdego odbiorcy inny! (tak jak każdy odbiorca ma swoją listę przyjaciół i ignorowanych)

Stan lokalny: można go opisać tak naprawdę na 3 bitach, nie potrzeba na 16stu ;)

w kolejności od najmniej do najbardziej znaczącego bitu, wartości kolejne to:

przyjaciel, ignor, ja sam

Zmiana stanu lokalnego sygnalizowana jest strukturą LocalStatusChange, którą przytoczyłem przy okazji opisu procedury wejścia do pokoju. Przytoczę ją jednak ponownie, dla jasności:

struktura LocalStatusChange
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 2;
   16-bit Ilosc_Stringow = 2;
   16-bit ID = 618;

   16-bit StanLokalnyUzytkownika;
   string NickUzytkownika;
}

Stan lokalny użytkownika wysyłany jest tylko do jednego odbiorcy (zasadniczo do nas), i występuje jako reakcja na polecenie (/buddy, /unbuddy, /ignore, /unignore - kiedy przybiera wartość 1, 2 lub 0) oraz w momencie wejścia do pokoju (kiedy przybiera wartość 4 i nicka [nasz nick, na którym weszliśmy do pokoju] aby zasygnalizować że nasz nick na liście - to my).

UWAGA: protokół Polchat POZWALA na taką sytuację, w której otrzymamy wartość 3 (buddy + ignor)! Jest to może i nielogiczne, ale jak najbardziej naturalne - applet Java polchatu potrafi w takiej sytuacji wyświetlić obie 'łapki' (w górę i w dół) przy nicku. Programy typu ICeQ czy NPCC 'załatwiają' sprawę za pomocą ustalenia 'ignora' (jeśli ktoś jest przyjacielem i jednocześnie ignorowany - jest ignorowany, i kropka).



2.6 – Prowadzenie rozmowy

Prowadzenie rozmowy jest kluczowym elementem systemu czatowego. Co zabawne, jest także najprostszym elementem protokołu Polchat.

2.6.1 – Rozmowy w oknie głównym (main)

Wiadomości w oknie głównym rozsyłane są do wszystkich użytkowników obecnych w danym pokoju, chyba że użytkownik zdecydował się ignorować daną osobę - wtedy pakietu z jej wypowiedzią teoretycznie otrzymać nie powinien.

Ktoś może mi się teraz zapytać - o co chodzi? Bo przecież jeden pakiet wypowiedzi już opisałem. Nie, jest pewna różnica: pakiet, który my (klient) wysyłamy do serwera posiada ID 410, podczas gdy ten sam pakiet z naszą wiadomością, wracając do nas, ma już ID równe 610. To pozwala rozróżnić pochodzenie pakietu - rozróżnienie to jest o tyle istotniejsze, że i main, i priv my 'załatwiamy' jednym pakietem, a serwer - dwoma. Ale do rzeczy: pakiet o id 410 nie różni się zasadniczo od pakietu serwerowego:

struktura Client_Chat_MSG
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 2;
   16-bit ID = 410;

   string Wiadomosc;

   string Pokoj; // na który pokój wysyłamy tekst?
}

Różnica jest w tym, że nasza wiadomość wysyłana jest na zasadzie 'Wiadomość', podczas gdy serwer odsyła to do nas spowrotem w formie '[nick]: Wiadomość', odpowiednio nakładając też kolor nicka, w/g jego flagi globalnej (patrz dział 2.5).

2.6.2 – Rozmowy prywatne (priv)

Prowadzenie rozmowy prywatnej jest jakby 'przejściem' pomiędzy rozmową publiczną a poleceniami - gdyż samo w sobie jest poleceniem. Aby rozmawiać z kimś prywatnie, każdą wypowiedź prywatną musimy ująć tak:

struktura Client_Priv_MSG
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 2;
   16-bit ID = 410;

   string Wiadomosc = "/msg [do kogo piszemy] [nasza wiadomosc]";
   string Pokoj; // tu może być pusto, albo nazwa jakiegoś pokoju w którym jesteśmy
}

Należy pamiętać aby KAŻDĄ wiadomość prywatną tak zapisywać, nie tylko pierwszą!

Wspaniale. O ile wysyłanie wiadomości prywatnej jest proste, o tyle odbieranie jej jest już nieco bardziej skomplikowane. Wiadomości prywatne przychodzą do nas pakietem typu 611 (pisałem o tym rozróżnieniu wcześniej), i ich budowa żalezy od tego czy my wysyłamy, czy odbieramy wiadomość prywatną:

struktura Przychodzaca_Wiadomosc_Prywatna
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 2;
   16-bit ID = 611;

   string Wiadomosc = "*[nick wysylajacego]* [Wiadomosc]";
   string Nick_Wysylajacy; // czyli od kogo
}

struktura Wychodzaca_Wiadomosc_Prywatna
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 3;
   16-bit ID = 611;

   string Wiadomosc = "*[nick wysylajacego]* [Wiadomosc]";
   string Nick_Wysylajacy; // czyli my
   string Nick_Docelowy; // czyli do kogo
}

Jeśli chwilę nad tym pomyślisz - zrozumiesz ideę takiego rozwiązania. Każdy z programów (u jednego i drugiego czatowicza) musi przecież wiedzieć, w którym okienku umieścić daną wypowiedź. ;-)

Należałoby także wspomnieć, iż jeśli nasz klient pracuje w trybie standardowym (ICeQ pozwala zmienić ten tryb), w momencie wysłania wiadomości prywatnej powinniśmy także zmienić nasz stan globalny - konkretniej flagę PRIV - na 1. Po zamknięciu priva przywracamy jej stan 0 (priv zamknięty).

2.6.2 – Polecenia

Polecenia wysyła się tak samo jak privy - za pomocą pakietu 410 o treści '/polecenie [parametry]'. Nie będę wchodził tutaj w szczegóły takiego pakietu, gdyż dostatecznie wiele razy powtarzałem jego strukturę powyżej. Chciałbym tylko wprowadzić pewne logiczne rozróżnienie ze względu na efekt polecenia: polecenia zwracające tekst (przychodzący pakietem 610), i polecenia wywołujące akcję sygnalizowaną innym typem pakietu. Do tych pierwszych zaliczyć należy:

Polecenia zwracające tekst:

- /info [nick] (informacje o nicku: jak długo siedzi, gdzie etc.)
- /buddies (lista przyjaciół)
- /ignored (lista ignorowanych)
- /bans (lista banów - wymagane uprawnienia operatora)
- /sbans (lista s-banów - wymagane uprawnienia s-operatora, hehe ;P)
- /bantime [opcjonalny czas] (wyświetlenie / zmiana domyślnego czasu bana, który 'dasz')
- /ops (lista operatorów - wymagane uprawnienia operatora)
- /sops (lista s-operatorów - chyba wiadomo ;P)
- /me
- /broadcast (wymagane pełne s-uprawnienia do tego polecenia, ale też zwraca tekst)

Do tych drugich nastomiast:

Polecenia wywołujące reakcję inną niż tekst:

[polecenia wywołujące zmianę flagi globalnej - OP]
- /op
- /halfop
- /tmphalfop
- /unop

  oraz s-operatorskie

- /sop
- /shalfop
- /stmphalfop
- /sunop


[polecenia wywołujące zmianę flagi globalnej - KOLOR]
- /guest
- /unguest

[polecenia wywołujące operację na czatowiczu - usunięcie, banowanie, zdjęcie bana]
- /kick
- /kickip
- /kickipof
- /ban
- /banip
- /banipof
- /unban
- /bantime

   oraz s-operatorskie odpowiedniki (poza /sbantime, która nie istnieje)

- /skick
- /skickip
- /skickipof
- /sban
- /sbanip
- /sbanipof
- /sunban


[polecenia wywołujące czynności wejścia/wyjścia pokoju]
- /join
- /part
- /quit

[polecenia wywołujące zmianę flagi lokalnej czatowicza - BUDDY]
- /buddy
- /unbuddy
- /ignore
- /unignore

[specjalne polecenia operatorów systemu]
- /push
- /loginop (wywołuje zmianę flagi globalnej, ale należy w sumie tutaj)



2.7 – Rozmowa moderowana


Z racji tego, iż moderkę wyłączono w ogólnym czacie, pominę tutaj jej opis. Nie mam po prostu jak sprawdzić, jak działa. Zrobię, jeśli będzie nacisk ze strony użytkowników tego opisu... ;) Zasadniczo sprowadzała się do tego co powyżej, z tą różnicą, że wiadomości otrzymywał moderator a nie wszyscy - i dopiero po zatwierdzeniu tej wiadomości (nieznanym pakietem) docierała ona do wszystkich uczestników. Podejrzewam, że istniały także specjalne stany globalne (moderator, +głos etc.) dla osób moderujących oraz np. gości, którzy mogli wypowiadać się bez ograniczeń.

Występowały oczywiście wiadomości typu "** Rozpoczyna się rozmowa moderowana" etc., ale te wysyłane były trywialnym 610.



2.8 – Kategorie


Listę kategorii otrzymujemy tuż po połączeniu się z serwerem (drugi string w pakiecie 626). Aby uzyskać listę pokoi w danej kategorii, wysyłamy pakiet o ID 411 - Category_Request:

struktura Zadanie_Listy_Pokoi_Kategorii
{
   32-bit dlugosc;
   16-bit Ilosc_intow = 2;
   16-bit Ilosc_stringow = 1;
   16-bit ID = 411;

   16-bit int1 = 7; // nie, nie wiem do czego to sluzy. Ktos wie? ;)

   string NazwaKategorii = "/zadana kategoria/"; // NA PRZYKLAD '/Erotyczne/'
}

Warto zauważyć, że nazwę kategorii odbieramy w postaci /Nazwa Kategorii/, i w taki sam sposób należy ją wysłać (tj. pomiędzy znakami /).

Jeżeli podaliśmy poprawną nazwę kategorii, powinniśmy otrzymać spowrotem pakiet o ID 613, czyli Category_Reply:

struktura Lista_Pokoi_Kategorii
{
   32-bit dlugosc;
   16-bit Ilosc_intow = 1;
   16-bit Ilosc_stringow = 1;
   16-bit ID = 613;

   string ListaPokoi;
}

Lista pokoi będzie w dość zagmatwanej postaci... na przykład:

- <b><a href="/join POLCHAT">POLCHAT</a></b> (15) &nbsp; [http://www.polchat.pl])<br>- <a href="
/join gotyx2">gotyx2</a> (2) &nbsp; [dla wszystkich ktorzy lubia inteligentna rozrywke]<br>- <b><a
href="/join sport">sport</a></b> (2)

O czym należy obowiązkowo wiedzieć w stringu powyżej (celowo pofragmentowałem jak widać, bo to jest faktycznie jeden ciąg):

Uwaga: ze względu na to, że pakiet informujący o liście pokoi zwyczajowo przekroczy dopuszczalne MTU w sieci (średnie MTU to 1450 bajtów), należy ekstra uważać na fragmentację pakietów. Ale komu ja to mówię? :)



2.9 – Zmiana hasła (lub rejestracja nicka)


Ze względu na to, że rejestracja identyfikatora/zmiana hasła w portalu Polchat.pl jest operacją obejmującą więcej niż wpis w bazie czatu, sekcja niniejsza nie powstanie. Z tego samego względu Ikari usunął opcję 'Zmień hasło' z najnowszych wersji programu ICeQ. Aby zmienić hasło lub zarejestrować identyfikator, należy udać się na stronę www.polchat.pl.



2.10 – Zakończenie sesji, wylogowanie się

Zakończenie naszej sesji z serwerem Polchat wywołujemy za pomocą wysłania polecenia '/quit [opcjonalna wiadomość pożegnalna]' (wiadomość nie wyświetli się, jeśli nie jesteśmy w żadnym pokoju, oczywiście). Serwer odpowie nam na nią pakietem 65535 (FF FF):

struktura GoodBye
{
   32-bit dlugosc;
   16-bit Ilosc_Intow = 1;
   16-bit Ilosc_Stringow = 1;
   16-bit ID = 65535;

   string Pozegnanie = "-"; // tak, znaczek 'minus'
}

Po otrzymaniu tej wiadomości powinniśmy zamknąć nasze połączenie z serwerem (serwer zapewne już to zrobił). I to wszystko, wylogowaliśmy się z sesji Polchatu.



3 – Indeks pakietów


[ w produkcji ]



4 – Pytania, korekty


Cóż... jestem tylko człowiekiem, a podobno mylić się jest rzeczą ludzką. Jak parokrotnie wspomniałem w tekście wyżej - jeśli doszukasz się błędów lub braków, bardzo gorąco proszę o kontakt mailowy (adres e-mail na górze strony) z informacją o tym, abym jak najszybciej mógł poprawić/dopisać rzeczony fragment.

Jeśli macie jakiekolwiek pytania, także dotyczące konkretnych rozwiązań które zastosowałem w swoich programach, możecie śmiało pytać, pisząc maila na adres: egonfreeman@gmail.com. W tytule wiadomości proszę na początku wpisać [Protokół Polchat].



5 – Changelog - lista zmian




6 – Podziękowania


Kłamałbym w żywe oczy gdybym stwierdził, że doszedłem do wszystkiego sam. Różne osoby pomogły mi w różny sposób po drodze, czy to wspomagając wysiłki, czy też wykonując dla mnie różne dziwne zadania (np. testowanie, sniffowanie), czy po prostu będąc przy mnie w chwilach załamania stresowego... Dlatego chciałbym skorzystać z jakże prestiżowej OSTATNIEJ (;P) części tego dokumentu, aby podziękować niektórym oficjalnie:


Wszystkim wam i tym, których pominąłem - serdecznie Dziękuję! ^__^

Chciałbym też gorąco podziękować Marcinowi 'MalCom' Malichowi za prześliczny opis protokołu Tlen.pl, oraz ludziom z zespołu ekg.chmurka.net (ekg-devel) za trochę trudniejszy ale mimo to wyśmienity opis protokołu Gadu-Gadu - to dzięki ich pracy w ogóle zainteresowałem się dokumentacją protokołów komunikacyjnych.