0
# Low-Level Protocol Implementation
1
2
The `gntp.core` module provides direct access to GNTP (Growl Notification Transport Protocol) message construction, parsing, and validation. This low-level API is useful for applications requiring fine-grained control over protocol details, custom message handling, or integration with other notification systems.
3
4
## Capabilities
5
6
### GNTP Message Classes
7
8
The core module provides classes for each GNTP message type, enabling direct protocol message construction and parsing.
9
10
```python { .api }
11
class GNTPRegister:
12
"""
13
Represents a GNTP Registration Command.
14
15
Required headers: Application-Name, Notifications-Count
16
"""
17
18
def __init__(self, data=None, password=None):
19
"""
20
Create GNTP Registration message.
21
22
Parameters:
23
- data (str): Optional raw GNTP data to decode
24
- password (str): Optional password for decoding
25
"""
26
27
def add_notification(self, name, enabled=True):
28
"""
29
Add notification type to registration.
30
31
Parameters:
32
- name (str): Notification type name
33
- enabled (bool): Enable by default in Growl
34
"""
35
36
def validate(self):
37
"""
38
Validate required headers and notification headers.
39
40
Raises:
41
ParseError: When required headers are missing
42
"""
43
44
def decode(self, data, password):
45
"""
46
Decode existing GNTP Registration message.
47
48
Parameters:
49
- data (str): Raw GNTP message data
50
- password (str): Password for authentication
51
"""
52
53
def encode(self):
54
"""
55
Encode to GNTP Registration message.
56
57
Returns:
58
bytes: Complete GNTP registration message
59
"""
60
61
class GNTPNotice:
62
"""
63
Represents a GNTP Notification Command.
64
65
Required headers: Application-Name, Notification-Name, Notification-Title
66
"""
67
68
def __init__(self, data=None, app=None, name=None, title=None, password=None):
69
"""
70
Create GNTP Notification message.
71
72
Parameters:
73
- data (str): Optional raw GNTP data to decode
74
- app (str): Application name
75
- name (str): Notification type name
76
- title (str): Notification title
77
- password (str): Optional password
78
"""
79
80
def validate(self):
81
"""
82
Validate required headers.
83
84
Raises:
85
ParseError: When required headers are missing
86
"""
87
88
def decode(self, data, password):
89
"""
90
Decode existing GNTP Notification message.
91
92
Parameters:
93
- data (str): Raw GNTP message data
94
- password (str): Password for authentication
95
"""
96
97
def encode(self):
98
"""
99
Encode to GNTP Notification message.
100
101
Returns:
102
bytes: Complete GNTP notification message
103
"""
104
105
class GNTPSubscribe:
106
"""
107
Represents a GNTP Subscribe Command.
108
109
Required headers: Subscriber-ID, Subscriber-Name
110
"""
111
112
def __init__(self, data=None, password=None):
113
"""
114
Create GNTP Subscribe message.
115
116
Parameters:
117
- data (str): Optional raw GNTP data to decode
118
- password (str): Optional password
119
"""
120
121
def validate(self):
122
"""
123
Validate required headers.
124
125
Raises:
126
ParseError: When required headers are missing
127
"""
128
129
def decode(self, data, password):
130
"""
131
Decode existing GNTP Subscribe message.
132
133
Parameters:
134
- data (str): Raw GNTP message data
135
- password (str): Password for authentication
136
"""
137
138
def encode(self):
139
"""
140
Encode to GNTP Subscribe message.
141
142
Returns:
143
bytes: Complete GNTP subscribe message
144
"""
145
146
class GNTPOK:
147
"""
148
Represents a GNTP OK Response.
149
150
Required headers: Response-Action
151
"""
152
153
def __init__(self, data=None, action=None):
154
"""
155
Create GNTP OK response.
156
157
Parameters:
158
- data (str): Optional raw GNTP data to decode
159
- action (str): Response action type
160
"""
161
162
def decode(self, data):
163
"""
164
Decode GNTP OK response.
165
166
Parameters:
167
- data (str): Raw GNTP response data
168
"""
169
170
def encode(self):
171
"""
172
Encode to GNTP OK response.
173
174
Returns:
175
bytes: Complete GNTP OK response
176
"""
177
178
class GNTPError:
179
"""
180
Represents a GNTP Error response.
181
182
Required headers: Error-Code, Error-Description
183
"""
184
185
def __init__(self, data=None, errorcode=None, errordesc=None):
186
"""
187
Create GNTP Error response.
188
189
Parameters:
190
- data (str): Optional raw GNTP data to decode
191
- errorcode (int): Error code
192
- errordesc (str): Error description
193
"""
194
195
def error(self):
196
"""
197
Get error information.
198
199
Returns:
200
tuple: (error_code, error_description)
201
"""
202
203
def decode(self, data):
204
"""
205
Decode GNTP Error response.
206
207
Parameters:
208
- data (str): Raw GNTP response data
209
"""
210
211
def encode(self):
212
"""
213
Encode to GNTP Error response.
214
215
Returns:
216
bytes: Complete GNTP error response
217
"""
218
```
219
220
### Base Message Functionality
221
222
All GNTP message classes inherit common functionality from the base class:
223
224
```python { .api }
225
# Base methods available on all GNTP message classes:
226
227
def set_password(self, password, encryptAlgo='MD5'):
228
"""
229
Set password for GNTP message authentication.
230
231
Parameters:
232
- password (str): Password string (None to clear)
233
- encryptAlgo (str): Hash algorithm ('MD5', 'SHA1', 'SHA256', 'SHA512')
234
235
Raises:
236
UnsupportedError: When hash algorithm is not supported
237
"""
238
239
def add_header(self, key, value):
240
"""
241
Add header to GNTP message.
242
243
Parameters:
244
- key (str): Header name
245
- value (str): Header value
246
"""
247
248
def add_resource(self, data):
249
"""
250
Add binary resource to GNTP message.
251
252
Parameters:
253
- data (bytes): Binary resource data
254
255
Returns:
256
str: Resource URL for referencing in headers
257
"""
258
259
def validate(self):
260
"""
261
Verify required headers are present.
262
263
Raises:
264
ParseError: When required headers are missing
265
"""
266
267
def encode(self):
268
"""
269
Encode message to GNTP protocol format.
270
271
Returns:
272
bytes: Complete GNTP message ready for transmission
273
"""
274
```
275
276
### Message Parsing
277
278
```python { .api }
279
def parse_gntp(data, password=None):
280
"""
281
Parse raw GNTP message data and return appropriate message object.
282
283
Parameters:
284
- data (str or bytes): Raw GNTP message data
285
- password (str): Optional password for authentication
286
287
Returns:
288
GNTPRegister | GNTPNotice | GNTPSubscribe | GNTPOK | GNTPError:
289
Parsed message object of appropriate type
290
291
Raises:
292
ParseError: When message format is invalid or parsing fails
293
"""
294
```
295
296
## Usage Examples
297
298
### Creating and Sending Registration Messages
299
300
```python
301
import gntp.core
302
import socket
303
304
# Create registration message
305
register = gntp.core.GNTPRegister()
306
register.add_header('Application-Name', 'My Custom App')
307
register.add_header('Application-Icon', 'http://example.com/icon.png')
308
309
# Add notification types
310
register.add_notification('Alert', enabled=True)
311
register.add_notification('Info', enabled=False)
312
313
# Set password if needed
314
register.set_password('mypassword', 'SHA256')
315
316
# Validate before sending
317
register.validate()
318
319
# Encode to protocol format
320
data = register.encode()
321
322
# Send over socket
323
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
324
sock.connect(('localhost', 23053))
325
sock.send(data)
326
response = sock.recv(1024)
327
sock.close()
328
329
# Parse response
330
response_obj = gntp.core.parse_gntp(response)
331
if isinstance(response_obj, gntp.core.GNTPOK):
332
print("Registration successful")
333
else:
334
print(f"Registration failed: {response_obj.error()}")
335
```
336
337
### Creating Notification Messages
338
339
```python
340
import gntp.core
341
342
# Create notification
343
notice = gntp.core.GNTPNotice()
344
notice.add_header('Application-Name', 'My Custom App')
345
notice.add_header('Notification-Name', 'Alert')
346
notice.add_header('Notification-Title', 'Important Message')
347
notice.add_header('Notification-Text', 'This is the notification body')
348
notice.add_header('Notification-Priority', '1')
349
notice.add_header('Notification-Sticky', 'True')
350
351
# Add binary icon resource
352
with open('icon.png', 'rb') as f:
353
icon_data = f.read()
354
resource_url = notice.add_resource(icon_data)
355
notice.add_header('Notification-Icon', resource_url)
356
357
# Set password and encode
358
notice.set_password('mypassword')
359
data = notice.encode()
360
```
361
362
### Parsing Incoming Messages
363
364
```python
365
import gntp.core
366
import gntp.errors
367
368
try:
369
# Parse raw GNTP data
370
message = gntp.core.parse_gntp(raw_data, password='mypassword')
371
372
if isinstance(message, gntp.core.GNTPRegister):
373
print(f"Registration from: {message.headers['Application-Name']}")
374
print(f"Notifications: {len(message.notifications)}")
375
376
elif isinstance(message, gntp.core.GNTPNotice):
377
print(f"Notification: {message.headers['Notification-Title']}")
378
print(f"From: {message.headers['Application-Name']}")
379
380
elif isinstance(message, gntp.core.GNTPError):
381
code, desc = message.error()
382
print(f"Error {code}: {desc}")
383
384
except gntp.errors.ParseError as e:
385
print(f"Invalid GNTP message: {e}")
386
except gntp.errors.AuthError as e:
387
print(f"Authentication failed: {e}")
388
```
389
390
### Custom Message Construction
391
392
```python
393
import gntp.core
394
395
# Create subscription message
396
subscribe = gntp.core.GNTPSubscribe()
397
subscribe.add_header('Subscriber-ID', 'client-123')
398
subscribe.add_header('Subscriber-Name', 'My Client')
399
subscribe.add_header('Subscriber-Port', '23054')
400
subscribe.set_password('subscription-password')
401
402
# Create custom OK response
403
ok_response = gntp.core.GNTPOK()
404
ok_response.add_header('Response-Action', 'REGISTER')
405
ok_response.add_header('Custom-Header', 'Custom-Value')
406
407
# Create custom error response
408
error_response = gntp.core.GNTPError()
409
error_response.add_header('Error-Code', '500')
410
error_response.add_header('Error-Description', 'Internal Server Error')
411
error_response.add_header('Server-Name', 'My GNTP Server')
412
```
413
414
### Working with Binary Resources
415
416
```python
417
import gntp.core
418
import hashlib
419
420
# Create notification with multiple resources
421
notice = gntp.core.GNTPNotice(
422
app='Media Player',
423
name='Now Playing',
424
title='Song Changed'
425
)
426
427
# Add album art
428
with open('album_art.jpg', 'rb') as f:
429
album_art = f.read()
430
art_url = notice.add_resource(album_art)
431
notice.add_header('Notification-Icon', art_url)
432
433
# Add audio preview (if supported by client)
434
with open('preview.mp3', 'rb') as f:
435
audio_data = f.read()
436
audio_url = notice.add_resource(audio_data)
437
notice.add_header('X-Audio-Preview', audio_url)
438
439
# Resources are automatically managed and included in encoded message
440
encoded = notice.encode()
441
```
442
443
## Protocol Details
444
445
### Message Format
446
447
All GNTP messages follow this structure:
448
449
```
450
GNTP/1.0 MESSAGETYPE ENCRYPTION [KEYHASHALGO:KEYHASH.SALT]
451
Header-Name: Header Value
452
Another-Header: Another Value
453
454
[Optional resource data]
455
```
456
457
### Supported Hash Algorithms
458
459
- `MD5`: MD5 hash (default, legacy)
460
- `SHA1`: SHA-1 hash
461
- `SHA256`: SHA-256 hash (recommended)
462
- `SHA512`: SHA-512 hash (most secure)
463
464
### Resource Handling
465
466
Binary resources are automatically:
467
- Hashed with MD5 to generate unique identifiers
468
- Included in the message payload
469
- Referenced via `x-growl-resource://` URLs in headers
470
471
### Error Codes
472
473
Common GNTP error codes:
474
- `400`: Authentication failed
475
- `500`: Internal server error, parsing error, unsupported operation
476
- Other codes as defined by GNTP specification