6ee0c4984a40799ccf9833e19ebe794d36ea930a
[spider.git] / perl / DXProtout.pm
1 #!/usr/bin/perl
2 #
3 # This module impliments the outgoing PCxx generation routines
4 #
5 # These are all the namespace of DXProt and are separated for "clarity"
6 #
7 # Copyright (c) 1998 Dirk Koopman G1TLH
8 #
9 #
10 #
11
12 package DXProt;
13
14 @ISA = qw(DXChannel);
15
16 use DXUtil;
17 use DXM;
18 use DXDebug;
19
20 use strict;
21
22 use vars qw($sentencelth $pc19_version $pc9x_nodupe_first_slot);
23
24 $sentencelth = 180;
25 $pc9x_nodupe_first_slot = 1;
26
27 #
28 # All the PCxx generation routines
29 #
30
31 # create a talk string ($from, $to, $via, $text)
32 sub pc10
33 {
34         my ($from, $to, $via, $text, $origin) = @_;
35         my ($user1, $user2);
36         if ($via && $via ne $to && $via ne '*') {
37                 $user1 = $via;
38                 $user2 = $to;
39         } else {
40                 $user2 = ' ';
41                 $user1 = $to;
42         }
43         $origin ||= $main::mycall;
44         $text = unpad($text);
45         $text = ' ' unless $text && length $text > 0;
46         $text =~ s/\^/%5E/g;
47         return "PC10^$from^$user1^$text^*^$user2^$origin^~";
48 }
49
50 # create a dx message (call, freq, dxcall, text)
51 sub pc11
52 {
53         my ($mycall, $freq, $dxcall, $text) = @_;
54         my $hops = get_hops(11);
55         my $t = time;
56         $text = ' ' if !$text;
57         $text =~ s/\^/%5E/g;
58         return sprintf "PC11^%.1f^$dxcall^%s^%s^$text^$mycall^$main::mycall^$hops^~", $freq, cldate($t), ztime($t);
59 }
60
61 # create an announce message
62 sub pc12
63 {
64         my ($call, $text, $tonode, $sysop, $wx, $origin) = @_;
65         my $hops = get_hops(12);
66         $text ||= ' ';
67         $text =~ s/\^/%5E/g;
68         $tonode ||= '*';
69         $sysop ||= ' ';
70         $wx ||= '0';
71         $origin ||= $main::mycall;
72         return "PC12^$call^$tonode^$text^$sysop^$origin^$wx^$hops^~";
73 }
74
75 #
76 # add one or more users (I am expecting references that have 'call',
77 # 'conf' & 'here' method)
78 #
79 # this will create a list of PC16 with up pc16_max_users in each
80 # called $self->pc16(..)
81 #
82 sub pc16
83 {
84         my $node = shift;
85         my $ncall = $node->call;
86         my @out;
87
88         my $s = "";
89         for (@_) {
90                 next unless $_;
91                 my $ref = $_;
92                 my $str = sprintf "^%s %s %d", $ref->call, $ref->conf ? '*' : '-', $ref->here;
93                 if (length($s) + length($str) > $sentencelth) {
94                         push @out, "PC16^$ncall" . $s . sprintf "^%s^", get_hops(16);
95                         $s = "";
96                 }
97                 $s .= $str;
98         }
99         push @out, "PC16^$ncall" . $s . sprintf "^%s^", get_hops(16);
100         return @out;
101 }
102
103 # remove a local user
104 sub pc17
105 {
106         my @out;
107         while (@_) {
108                 my $node = shift;
109                 my $ref = shift;
110                 my $hops = get_hops(17);
111                 my $ncall = $node->call;
112                 my $ucall = $ref->call;
113                 push @out, "PC17^$ucall^$ncall^$hops^";
114         }
115         return @out;
116 }
117
118 # Request init string
119 sub pc18
120 {
121         my $flags = shift;
122         return "PC18^DXSpider Version: $main::version Build: $main::subversion.$main::build$flags^$DXProt::myprot_version^";
123 }
124
125 #
126 # add one or more nodes
127 #
128 sub pc19
129 {
130         my @out;
131         my @in;
132
133         my $s = "";
134         for (@_) {
135                 next unless $_;
136                 my $ref = $_;
137                 my $call = $ref->call;
138                 my $here = $ref->here;
139                 my $conf = $ref->conf;
140                 my $version = $ref->version;
141                 $version = $pc19_version unless $version =~ /^\d\d\d\d$/;
142
143                 my $str = "^$here^$call^$conf^$version";
144                 if (length($s) + length($str) > $sentencelth) {
145                         push @out, "PC19" . $s . sprintf "^%s^", get_hops(19);
146                         $s = "";
147                 }
148                 $s .= $str;
149         }
150         push @out, "PC19" . $s . sprintf "^%s^", get_hops(19);
151         return @out;
152 }
153
154 # end of Rinit phase
155 sub pc20
156 {
157         return 'PC20^';
158 }
159
160 # delete a node
161 sub pc21
162 {
163         my @out;
164         while (@_) {
165                 my $node = shift;
166                 my $hops = get_hops(21);
167                 my $call = $node->call;
168                 push @out, "PC21^$call^Gone^$hops^";
169         }
170         return @out;
171 }
172
173 # end of init phase
174 sub pc22
175 {
176         return 'PC22^';
177 }
178
179 # here status
180 sub pc24
181 {
182         my $self = shift;
183         my $call = $self->call;
184         my $flag = $self->here ? '1' : '0';
185         my $hops = shift || get_hops(24);
186
187         return "PC24^$call^$flag^$hops^";
188 }
189
190
191 # create a merged dx message (freq, dxcall, t, text, spotter, orig-node)
192 sub pc26
193 {
194         my ($freq, $dxcall, $t, $text, $spotter, $orignode) = @_;
195         $text = ' ' unless $text;
196         $orignode = $main::mycall unless $orignode;
197         return sprintf "PC26^%.1f^$dxcall^%s^%s^$text^$spotter^$orignode^ ^~", $freq, cldate($t), ztime($t);
198 }
199
200 # create a merged WWV spot (logger, t, sfi, a, k, forecast, orig-node)
201 sub pc27
202 {
203         my ($logger, $t, $sfi, $a, $k, $forecast, $orignode) = @_;
204         return sprintf "PC27^%s^%-2.2s^$sfi^$a^$k^$forecast^$logger^$orignode^ ^~", cldate($t), ztime($t);
205 }
206
207 # message start (fromnode, tonode, to, from, t, private, subject, origin)
208 sub pc28
209 {
210         my ($tonode, $fromnode, $to, $from, $t, $private, $subject, $origin, $rr) = @_;
211         my $date = cldate($t);
212         my $time = ztime($t);
213         $private = $private ? '1' : '0';
214         $rr = $rr ? '1' : '0';
215         $subject ||= ' ';
216         return "PC28^$tonode^$fromnode^$to^$from^$date^$time^$private^$subject^ ^5^$rr^ ^$origin^~";
217 }
218
219 # message text (from and to node same way round as pc29)
220 sub pc29
221 {
222         my ($fromnode, $tonode, $stream, $text) = @_;
223         $text = ' ' unless defined $text && length $text > 0;
224         $text =~ s/\^/%5E/og;                   # remove ^
225         return "PC29^$fromnode^$tonode^$stream^$text^~";
226 }
227
228 # subject acknowledge (will have to and from node reversed to pc28)
229 sub pc30
230 {
231         my ($fromnode, $tonode, $stream) = @_;
232         return "PC30^$fromnode^$tonode^$stream^";
233 }
234
235 # acknowledge this tranche of lines (to and from nodes reversed to pc29 and pc28
236 sub pc31
237 {
238         my ($fromnode, $tonode, $stream) = @_;
239         return "PC31^$fromnode^$tonode^$stream^";
240 }
241
242 #  end of message from the sending end (pc28 node order)
243 sub pc32
244 {
245         my ($fromnode, $tonode, $stream) = @_;
246         return "PC32^$fromnode^$tonode^$stream^";
247 }
248
249 # acknowledge end of message from receiving end (opposite pc28 node order)
250 sub pc33
251 {
252         my ($fromnode, $tonode, $stream) = @_;
253         return "PC33^$fromnode^$tonode^$stream^";
254 }
255
256 # remote cmd send
257 sub pc34
258 {
259         my($fromnode, $tonode, $msg) = @_;
260         return "PC34^$tonode^$fromnode^$msg^~";
261 }
262
263 # remote cmd reply
264 sub pc35
265 {
266         my($fromnode, $tonode, $msg) = @_;
267         return "PC35^$tonode^$fromnode^$msg^~";
268 }
269
270 # send all the DX clusters I reckon are connected
271 sub pc38
272 {
273         return join '^', "PC38", map {$_->call} Route::Node::get_all();
274 }
275
276 # tell the local node to discconnect
277 sub pc39
278 {
279         my ($call, $reason) = @_;
280         my $hops = get_hops(39);
281         $reason = "Gone." if !$reason;
282         return "PC39^$call^$reason^$hops^";
283 }
284
285 # cue up bulletin or file for transfer
286 sub pc40
287 {
288         my ($to, $from, $fn, $bull) = @_;
289         $bull = $bull ? '1' : '0';
290         return "PC40^$to^$from^$fn^$bull^5^";
291 }
292
293 # user info
294 sub pc41
295 {
296         my $call = shift;
297         $call = shift if ref $call;
298
299         my $sort = shift || '0';
300         my $info = shift || ' ';
301         my $hops = shift || get_hops(41);
302         return "PC41^$call^$sort^$info^$hops^~";
303 }
304
305 # abort message
306 sub pc42
307 {
308         my ($fromnode, $tonode, $stream) = @_;
309         return "PC42^$fromnode^$tonode^$stream^";
310 }
311
312 # remote db request
313 sub pc44
314 {
315         my ($fromnode, $tonode, $stream, $db, $req, $call) = @_;
316         $db = uc $db;
317         return "PC44^$tonode^$fromnode^$stream^$db^$req^$call^";
318 }
319
320 # remote db data
321 sub pc45
322 {
323         my ($fromnode, $tonode, $stream, $data) = @_;
324         return "PC45^$tonode^$fromnode^$stream^$data^";
325 }
326
327 # remote db data complete
328 sub pc46
329 {
330         my ($fromnode, $tonode, $stream) = @_;
331         return "PC46^$tonode^$fromnode^$stream^";
332 }
333
334 # bull delete
335 sub pc49
336 {
337         my ($from, $subject) = @_;
338         my $hops = get_hops(49);
339         return "PC49^$from^$subject^$hops^~";
340 }
341
342 # periodic update of users, plus keep link alive device (always H99)
343 sub pc50
344 {
345         my $self = shift;
346         my $call = $self->call;
347         my $n = shift || '0';
348         my $hops = shift || 'H99';
349         return "PC50^$call^$n^$hops^";
350 }
351
352 # generate pings
353 sub pc51
354 {
355         my ($to, $from, $val) = @_;
356         return "PC51^$to^$from^$val^";
357 }
358
359 # clx remote cmd send
360 sub pc84
361 {
362         my($fromnode, $tonode, $call, $msg) = @_;
363         return "PC84^$tonode^$fromnode^$call^$msg^~";
364 }
365
366 # clx remote cmd reply
367 sub pc85
368 {
369         my($fromnode, $tonode, $call, $msg) = @_;
370         return "PC85^$tonode^$fromnode^$call^$msg^~";
371 }
372
373 # spider route broadcasts
374 #
375
376
377 sub _gen_pc92
378 {
379         my $sort = shift;
380         my $ext = shift;
381         my $s = "PC92^$main::mycall^" . gen_pc9x_t() . "^$sort";
382         if ($pc9x_nodupe_first_slot && ($sort eq 'A' || $sort eq 'D') && $_[0]->call eq $main::mycall) {
383                 shift;
384                 $s .= '^';
385         }
386         for (@_) {
387                 $s .= '^' . _encode_pc92_call($_, $ext);
388                 $ext = 0;                               # only the first slot has an ext.
389         }
390         return $s . '^H99^';
391 }
392
393 sub gen_pc92_with_time
394 {
395         my $call = shift;
396         my $sort = shift;
397         my $t = shift;
398         my $ext = 1;
399         my $s = "PC92^$call^$t^$sort";
400         if ($pc9x_nodupe_first_slot && ($sort eq 'A' || $sort eq 'D') && $_[0]->call eq $main::mycall) {
401                 shift;
402                 $s .= '^';
403         }
404         for (@_) {
405                 $s .= "^" . _encode_pc92_call($_, $ext);
406         }
407         return $s . '^H99^';
408 }
409
410 # add a local one
411 sub pc92a
412 {
413         return _gen_pc92('A', 0, @_);
414 }
415
416 # delete a local one
417 sub pc92d
418 {
419         return _gen_pc92('D', 0, @_);
420 }
421
422 # send a config
423 sub pc92c
424 {
425         return _gen_pc92('C', 1, @_);
426 }
427
428 # send a keep alive
429 sub pc92k
430 {
431         my $nref = shift;
432         my $s = "PC92^$main::mycall^" . gen_pc9x_t() . "^K";
433         $s .= "^" . _encode_pc92_call($nref, 1) . ":$main::me->{build}";
434         $s .= "^" . scalar $nref->nodes;
435         $s .= "^" . scalar $nref->users;
436         return $s . '^H99^';
437 }
438
439 # send a 'find' message
440 sub pc92f
441 {
442         my $target = shift;
443         my $from = shift;
444         return "PC92^$main::mycall^" . gen_pc9x_t() . "^F^$from^$target^H99^"
445 }
446
447 # send a 'reply' message
448 sub pc92r
449 {
450         my $to = shift;
451         my $target = shift;
452         my $flag = shift;
453         my $ms = shift;
454         return "PC92^$main::mycall^" . gen_pc9x_t() . "^R^$to^$target^$flag^$ms^H99^"
455 }
456
457 sub pc93
458 {
459         my $to = shift;                         # *, callsign, chat group name, sysop
460         my $from = shift;                       # from user callsign
461         my $via = shift || '*';                 # *, node call
462         my $line = shift;                       # the text
463         my $origin = shift;                     # this will be present on proxying from PC10
464
465         $line = unpad($line);
466         $line =~ s/\^/\\5E/g;           # remove any ^ characters
467         my $s = "PC93^$main::mycall^" . gen_pc9x_t() . "^$to^$from^$via^$line";
468         $s .= "^$origin" if $origin;
469         $s .= "^H99^";
470         return $s;
471 }
472
473 1;
474 __END__
475
476
477