1 /*
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif // HAVE_CONFIG_H
27
28 #include "snprintf.h"
29
30 #include <Xm/XmAll.h>
31 #include <X11/Xatom.h>
32 #include <X11/Shell.h>
33
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #else
43 #include <strings.h>
44 #endif
45 #include <ctype.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 #if TIME_WITH_SYS_TIME
50 #include <sys/time.h>
51 #include <time.h>
52 #else // TIME_WITH_SYS_TIME
53 #if HAVE_SYS_TIME_H
54 #include <sys/time.h>
55 #else // HAVE_SYS_TIME_H
56 #include <time.h>
57 #endif // HAVE_SYS_TIME_H
58 #endif // TIME_WITH_SYS_TIME
59
60 #include "xastir.h"
61 #include "main.h"
62 #include "messages.h"
63 #include "util.h"
64 #include "interface.h"
65 #include "xa_config.h"
66
67 // Must be last include file
68 #include "leak_detection.h"
69
70
71
72 char group_data_file[400];
73 char *group_data_list = NULL; // Need this NULL for Solaris!
74 int group_data_count = 0;
75 int group_data_max = 0;
76
77 char message_counter[5+1];
78
79 int auto_reply;
80 char auto_reply_message[100];
81
82 Message_Window mw[MAX_MESSAGE_WINDOWS+1]; // Send Message widgets
83
84 Message_transmit message_pool[MAX_OUTGOING_MESSAGES+1]; // Transmit message queue
85
86
87
88
89
clear_message_windows(void)90 void clear_message_windows(void)
91 {
92 int i;
93
94 begin_critical_section(&send_message_dialog_lock, "messages.c:clear_message_windows" );
95
96 for (i = 0; i < MAX_MESSAGE_WINDOWS; i++)
97 {
98
99 if (mw[i].send_message_dialog)
100 {
101 XtDestroyWidget(mw[i].send_message_dialog);
102 }
103
104 mw[i].send_message_dialog = (Widget)NULL;
105 mw[i].to_call_sign[0] = '\0';
106 mw[i].send_message_call_data = (Widget)NULL;
107 mw[i].D700_mode = (Widget)NULL;
108 mw[i].D7_mode = (Widget)NULL;
109 mw[i].HamHUD_mode = (Widget)NULL;
110 mw[i].message_data_line1 = (Widget)NULL;
111 mw[i].message_data_line2 = (Widget)NULL;
112 mw[i].message_data_line3 = (Widget)NULL;
113 mw[i].message_data_line4 = (Widget)NULL;
114 mw[i].send_message_text = (Widget)NULL;
115 }
116
117 end_critical_section(&send_message_dialog_lock, "messages.c:clear_message_windows" );
118
119 }
120
121
122
123
124
group_comp(const void * a,const void * b)125 static int group_comp(const void *a, const void *b)
126 {
127 if (!*(char *)a)
128 {
129 return ((int)(*(char *)b != '\0'));
130 }
131 return strcasecmp(a, b);
132 }
133
134
135
136
137
group_build_list(char * filename)138 void group_build_list(char *filename)
139 {
140 char *ptr;
141 FILE *f;
142 struct stat group_stat;
143 int i;
144
145 if (group_data_count == group_data_max)
146 {
147 ptr = realloc(group_data_list, (size_t)(group_data_max+10)*10);
148
149 if (ptr)
150 {
151 group_data_list = ptr;
152 group_data_max += 10;
153
154 //fprintf(stderr, "group_data_max: %d\n", group_data_max);
155
156 }
157 else
158 {
159 fprintf(stderr,
160 "Unable to allocate more memory for group_data_list (1)\n");
161 }
162 }
163
164
165 // Make sure we always listen for ourself, XASTIR, & our Version groups
166 xastir_snprintf(&group_data_list[0],10,"%s",my_callsign);
167 xastir_snprintf(&group_data_list[10],10,"XASTIR");
168 xastir_snprintf(&group_data_list[20],10,"%s",XASTIR_TOCALL);
169 group_data_count = 3;
170 // If we are in special group look for messages.
171 if (altnet)
172 {
173 xastir_snprintf(&group_data_list[group_data_count*10],10,"%s",altnet_call);
174 group_data_count++;
175 }
176 //
177
178 if (! stat(filename, &group_stat) )
179 {
180 f = fopen(filename, "r"); // File exists
181 }
182 else
183 {
184 f = fopen(filename, "w+"); // No file. Create it and open it.
185 }
186
187 if (f == NULL)
188 {
189 fprintf(stderr,"Couldn't open file for reading -or- appending: %s\n", filename);
190 return;
191 }
192
193 while (!feof(f))
194 {
195 if (group_data_count == group_data_max)
196 {
197 ptr = realloc(group_data_list, (size_t)(group_data_max+10)*10);
198 if (ptr)
199 {
200 group_data_list = ptr;
201 group_data_max += 10;
202
203 //fprintf(stderr, "group_data_max(2): %d\n", group_data_max);
204
205 }
206 else
207 {
208 fprintf(stderr,
209 "Unable to allocate more memory for group_data_list (2)\n");
210 }
211 }
212 if (group_data_count < group_data_max)
213 {
214 group_data_list[group_data_count*10] = '\0';
215 if (fgets(&group_data_list[group_data_count*10], 10, f) == NULL)
216 {
217 // Error reading file or end-of-file. Continue processing
218 // the (partial?) string we just read, in the code below.
219 }
220 if ((ptr = strchr(&group_data_list[group_data_count*10], '\n')))
221 {
222 *ptr = '\0';
223 }
224 else
225 while ((i = fgetc(f)) != EOF && i != '\n'); // clean-up after long group name
226
227 // check for DOS EOL markup!
228 if ((ptr = strchr(&group_data_list[group_data_count*10], '\r')))
229 {
230 *ptr = '\0';
231 }
232 if (group_data_list[group_data_count*10])
233 {
234 group_data_count++;
235 }
236 }
237 }
238 (void)fclose(f);
239 qsort(group_data_list, (size_t)group_data_count, 10, group_comp);
240
241 if (debug_level & 2)
242 {
243 for (i = 0; i < group_data_count; i++)
244 {
245 fprintf(stderr,"Group %2d: %s\n", i, &group_data_list[i*10]);
246 }
247 }
248 }
249
250
251
252
253
group_active(char * from)254 int group_active(char *from)
255 {
256 static struct stat current_group_stat;
257 struct stat group_stat;
258 static char altgroup[10];
259 char group_data_path[MAX_VALUE];
260
261 get_user_base_dir(group_data_file, group_data_path,
262 sizeof(group_data_path));
263
264 (void)remove_trailing_spaces(from);
265
266 // If we cycle to/from special group or file changes, rebuild group list.
267 if ((!stat( group_data_path, &group_stat )
268 && (current_group_stat.st_size != group_stat.st_size
269 || current_group_stat.st_mtime != group_stat.st_mtime
270 || current_group_stat.st_ctime != group_stat.st_ctime)))
271 {
272
273 // altgroup equates to "address of altgroup" which always evaluates
274 // as true. Commenting it out of the conditional. --we7u.
275 // || (altgroup && strcasecmp(altgroup, VERSIONFRM))) {
276
277 group_build_list( group_data_path );
278 current_group_stat = group_stat;
279 xastir_snprintf(altgroup,sizeof(altgroup),"%s",VERSIONFRM);
280 }
281 if (group_data_list != NULL) // Causes segfault on Solaris 2.5 without this!
282 {
283 return (int)(bsearch(from, group_data_list, (size_t)group_data_count, (size_t)10, group_comp) != NULL);
284 }
285 else
286 {
287 return(0);
288 }
289 }
290
291
292
293
294
look_for_open_group_data(char * to)295 int look_for_open_group_data(char *to)
296 {
297 int i,found;
298 char temp1[MAX_CALLSIGN+1];
299 char *temp_ptr;
300
301
302 begin_critical_section(&send_message_dialog_lock, "messages.c:look_for_open_group_data" );
303
304 found = FALSE;
305 for(i = 0; i < MAX_MESSAGE_WINDOWS; i++)
306 {
307 /* find station */
308 if(mw[i].send_message_dialog != NULL)
309 {
310
311 temp_ptr = XmTextFieldGetString(mw[i].send_message_call_data);
312 xastir_snprintf(temp1,
313 sizeof(temp1),
314 "%s",
315 temp_ptr);
316 XtFree(temp_ptr);
317
318 (void)to_upper(temp1);
319 /*fprintf(stderr,"Looking at call <%s> for <%s>\n",temp1,to);*/
320 if(strcmp(temp1,to)==0)
321 {
322 found=(int)TRUE;
323 break;
324 }
325 }
326 }
327
328 end_critical_section(&send_message_dialog_lock, "messages.c:look_for_open_group_data" );
329
330 return(found);
331 }
332
333
334
335
336
337 // What we wish to do here: Check for an active Send Message dialog
338 // that contains the callsign of interest. If one doesn't exist,
339 // create one and pop it up. We don't want to do this for duplicate
340 // message lines or duplicate acks for any particular QSO. To do so
341 // would cause the Send Message dialog to pop up on every such
342 // received message, which is VERY annoying (that was the default in
343 // Xastir for years, and nobody liked it!).
344 //
check_popup_window(char * from_call_sign,int group)345 int check_popup_window(char *from_call_sign, int group)
346 {
347 int i,found,j,ret;
348 char temp1[MAX_CALLSIGN+1];
349 char *temp_ptr;
350
351
352 //fprintf(stderr,"\tcheck_popup_window()\n");
353
354 ret = -1;
355 found = -1;
356
357 begin_critical_section(&send_message_dialog_lock, "messages.c:check_popup_window" );
358
359 // Check for an already-created dialog for talking to this
360 // particular call_sign.
361 //
362 for (i = 0; i < MAX_MESSAGE_WINDOWS; i++)
363 {
364
365 if (mw[i].send_message_dialog != NULL) // If dialog created
366 {
367
368 temp_ptr = XmTextFieldGetString(mw[i].send_message_call_data);
369 xastir_snprintf(temp1,
370 sizeof(temp1),
371 "%s",
372 temp_ptr);
373 XtFree(temp_ptr);
374
375 /*fprintf(stderr,"Looking at call <%s> for <%s>\n",temp1,from_call_sign);*/
376 if (strcasecmp(temp1, from_call_sign) == 0)
377 {
378 // Found a call_sign match in a Send Message dialog!
379
380 //fprintf(stderr,"\tFound a Send_message dialog match\n");
381
382 found = i;
383 break;
384 }
385 }
386 }
387
388 end_critical_section(&send_message_dialog_lock, "messages.c:check_popup_window" );
389
390 // If found == -1 at this point, we haven't found a Send Message
391 // dialog that contains the call_sign of interest.
392 //
393 if (found == -1 && (group == 2 || group_active(from_call_sign)))
394 {
395 /* no window found Open one! */
396
397 //fprintf(stderr,"\tNo Send Message dialog found, creating one\n");
398
399 begin_critical_section(&send_message_dialog_lock, "messages.c:check_popup_window2" );
400
401 i= -1;
402 for (j=0; j<MAX_MESSAGE_WINDOWS; j++)
403 {
404 if (!mw[j].send_message_dialog)
405 {
406 i=j;
407 break;
408 }
409 }
410
411 end_critical_section(&send_message_dialog_lock, "messages.c:check_popup_window2" );
412
413 if (i!= -1)
414 {
415
416 if (group == 1)
417 {
418 temp1[0] = '*';
419 temp1[1] = '\0';
420 }
421 else
422 {
423 temp1[0] = '\0';
424 }
425
426 strncat(temp1,
427 from_call_sign,
428 sizeof(temp1) - 1 - strlen(temp1));
429
430 if (!disable_all_popups)
431 {
432 Send_message(appshell, temp1, NULL);
433 }
434
435 update_messages(1);
436
437 ret=i;
438 }
439 else
440 {
441 fprintf(stderr,"No open windows!\n");
442 }
443 }
444 else
445 {
446 /* window open! */
447 // Pop it up
448 ret=found;
449 }
450
451 if (found != -1) // Already have a window
452 {
453 XtPopup(mw[i].send_message_dialog,XtGrabNone);
454 }
455
456 return(ret);
457 }
458
459
460
461
462
clear_outgoing_message(int i)463 void clear_outgoing_message(int i)
464 {
465 message_pool[i].active=MESSAGE_CLEAR;
466 message_pool[i].to_call_sign[0] = '\0';
467 message_pool[i].from_call_sign[0] = '\0';
468 message_pool[i].message_line[0] = '\0';
469 message_pool[i].seq[0] = '\0';
470 message_pool[i].active_time=0;;
471 message_pool[i].next_time=0L;
472 message_pool[i].tries=0;
473 }
474
475
476
477
478
479 // Clear all pending transmit messages that are from us and to the
480 // callsign listed. Perhaps it'd be better to time it out instead
481 // so that it still shows up in the message window? Here we just
482 // erase it.
483 //
clear_outgoing_messages_to(char * callsign)484 void clear_outgoing_messages_to(char *callsign)
485 {
486 int ii;
487
488
489 // fprintf(stderr,"Callsign: %s\n", callsign);
490
491 // Run through the entire outgoing message queue
492 for (ii = 0; ii < MAX_OUTGOING_MESSAGES; ii++)
493 {
494
495 // If it matches the callsign we're talking to
496 if (strcasecmp(message_pool[ii].to_call_sign,callsign) == 0)
497 {
498
499 // Record a fake ack and add "*CANCELLED*" to the
500 // message. This will be displayed in the Send Message
501 // dialog.
502 msg_record_ack(message_pool[ii].to_call_sign,
503 message_pool[ii].from_call_sign,
504 message_pool[ii].seq,
505 0, // Not a timeout
506 1); // Record a cancel
507
508 // Clear it out.
509 message_pool[ii].active=MESSAGE_CLEAR;
510 message_pool[ii].to_call_sign[0] = '\0';
511 message_pool[ii].from_call_sign[0] = '\0';
512 message_pool[ii].message_line[0] = '\0';
513 message_pool[ii].seq[0] = '\0';
514 message_pool[ii].active_time=0;;
515 message_pool[ii].next_time=0L;
516 message_pool[ii].tries=0;
517 }
518 }
519 }
520
521
522
523
524
525 // Change path on all pending transmit messages that are from us and
526 // to the callsign listed.
527 //
change_path_outgoing_messages_to(char * callsign,char * new_path)528 void change_path_outgoing_messages_to(char *callsign, char *new_path)
529 {
530 int ii;
531 char my_callsign[20];
532
533
534 //fprintf(stderr,
535 // "Changing all outgoing msgs to %s to new path: %s\n",
536 // callsign,
537 // new_path);
538
539 xastir_snprintf(my_callsign,
540 sizeof(my_callsign),
541 "%s",
542 callsign);
543
544 remove_trailing_spaces(my_callsign);
545
546 // Run through the entire outgoing message queue
547 for (ii = 0; ii < MAX_OUTGOING_MESSAGES; ii++)
548 {
549
550 if (message_pool[ii].active == MESSAGE_ACTIVE)
551 {
552
553 //fprintf(stderr,"\t'%s'\n\t'%s'\n",
554 // message_pool[ii].to_call_sign,
555 // my_callsign);
556
557 // If it matches the callsign we're talking to
558 if (strcasecmp(message_pool[ii].to_call_sign,my_callsign) == 0)
559 {
560
561 //fprintf(stderr,"\tFound an outgoing queued msg to change path on.\n");
562
563 xastir_snprintf(message_pool[ii].path,
564 sizeof(message_pool[ii].path),
565 "%s",
566 new_path);
567 }
568 }
569 }
570 }
571
572
573
574
575
576 time_t last_check_and_transmit = (time_t)0L;
577
578
579 // Kick the interval timer back to 7 and tries back to 1 for
580 // messages in this QSO. Used to get a QSO going again when the
581 // interval timer has gotten large, but the message is important to
582 // get through quickly.
583 //
kick_outgoing_timer(char * callsign)584 void kick_outgoing_timer(char *callsign)
585 {
586 int ii;
587
588
589 // fprintf(stderr,"Callsign: %s\n", callsign);
590
591 // Run through the entire outgoing message queue
592 for (ii = 0; ii < MAX_OUTGOING_MESSAGES; ii++)
593 {
594
595 // If it matches the callsign we're talking to
596 if (strcasecmp(message_pool[ii].to_call_sign,callsign) == 0)
597 {
598 message_pool[ii].next_time = (time_t)7L;
599 message_pool[ii].tries = 0;
600 message_pool[ii].active_time = (time_t)0L;
601 }
602 }
603
604 // Cause the transmit routine to get called again
605 last_check_and_transmit = (time_t)0L;
606 }
607
608
609
610
611
reset_outgoing_messages(void)612 void reset_outgoing_messages(void)
613 {
614 int i;
615
616 for(i=0; i<MAX_OUTGOING_MESSAGES; i++)
617 {
618 clear_outgoing_message(i);
619 }
620 }
621
622
623
624
625
clear_outgoing_messages(void)626 void clear_outgoing_messages(void)
627 {
628 int i;
629
630 for (i=0; i<MAX_OUTGOING_MESSAGES; i++)
631 {
632 clear_outgoing_message(i);
633 }
634
635 begin_critical_section(&send_message_dialog_lock, "messages.c:clear_outgoing_messages" );
636
637 /* clear message send buttons */
638 for (i=0; i<MAX_MESSAGE_WINDOWS; i++)
639 {
640 /* find station */
641 // if (mw[i].send_message_dialog!=NULL) /* clear submit */
642 // XtSetSensitive(mw[i].button_ok,TRUE);
643 }
644
645 end_critical_section(&send_message_dialog_lock, "messages.c:clear_outgoing_messages" );
646
647 }
648
649
650
651
652
653 // Bumps message sequence ID up to the next value.
654 //
655 // Roll over message_counter if we hit the max. Now with Reply/Ack
656 // protocol the max is only two characters worth. We changed to
657 // sending the sequence number in Base-?? format in order to get
658 // more range from the 2-character variable.
659 //
bump_message_counter(char * message_counter)660 int bump_message_counter(char *message_counter)
661 {
662
663 int bump_warning = 0;
664 message_counter[2] = '\0'; // Terminate at 2 chars
665
666 // Increment the least significant digit
667 message_counter[1]++;
668
669 // Span the gaps between the correct ranges
670 if (message_counter[1] == ':')
671 {
672 message_counter[1] = 'A';
673 }
674
675 if (message_counter[1] == '[')
676 {
677 message_counter[1] = 'a';
678 }
679
680 if (message_counter[1] == '{')
681 {
682 message_counter[1] = '0';
683 message_counter[0]++; // Roll over to next char
684 }
685
686 // Span the gaps between the correct ranges
687 if (message_counter[0] == ':')
688 {
689 message_counter[0] = 'A';
690 }
691
692 if (message_counter[0] == '[')
693 {
694 message_counter[0] = 'a';
695 }
696
697 if (message_counter[0] == '{')
698 {
699 message_counter[0] = '0';
700 bump_warning = 1;
701 }
702 return bump_warning;
703 }
704
705
706
707
708
709 // Adds a message to the outgoing message queue. Doesn't actually
710 // cause a transmit. "check_and_transmit_messages()" is the
711 // function which actually gets things moving.
712 //
713 // We also stuff the message into the main message queue so that the
714 // queued messages will appear in the Send Message box.
715 //
output_message(char * from,char * to,char * message,char * path)716 void output_message(char *from, char *to, char *message, char *path)
717 {
718 int ok,i,j;
719 char message_out[MAX_MESSAGE_OUTPUT_LENGTH+1+5+1]; // +'{' +msg_id +terminator
720 int last_space, message_ptr, space_loc;
721 int wait_on_first_ack;
722 int error;
723 long record;
724
725
726 //fprintf(stderr,"output_message:%s\n", message);
727
728 message_ptr=0;
729 last_space=0;
730 ok=0;
731 error=0;
732
733 if (debug_level & 2)
734 {
735 fprintf(stderr,"Output Message from <%s> to <%s>\n",from,to);
736 }
737
738 // Repeat until we process the entire message. We'll process it
739 // a chunk at a time, size of chunk to correspond to max APRS
740 // message line length.
741 //
742 while (!error && (message_ptr < (int)strlen(message)))
743 {
744 ok=0;
745 space_loc=0;
746
747 // Break a long message into smaller chunks that can be
748 // processed into APRS messages. Break at a space character
749 // if possible.
750 //
751 for (j=0; j<MAX_MESSAGE_OUTPUT_LENGTH; j++)
752 {
753
754 if(message[j+message_ptr] != '\0')
755 {
756
757 if(message[j+message_ptr]==' ')
758 {
759 last_space=j+message_ptr+1;
760 space_loc=j;
761 }
762
763 if (j!=MAX_MESSAGE_OUTPUT_LENGTH)
764 {
765 message_out[j]=message[j+message_ptr];
766 message_out[j+1] = '\0';
767 }
768 else
769 {
770
771 if(space_loc!=0)
772 {
773 message_out[space_loc] = '\0';
774 }
775 else
776 {
777 last_space=j+message_ptr;
778 }
779 }
780 }
781 else
782 {
783 j=MAX_MESSAGE_OUTPUT_LENGTH+1;
784 last_space=strlen(message)+1;
785 }
786 }
787
788 //fprintf(stderr,"message_out: %s\n", message_out);
789
790 if (debug_level & 2)
791 {
792 fprintf(stderr,"MESSAGE <%s> %d %d\n",message_out,message_ptr,last_space);
793 }
794
795 if (j >= MAX_MESSAGE_OUTPUT_LENGTH)
796 {
797 message_ptr = MAX_MESSAGE_OUTPUT_LENGTH;
798 }
799 else
800 {
801 message_ptr=last_space;
802 }
803
804 /* check for others in the queue */
805 wait_on_first_ack=0;
806 for (i=0; i<MAX_OUTGOING_MESSAGES; i++)
807 {
808 if (message_pool[i].active == MESSAGE_ACTIVE
809 && strcmp(to, message_pool[i].to_call_sign) == 0
810 && strcmp(from, "***") != 0)
811 {
812 wait_on_first_ack=1;
813 i=MAX_OUTGOING_MESSAGES+1; // Done with loop
814 }
815 }
816
817 for (i=0; i<MAX_OUTGOING_MESSAGES && !ok ; i++)
818 {
819 /* Check for clear position*/
820 if (message_pool[i].active==MESSAGE_CLEAR)
821 {
822 /* found a spot */
823 ok=1;
824
825 // Increment the message sequence ID variable
826 if (bump_message_counter(message_counter))
827 {
828 fprintf(stderr, "!WARNING!: Wrap around Message Counter");
829 }
830
831
832 // Note that Xastir's messaging can lock up if we do a rollover and
833 // have unacked messages on each side of the rollover. This is due
834 // to the logic in db.c that looks for the lowest numbered unacked
835 // message. We get stuck on both sides of the fence at once. To
836 // avoid this condition we could reduce the compare number (8100) to
837 // a smaller value, and only roll over when there are no unacked
838 // messages? Another way to do it would be to write a "0" to the
839 // config file if we're more than 1000 when we quit Xastir? That
840 // would probably be easier. It's still possible to get to 8100
841 // messages during one runtime though. Unlikely, but possible.
842
843 message_pool[i].active = MESSAGE_ACTIVE;
844 message_pool[i].wait_on_first_ack = wait_on_first_ack;
845 xastir_snprintf(message_pool[i].to_call_sign,
846 sizeof(message_pool[i].to_call_sign),
847 "%s",
848 to);
849 xastir_snprintf(message_pool[i].from_call_sign,
850 sizeof(message_pool[i].from_call_sign),
851 "%s",
852 from);
853 memcpy(message_pool[i].message_line, message_out, sizeof(message_pool[i].message_line));
854 // Terminate line
855 message_pool[i].message_line[sizeof(message_pool[i].message_line)-1] = '\0';
856
857 if (path != NULL)
858 xastir_snprintf(message_pool[i].path,
859 sizeof(message_pool[i].path),
860 "%s",
861 path);
862 else
863 {
864 message_pool[i].path[0] = '\0';
865 }
866
867 // // We compute the base-90 sequence number here
868 // // This allows it to range from "!!" to "zz"
869 // xastir_snprintf(message_pool[i].seq,
870 // sizeof(message_pool[i].seq),
871 // "%c%c",
872 // (char)(((message_counter / 90) % 90) + 33),
873 // (char)((message_counter % 90) + 33));
874
875 xastir_snprintf(message_pool[i].seq,
876 sizeof(message_pool[i].seq),
877 "%c%c",
878 message_counter[0],
879 message_counter[1]);
880
881 message_pool[i].active_time=0;
882 message_pool[i].next_time = (time_t)7L;
883
884 if (strcmp(from,"***")!= 0)
885 {
886 message_pool[i].tries = 0;
887 }
888 else
889 {
890 message_pool[i].tries = MAX_TRIES-1;
891 }
892
893 // Cause the message to get added to the main
894 // message queue as well, with the proper sequence
895 // number, so queued messages will appear in the
896 // Send Message box as unacked messages.
897 //
898
899 // We must get rid of the lock we already have for a moment, as
900 // update_messages(), which is called by msg_data_add(), also snags
901 // this lock.
902 end_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
903
904 (void)msg_data_add(to,
905 from,
906 message_out,
907 message_pool[i].seq,
908 MESSAGE_MESSAGE,
909 'L', // From the Local system
910 &record);
911 /*
912 fprintf(stderr,"msg_data_add %s %s %s %s\n",
913 to,
914 from,
915 message_out,
916 message_pool[i].seq);
917 */
918
919 // Regain the lock we had before
920 begin_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
921
922 }
923 }
924 if(!ok)
925 {
926 fprintf(stderr,"Output message queue is full!\n");
927 error=1;
928 }
929 }
930 }
931
932
933
934
935
936 // Here we're doing some routing of the transmitted packets. We
937 // want to keep Xastir from transmitting on ports that aren't
938 // actively being used in the QSO, but also cover the case where
939 // ports can go up/down during the QSO.
940 //
941 // Note that igates might get into the act quite a bit for RF<->RF
942 // QSO's if we're sending to the internet too, but that's a bug in
943 // the igate software, and not something that Xastir should try to
944 // correct itself.
945 //
transmit_message_data(char * to,char * message,char * path)946 void transmit_message_data(char *to, char *message, char *path)
947 {
948 DataRow *p_station;
949
950 if (debug_level & 2)
951 {
952 fprintf(stderr,"Transmitting data to %s : %s\n",to,message);
953 }
954
955 p_station = NULL;
956
957
958 if (strcmp(to, my_callsign) == 0) // My station message
959 {
960
961 // Send out all active ports
962
963 if (debug_level & 2)
964 {
965 fprintf(stderr,"My call VIA any way\n");
966 }
967
968 output_my_data(message,-1,0,0,0,path);
969
970 // All done
971 return;
972 }
973
974
975 if (!search_station_name(&p_station,to,1))
976 {
977
978 // No data record found for this station. Send to all
979 // active ports.
980
981 if (debug_level & 2)
982 {
983 fprintf(stderr,"VIA any way\n");
984 }
985
986 output_my_data(message,-1,0,0,0,path);
987
988 // All done
989 return;
990 }
991
992
993 if (debug_level & 2)
994 {
995 fprintf(stderr,"found station %s\n",p_station->call_sign);
996 }
997
998
999 // It's not being sent to my callsign but to somebody else
1000 // "out there". Because the truth is...
1001
1002
1003 if ( ((p_station->flag & ST_VIATNC) != 0)
1004 && (heard_via_tnc_in_past_hour(to)) )
1005 {
1006
1007 int port_num;
1008
1009
1010 // Station was heard via a TNC port within the previous
1011 // hour. Send to TNC port it was heard on.
1012 //
1013 output_my_data(message,p_station->heard_via_tnc_port,0,0,0,path);
1014
1015 // Send to all internet ports. Iterate through the port
1016 // definitions looking for internet ports, send the message
1017 // out once to each.
1018 //
1019 for (port_num = 0; port_num < MAX_IFACE_DEVICES; port_num++)
1020 {
1021
1022 // If it's an internet port, send the message.
1023 if (port_data[port_num].device_type == DEVICE_NET_STREAM)
1024 {
1025 output_my_data(message,port_num,0,0,0,path);
1026 }
1027 }
1028
1029 // All done
1030 return;
1031 }
1032
1033 else if (p_station->data_via==DATA_VIA_NET)
1034 {
1035 int port_num;
1036 int active_internet_ports_found = 0;
1037
1038
1039 // Station was heard over an internet interface. Check
1040 // whether we have any internet interfaces available with TX
1041 // enabled. If so, send out those ports. Else drop through
1042 // and hit the TRANSMIT-ALL clause at the end of this
1043 // function.
1044
1045 // Iterate through the port definitions looking for internet
1046 // ports with transmit enabled. Send the message out once
1047 // to each.
1048 //
1049 for (port_num = 0; port_num < MAX_IFACE_DEVICES; port_num++)
1050 {
1051
1052 // If it's an internet port and transmit is enabled,
1053 // send the message and set the flag.
1054 if ( (port_data[port_num].device_type == DEVICE_NET_STREAM)
1055 && (port_data[port_num].active == DEVICE_IN_USE)
1056 && (port_data[port_num].status == DEVICE_UP)
1057 && (devices[port_num].transmit_data == 1) )
1058 {
1059
1060 // Found a tx-enabled internet port that was up and
1061 // running. Send the message out this port.
1062 output_my_data(message,port_num,0,0,0,path);
1063
1064 active_internet_ports_found++;
1065 }
1066 }
1067
1068 if (active_internet_ports_found)
1069 {
1070 // We found at least one tx-enabled internet interface
1071 // that was up and running.
1072
1073 // All done.
1074 return;
1075 }
1076 else // No active tx-enabled internet ports were found.
1077 {
1078 // Drop through to the TRANSMIT-ALL clause below.
1079 }
1080 }
1081
1082
1083 // We've NOT heard this station on a TNC port within the
1084 // last hour and have no active tx-enabled internet ports to
1085 // send to. Send to ALL active ports.
1086
1087 if (debug_level & 2)
1088 {
1089 fprintf(stderr,"VIA any way\n");
1090 }
1091
1092 output_my_data(message,-1,0,0,0,path);
1093 }
1094
1095
1096
1097
1098
1099 // The below variables and functions implement the capability to
1100 // schedule ACK's some number of seconds out from the current time.
1101 // We use it to schedule duplicate ACK's at 30/60/120 seconds out,
1102 // but only if we see duplicate message lines from remote stations.
1103 //
1104 // Create a struct to hold the delayed ack's.
1105 typedef struct _delayed_ack_record
1106 {
1107 char to_call_sign[MAX_CALLSIGN+1];
1108 char message_line[MAX_MESSAGE_OUTPUT_LENGTH+1+5+1];
1109 char path[200];
1110 time_t active_time;
1111 struct _delayed_ack_record *next;
1112 } delayed_ack_record, *delayed_ack_record_p;
1113
1114 // And a pointer to a list of them.
1115 delayed_ack_record_p delayed_ack_list_head = NULL;
1116
1117
transmit_message_data_delayed(char * to,char * message,char * path,time_t when)1118 void transmit_message_data_delayed(char *to, char *message,
1119 char *path, time_t when)
1120 {
1121 delayed_ack_record_p ptr = delayed_ack_list_head;
1122
1123
1124 // We need to run down the current list looking for any records
1125 // that are identical and within 30 seconds time-wise of this
1126 // one. If so, don't allocate a new record. This keeps the
1127 // dupes down on transmit so that at the most we transmit one
1128 // ack per 30 seconds per QSO, except of course for real-time
1129 // ack's which don't go through this function.
1130
1131 // Run through the queue and check each record
1132 while (ptr != NULL)
1133 {
1134
1135 if ( strcmp(ptr->to_call_sign,to) == 0
1136 && strcmp(ptr->message_line,message) == 0 )
1137 {
1138
1139 //
1140 // We have matches on call_sign and message. Check the
1141 // time next.
1142 //
1143 if (labs(when - ptr->active_time) < 30)
1144 {
1145 //
1146 // We're within 30 seconds of an identical ack.
1147 // Drop this new one (don't add it).
1148 //
1149
1150 //fprintf(stderr,"Dropping delayed ack: Too close timewise to another: %s, %s\n",
1151 // to, message);
1152
1153 return; // Don't allocate new record on queue
1154 }
1155 }
1156 ptr = ptr->next;
1157 }
1158
1159 // If we made it to here, there aren't any queued ACK's that are
1160 // close enough in time to drop this new one. Add it to the
1161 // queue.
1162
1163 //fprintf(stderr, "Queuing ACK for delayed transmit: %s, %s\n",
1164 // to, message);
1165
1166 // Allocate a record to hold it
1167 ptr = (delayed_ack_record_p)malloc(sizeof(delayed_ack_record));
1168
1169 // Fill in the record
1170 xastir_snprintf(ptr->to_call_sign,
1171 sizeof(ptr->to_call_sign),
1172 "%s",
1173 to);
1174
1175 xastir_snprintf(ptr->message_line,
1176 sizeof(ptr->message_line),
1177 "%s",
1178 message);
1179
1180 if (path == NULL)
1181 {
1182 ptr->path[0] = '\0';
1183 }
1184 else
1185 {
1186 xastir_snprintf(ptr->path,
1187 sizeof(ptr->path),
1188 "%s",
1189 path);
1190 }
1191
1192 ptr->active_time = when;
1193
1194 // Add the record to the head of the list
1195 ptr->next = delayed_ack_list_head;
1196 delayed_ack_list_head = ptr;
1197 }
1198
1199
1200
1201
1202
1203 time_t delayed_transmit_last_check = (time_t)0;
1204
1205
check_delayed_transmit_queue(int curr_sec)1206 void check_delayed_transmit_queue(int curr_sec)
1207 {
1208 delayed_ack_record_p ptr = delayed_ack_list_head;
1209 int active_records = 0;
1210
1211
1212 // Skip this function if we did it during this second already.
1213 if (delayed_transmit_last_check == curr_sec)
1214 {
1215 return;
1216 }
1217 delayed_transmit_last_check = curr_sec;
1218
1219 //fprintf(stderr, "Checking delayed TX queue for something to transmit.\n");
1220 //fprintf(stderr, ".");
1221
1222 // Run down the linked list checking every record.
1223 while (ptr != NULL)
1224 {
1225 if (ptr->active_time != 0) // Active record
1226 {
1227 char new_path[MAX_LINE_SIZE+1];
1228
1229
1230 //fprintf(stderr, "Found active record\n");
1231
1232 active_records++;
1233
1234
1235 // Check for a custom path having been set in the Send
1236 // Message dialog. If so, use this for our outgoing
1237 // path instead and reset all of the queued message
1238 // paths to this station to this new path.
1239 //
1240 get_send_message_path(ptr->to_call_sign,
1241 new_path,
1242 sizeof(new_path));
1243
1244 if (new_path[0] != '\0'
1245 && strcmp(new_path, ptr->path) != 0)
1246 {
1247
1248 // We have a custom path set which is different than
1249 // the path saved with the outgoing message. Change
1250 // the path to match the new path.
1251 //
1252 //fprintf(stderr,
1253 // "Changing queued ack's to new path: %s\n",
1254 // new_path);
1255
1256 memcpy(ptr->path, new_path, sizeof(ptr->path));
1257 ptr->path[sizeof(ptr->path)-1] = '\0'; // Terminate string
1258 }
1259
1260
1261 if (ptr->active_time <= sec_now())
1262 {
1263 // Transmit it
1264 //fprintf(stderr,"Found something delayed to transmit! %ld\n",sec_now());
1265
1266 if (ptr->path[0] == '\0')
1267 {
1268 transmit_message_data(ptr->to_call_sign,
1269 ptr->message_line,
1270 NULL);
1271 }
1272 else
1273 {
1274 transmit_message_data(ptr->to_call_sign,
1275 ptr->message_line,
1276 ptr->path);
1277 }
1278
1279 ptr->active_time = (time_t)0;
1280 }
1281 }
1282 ptr = ptr->next;
1283 }
1284
1285 // Check if entire list contains inactive records. If so,
1286 // delete the list.
1287 //
1288 if (!active_records && (delayed_ack_list_head != NULL))
1289 {
1290 // No active records, but the list isn't empty. Reclaim the
1291 // records in the list.
1292 while (delayed_ack_list_head != NULL)
1293 {
1294 ptr = delayed_ack_list_head->next;
1295 free(delayed_ack_list_head);
1296 //fprintf(stderr,"Free'ing delayed_ack record\n");
1297 delayed_ack_list_head = ptr;
1298 }
1299 }
1300 }
1301
1302
1303
1304
1305
check_and_transmit_messages(time_t time)1306 void check_and_transmit_messages(time_t time)
1307 {
1308 int i;
1309 char temp[200];
1310 char to_call[40];
1311
1312
1313 // Skip this function if we did it during this second already.
1314 if (last_check_and_transmit == time)
1315 {
1316 return;
1317 }
1318 last_check_and_transmit = time;
1319
1320 for (i=0; i<MAX_OUTGOING_MESSAGES; i++)
1321 {
1322 if (message_pool[i].active==MESSAGE_ACTIVE)
1323 {
1324 if (message_pool[i].wait_on_first_ack!=1) // Tx only if 0
1325 {
1326 if (message_pool[i].active_time < time)
1327 {
1328 char *last_ack_ptr;
1329 char last_ack[5+1];
1330
1331
1332 if (message_pool[i].tries < MAX_TRIES)
1333 {
1334 char new_path[MAX_LINE_SIZE+1];
1335
1336
1337 /* sending message let the tnc and net transmits check to see if we should */
1338 if (debug_level & 2)
1339 fprintf(stderr,
1340 "Time %ld Active time %ld next time %ld\n",
1341 (long)time,
1342 (long)message_pool[i].active_time,
1343 (long)message_pool[i].next_time);
1344
1345 if (debug_level & 2)
1346 fprintf(stderr,"Send message#%d to <%s> from <%s>:%s-%s\n",
1347 message_pool[i].tries,
1348 message_pool[i].to_call_sign,
1349 message_pool[i].from_call_sign,
1350 message_pool[i].message_line,
1351 message_pool[i].seq);
1352
1353 pad_callsign(to_call,message_pool[i].to_call_sign);
1354
1355 // Add Leading ":" as per APRS Spec.
1356 // Add trailing '}' to signify that we're
1357 // Reply/Ack protocol capable.
1358 last_ack_ptr = get_most_recent_ack(to_call);
1359 if (last_ack_ptr != NULL)
1360 xastir_snprintf(last_ack,
1361 sizeof(last_ack),
1362 "%s",
1363 last_ack_ptr);
1364 else
1365 {
1366 last_ack[0] = '\0';
1367 }
1368
1369 xastir_snprintf(temp, sizeof(temp), ":%s:%s{%s}%s",
1370 to_call,
1371 message_pool[i].message_line,
1372 message_pool[i].seq,
1373 last_ack);
1374
1375 if (debug_level & 2)
1376 {
1377 fprintf(stderr,"MESSAGE OUT>%s<\n",temp);
1378 }
1379
1380
1381 // Check for a custom path having been set
1382 // in the Send Message dialog. If so, use
1383 // this for our outgoing path instead and
1384 // reset all of the queued message paths to
1385 // this station to this new path.
1386 //
1387 get_send_message_path(to_call,
1388 new_path,
1389 sizeof(new_path));
1390
1391 //fprintf(stderr,"get_send_message_path(%s) returned: %s\n",to_call,new_path);
1392
1393 if (new_path[0] != '\0'
1394 && strcmp(new_path,message_pool[i].path) != 0)
1395 {
1396
1397 // We have a custom path set which is
1398 // different than the path saved with
1399 // the outgoing message.
1400 //
1401 // Change all messages to that callsign
1402 // to match the new path.
1403 //
1404 change_path_outgoing_messages_to(to_call,new_path);
1405 }
1406
1407
1408 // Transmit the message
1409 transmit_message_data(message_pool[i].to_call_sign,
1410 temp,
1411 message_pool[i].path);
1412
1413
1414 message_pool[i].active_time = time + message_pool[i].next_time;
1415
1416 //fprintf(stderr,"%d\n",(int)message_pool[i].next_time);
1417 }
1418
1419 /*
1420 fprintf(stderr,
1421 "Msg Interval = %3ld seconds or %4.1f minutes\n",
1422 message_pool[i].next_time,
1423 message_pool[i].next_time / 60.0);
1424 */
1425
1426 // Record the interval we're using. Put it with
1427 // the message in the general message pool, so
1428 // that the Send Message dialog can display it.
1429 // It will only display it if the message is
1430 // actively being transmitted. If it has been
1431 // cancelled, timed out, or hasn't made it to
1432 // the transmit position yet, it won't be shown.
1433 //
1434 msg_record_interval_tries(message_pool[i].to_call_sign,
1435 message_pool[i].from_call_sign,
1436 message_pool[i].seq,
1437 message_pool[i].next_time, // Interval
1438 message_pool[i].tries); // Tries
1439
1440 // Start at 7 seconds for the interval. We set
1441 // it to 7 seconds in output_message() above.
1442 // Double the interval each retry until we hit
1443 // 10 minutes. Keep transmitting at 10 minute
1444 // intervals until we hit MAX_TRIES.
1445
1446 // Double the interval between messages
1447 message_pool[i].next_time = message_pool[i].next_time * 2;
1448
1449 // Limit the max interval to 10 minutes
1450 if (message_pool[i].next_time > (time_t)600L)
1451 {
1452 message_pool[i].next_time = (time_t)600L;
1453 }
1454
1455 message_pool[i].tries++;
1456
1457 // Expire it if we hit the limit
1458 if (message_pool[i].tries > MAX_TRIES)
1459 {
1460 char temp[150];
1461 char temp_to[20];
1462
1463 xastir_snprintf(temp,sizeof(temp),"To: %s, Msg: %s",
1464 message_pool[i].to_call_sign,
1465 message_pool[i].message_line);
1466 //popup_message(langcode("POPEM00004"),langcode("POPEM00017"));
1467 popup_message( "Retries Exceeded!", temp );
1468
1469 // Fake the system out: We're pretending
1470 // that we got an ACK back from it so that
1471 // we can either release the next message to
1472 // go out, or at least make the send button
1473 // sensitive again.
1474 // We need to copy the to_call_sign into
1475 // another variable because the
1476 // clear_acked_message() function clears out
1477 // the message then needs this parameter to
1478 // do another compare (to enable the Send Msg
1479 // button again).
1480 xastir_snprintf(temp_to,
1481 sizeof(temp_to),
1482 "%s",
1483 message_pool[i].to_call_sign);
1484
1485 // Record a fake ack and add "*TIMEOUT*" to
1486 // the message. This will be displayed in
1487 // the Send Message dialog.
1488 msg_record_ack(temp_to,
1489 message_pool[i].from_call_sign,
1490 message_pool[i].seq,
1491 1, // "1" specifies a timeout
1492 0); // Not a cancel
1493
1494 clear_acked_message(temp_to,
1495 message_pool[i].from_call_sign,
1496 message_pool[i].seq);
1497
1498 // if (mw[i].send_message_dialog!=NULL) /* clear submit */
1499 // XtSetSensitive(mw[i].button_ok,TRUE);
1500 }
1501 }
1502 }
1503 else
1504 {
1505 if (debug_level & 2)
1506 {
1507 fprintf(stderr,"Message #%s is waiting to have a previous one cleared\n",message_pool[i].seq);
1508 }
1509 }
1510 }
1511 }
1512 }
1513
1514
1515
1516
1517
1518 // Function which marks a message as ack'ed in the transmit queue
1519 // and releases the next message to allow it to be transmitted.
1520 // Handles REPLY-ACK format or normal ACK format just fine.
1521 //
clear_acked_message(char * from,char * to,char * seq)1522 void clear_acked_message(char *from, char *to, char *seq)
1523 {
1524 int i,ii;
1525 int found;
1526 char lowest[3];
1527 char temp1[MAX_CALLSIGN+1];
1528 char *temp_ptr;
1529 char msg_id[5+1];
1530
1531
1532 // Copy seq into local variable
1533 xastir_snprintf(msg_id,
1534 sizeof(msg_id),
1535 "%s",
1536 seq);
1537
1538 // Check for REPLY-ACK protocol. If found, terminate at the end
1539 // of the first ack.
1540 temp_ptr = strchr(msg_id, '}');
1541 if (temp_ptr)
1542 {
1543 *temp_ptr = '\0';
1544 }
1545
1546 (void)remove_trailing_spaces(msg_id); // This is IMPORTANT here!!!
1547
1548 //lowest=100000;
1549 // Highest Base-90 2-char string
1550 xastir_snprintf(lowest,sizeof(lowest),"zz");
1551 found= -1;
1552 for (i=0; i<MAX_OUTGOING_MESSAGES; i++)
1553 {
1554 if (message_pool[i].active==MESSAGE_ACTIVE)
1555 {
1556
1557 if (debug_level & 1)
1558 fprintf(stderr,
1559 "TO <%s> <%s> from <%s> <%s> seq <%s> <%s>\n",
1560 to,
1561 message_pool[i].to_call_sign,
1562 from,
1563 message_pool[i].from_call_sign,
1564 msg_id,
1565 message_pool[i].seq);
1566
1567 if (strcmp(message_pool[i].to_call_sign,from)==0)
1568 {
1569 if (debug_level & 1)
1570 {
1571 fprintf(stderr,"Matched message to_call_sign\n");
1572 }
1573 if (strcmp(message_pool[i].from_call_sign,to)==0)
1574 {
1575 if (debug_level & 1)
1576 {
1577 fprintf(stderr,"Matched message from_call_sign\n");
1578 }
1579 if (strcmp(message_pool[i].seq,msg_id)==0)
1580 {
1581 if (debug_level & 2)
1582 {
1583 fprintf(stderr,"Found and cleared\n");
1584 }
1585
1586 clear_outgoing_message(i);
1587 // now find and release next message, look for the lowest sequence?
1588 // What about when the sequence rolls over?
1589 for (i=0; i<MAX_OUTGOING_MESSAGES; i++)
1590 {
1591 if (message_pool[i].active==MESSAGE_ACTIVE)
1592 {
1593 if (strcmp(message_pool[i].to_call_sign,from)==0)
1594 {
1595 // Need to change this to a string compare instead of an integer
1596 // compare. We are using base-90 encoding now.
1597 //if (atoi(message_pool[i].seq)<lowest) {
1598 if (strncmp(message_pool[i].seq,lowest,2) < 0)
1599 {
1600 //lowest=atoi(message_pool[i].seq);
1601 xastir_snprintf(lowest,
1602 sizeof(lowest),
1603 "%s",
1604 message_pool[i].seq);
1605 found=i;
1606 }
1607 }
1608 }
1609 }
1610 // Release the next message in the queue for transmission
1611 if (found!= -1)
1612 {
1613 message_pool[found].wait_on_first_ack=0;
1614 }
1615 else
1616 {
1617 /* if no more clear the send button */
1618
1619 begin_critical_section(&send_message_dialog_lock, "messages.c:clear_acked_message" );
1620
1621 for (ii=0; ii<MAX_MESSAGE_WINDOWS; ii++)
1622 {
1623 /* find station */
1624 if (mw[ii].send_message_dialog!=NULL)
1625 {
1626
1627 temp_ptr = XmTextFieldGetString(mw[ii].send_message_call_data);
1628 xastir_snprintf(temp1,
1629 sizeof(temp1),
1630 "%s",
1631 temp_ptr);
1632 XtFree(temp_ptr);
1633
1634 (void)to_upper(temp1);
1635 //fprintf(stderr,"%s\t%s\n",temp1,from);
1636 // if (strcmp(temp1,from)==0) {
1637 /*clear submit*/
1638 // XtSetSensitive(mw[ii].button_ok,TRUE);
1639 // }
1640 }
1641 }
1642
1643 end_critical_section(&send_message_dialog_lock, "messages.c:clear_acked_message" );
1644
1645 }
1646 }
1647 else
1648 {
1649 if (debug_level & 1)
1650 {
1651 fprintf(stderr,"Sequences didn't match: %s %s\n",message_pool[i].seq,msg_id);
1652 }
1653 }
1654 }
1655 }
1656 }
1657 }
1658 }
1659
1660
1661
1662
1663
1664 // This routine is not currently used.
1665 //
send_queued(char * to)1666 void send_queued(char *to)
1667 {
1668 int i;
1669
1670 for (i=0; i<MAX_OUTGOING_MESSAGES ; i++)
1671 {
1672 /* Check for messages to call */
1673 if (message_pool[i].active==MESSAGE_ACTIVE)
1674 if (strcmp(to,message_pool[i].to_call_sign)==0)
1675 {
1676 message_pool[i].active_time=0;
1677 }
1678
1679 }
1680 }
1681
1682