[PL] SMS PDU - Format wiadomości w praktyce
Popularne SMSy przesyłane mogą być w dwóch formatach: tekstowym lub PDU (ang. Protocol Data Unit). Z oczywistych przyczyn pierwszy wariant jest używany rzadziej. Możliwości, jakie mamy zaś w PDU są następujące.
- 7 bity na znak - rozwiązanie pozwalające na uzyskanie max. 160 znaków na SMS,
- 8 bity na znak - opcja pośrednia, 140 znaków,
- 16 bitów na znak - czyli dla bogatych (70 znaków/SMS).
Powyższe skomplikowane obliczenia uwzględniają nagłówek PDU, więc proszę mnie nie posądzać o nieumiejętność dzielenia :-). Format wspomnianego nagłowka jest całkiem nieźle opisany w tym miejscu.
Koniec części teoretycznej. Odpalamy swój najlepszy edytor tekstowy (vim), zaprzągamy ulubiony język (python), i ... dewelopujemy na wesoło.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 def int2hex (n):
5 s = '%X'%n
6 if int(len(s))%2 == 1:
7 s = '0'+s
8 return s
9
10 def encode_message (message):
11 pdu = ""
12 l = len (message)
13 message += '\0'
14
15 # Wyznaczamy rozmiar tekstu po upakowaniu.
16 msglen = int (len(message)*7/8)
17
18 # Inicjujemy bufor i zmienne.
19 buf = [0] * msglen
20 c = shift = 0
21
22 # Dla każdego septetu...
23 for n in range(msglen):
24 # Przeskok po każdej paczce 7 bajtów.
25 if shift == 6:
26 c += 1
27
28 # Wyznaczamy oktet z dwóch septetów.
29 shift = n % 7
30 lb = ord(message[c]) >> shift
31 hb = (ord(message[c+1]) << (7-shift) & 255)
32 buf[n] = lb+hb
33
34 c += 1
35
36 # Łączymy wiadomość z jej rozmiarem w postać heksadecymalną.
37 pdu = chr(l) + ''.join(map(chr, buf))
38 return ''.join(["%02x" % ord(n) for n in pdu])
39
40 def encode_SMS (dest_number, text):
41 # Zaczynamy od szablonu SMSa.
42 smspdu = "001100"
43
44 # Dodajemy długość numeru i jego format.
45 smspdu += int2hex(len(dest_number)) + '81'
46
47 # Dodajemy numer nadawcy.
48 tmp_number = dest_number
49 if len(tmp_number) % 2 == 1:
50 tmp_number += 'F'
51 i = 0
52 while i < len(tmp_number):
53 smspdu = smspdu + tmp_number[i+1] + tmp_number[i]
54 i += 2
55
56 # Dodajemy id protokołu, pole DCS i okres ważności.
57 smspdu += '0000FF'
58
59 # Dodajemy dł. wiadomości no i samą wiadomość :-).
60 smspdu += int2hex(len(text))
61 smspdu += encode_message(text)
62
63 return smspdu
Zaletą pythona jest to, że kod w nim napisany jest prawie samo-dokumentujący się. Jeżeli deklarujesz się jako profesjonalny programista, ale jednak nie rozumiesz co się dzieje powyżej, to (nie chce mi się wymyślać żadnych epitetów, po prostu zacytuję pewnego pana):
to prawdopodobnie masz jeszcze szanse na jakąś karierę, nie wiem, w polityce chyba wiele nie trzeba, musisz tylko dobrze wyglądać w krawacie
Powyższy kod całkiem dobrze się sprawuje pod warunkiem, że wszystko uda się upakować w jednej wiadomości. W innych wypadkach mogą dziać się różne rzeczy, od bełkotu wyświetlającego się na twojej komórce do niczego - SMS nie dochodzi w ogóle i nikt nie wie dlaczego (sytuacje rodem z politechniki), bramka twierdzi, że wiadomość wysłała, a komórka milczy. Wracając jeszcze raz do dokumentacji szybko znajdujemy informacje jako by przy długich wiadomościach istotną rolę odgrywał tzw. nagłówekUDH. Dodajmy więc małą modyfikację:
1 message = '\x00\x00\x00\x00\x00\x00' + message
...
? for i, ch in enumerate (udh):
? buf[i] = ord(ch)
Pierwszą linię dodajemy dokładnie na początku funkcji encode_SMS, a pętlę zaraz po upakowaniu wiadomości (czyli zaraz za pierwszą pętlą we wcześniej opisanej funkcji encode_SMS.
Na zakończenie powiem coś o sprzęcie. Można zakupić takie urządzenie które nazywa się chyba modemem GPRS. To taki mały pierdolnik podpinany do komputera przez USB i udostępniający mu internet. Nie jestem pewien na 100%, lecz bardzo prawdopodobne, że do tego modemu można podpiąć się za pomocą np. telnetu i wysyłać komendyHayes'a. Ja, szczęśliwie, miałem możliwość obcowania z pełnoprawną bramką GSM, która mieściła w sobie do 30 kart SIM pozwalając nieźle naspamować ]:->. Dziękuję za dotrwanie do końca.