]> git.itanic.dy.fi Git - maemo-mapper/blob - src/aprs_message.c
Added basic APRS support - Can be disabled by removing definition of INCLUDE_APRS
[maemo-mapper] / src / aprs_message.c
1 /*
2  * 
3  * This file is part of Maemo Mapper.
4  *
5  * Maemo Mapper is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Maemo Mapper is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
17  * 
18  * 
19  * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008):
20  * 
21  *  * XASTIR, Amateur Station Tracking and Information Reporting
22  * Copyright (C) 1999,2000  Frank Giannandrea
23  * Copyright (C) 2000-2007  The Xastir Group
24  * 
25  */
26
27
28 #ifdef HAVE_CONFIG_H
29 #    include "config.h"
30 #endif
31
32 #ifdef INCLUDE_APRS
33
34 #include "aprs_message.h"
35 #include "data.h"
36 #include "aprs.h"
37
38
39 /////////////////////////////////////////// Messages ///////////////////////////////////////////
40
41
42 long *msg_index;
43 long msg_index_end;
44 static long msg_index_max;
45
46 int log_wx_alert_data = 0;
47
48 Message *msg_data; // Array containing all messages,
49                           // including ones we've transmitted (via
50                           // loopback in the code)
51
52 time_t last_message_update = 0;
53 ack_record *ack_list_head = NULL;  // Head of linked list storing most recent ack's
54 int satellite_ack_mode;
55
56 int  new_message_data;
57 time_t last_message_remove;     // last time we did a check for message removing
58
59 // How often update_messages() will run, in seconds.
60 // This is necessary because routines like UpdateTime()
61 // call update_messages() VERY OFTEN.
62 //
63 // Actually, we just changed the code around so that we only call
64 // update_messages() with the force option, and only when we receive a
65 // message.  message_update_delay is no longer used, and we don't call
66 // update_messages() from UpdateTime() anymore.
67 static int message_update_delay = 300;
68
69
70
71
72 char *remove_trailing_spaces(char *data);
73 void update_messages(int force);
74
75
76
77
78 void clear_acked_message(char *from, char *to, char *seq) {
79         // TODO - replace stub
80 }
81
82 int check_popup_window(char *from_call_sign, int group) {
83         // TODO - replace stub
84         return 1;
85 }
86
87 void bulletin_data_add(char *call_sign, char *from_call, char *data, char *seq, char type, TAprsPort port)
88 {
89         // TODO - replace stub
90 }
91
92 int look_for_open_group_data(char *to) {
93         // TODO - replace stub
94         return 0;
95 }
96
97 void get_send_message_path(char *callsign, char *path, int path_size)
98 {
99         // TODO - replace stub
100 }
101
102 void transmit_message_data_delayed(char *to, char *message,
103                                    char *path, time_t when) {
104         // TODO - replace stub
105 }
106
107 int process_directed_query(char *call,char *path,char *message,TAprsPort port) {
108         // TODO - replace stub
109         return 0;
110 }
111
112 void transmit_message_data(char *to, char *message, char *path)
113 {
114         // TODO - replace stub
115 }
116
117 void popup_message(char *banner, char *message)
118 {
119         
120 }
121
122 // Saves latest ack in a linked list.  We need this value in order
123 // to use Reply/Ack protocol when sending out messages.
124 void store_most_recent_ack(char *callsign, char *ack) {
125     ack_record *p;
126     int done = 0;
127     char call[MAX_CALLSIGN+1];
128     char new_ack[5+1];
129
130     snprintf(call,
131         sizeof(call),
132         "%s",
133         callsign);
134     remove_trailing_spaces(call);
135
136     // Get a copy of "ack".  We might need to change it.
137     snprintf(new_ack,
138         sizeof(new_ack),
139         "%s",
140         ack);
141
142     // If it's more than 2 characters long, we can't use it for
143     // Reply/Ack protocol as there's only space enough for two.
144     // In this case we need to make sure that we blank out any
145     // former ack that was 1 or 2 characters, so that communications
146     // doesn't stop.
147     if ( strlen(new_ack) > 2 ) {
148         // It's too long, blank it out so that gets saved as "",
149         // which will overwrite any previously saved ack's that were
150         // short enough to use.
151         new_ack[0] = '\0';
152     }
153
154     // Search for matching callsign through linked list
155     p = ack_list_head;
156     while ( !done && (p != NULL) ) {
157         if (strcasecmp(call,p->callsign) == 0) {
158             done++;
159         }
160         else {
161             p = p->next;
162         }
163     }
164
165     if (done) { // Found it.  Update the ack field.
166         //fprintf(stderr,"Found callsign %s on recent ack list, Old:%s, New:%s\n",call,p->ack,new_ack);
167         snprintf(p->ack,sizeof(p->ack),"%s",new_ack);
168     }
169     else {  // Not found.  Add a new record to the beginning of the
170             // list.
171         //fprintf(stderr,"New callsign %s, adding to list.  Ack: %s\n",call,new_ack);
172         p = (ack_record *)malloc(sizeof(ack_record));
173         CHECKMALLOC(p);
174
175         snprintf(p->callsign,sizeof(p->callsign),"%s",call);
176         snprintf(p->ack,sizeof(p->ack),"%s",new_ack);
177         p->next = ack_list_head;
178         ack_list_head = p;
179     }
180 }
181
182
183
184
185
186 // Gets latest ack by callsign
187 char *get_most_recent_ack(char *callsign) {
188     ack_record *p;
189     int done = 0;
190     char call[MAX_CALLSIGN+1];
191
192     snprintf(call,
193         sizeof(call),
194         "%s",
195         callsign);
196     remove_trailing_spaces(call);
197
198     // Search for matching callsign through linked list
199     p = ack_list_head;
200     while ( !done && (p != NULL) ) {
201         if (strcasecmp(call,p->callsign) == 0) {
202             done++;
203         }
204         else {
205             p = p->next;
206         }
207     }
208
209     if (done) { // Found it.  Return pointer to ack string.
210         //fprintf(stderr,"Found callsign %s on linked list, returning ack: %s\n",call,p->ack);
211         return(&p->ack[0]);
212     }
213     else {
214         //fprintf(stderr,"Callsign %s not found\n",call);
215         return(NULL);
216     }
217 }
218
219
220
221
222
223 void init_message_data(void) {  // called at start of main
224
225     new_message_data = 0;
226     last_message_remove = sec_now();
227 }
228
229
230
231
232
233
234 void msg_clear_data(Message *clear) {
235     int size;
236     int i;
237     unsigned char *data_ptr;
238
239     data_ptr = (unsigned char *)clear;
240     size=sizeof(Message);
241     for(i=0;i<size;i++)
242         *data_ptr++ = 0;
243 }
244
245
246
247 #ifdef MSG_DEBUG
248
249 void msg_copy_data(Message *to, Message *from) {
250     int size;
251     int i;
252     unsigned char *data_ptr;
253     unsigned char *data_ptr_from;
254
255     data_ptr = (unsigned char *)to;
256     data_ptr_from = (unsigned char *)from;
257     size=sizeof(Message);
258     for(i=0;i<size;i++)
259         *data_ptr++ = *data_ptr_from++;
260 }
261 #endif /* MSG_DEBUG */
262
263
264
265
266
267 // Returns 1 if it's time to update the messages again
268 int message_update_time (void) {
269     if ( sec_now() > (last_message_update + message_update_delay) )
270         return(1);
271     else
272         return(0);
273 }
274
275
276
277
278
279 int msg_comp_active(const void *a, const void *b) {
280     char temp_a[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+2];
281     char temp_b[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+2];
282
283     snprintf(temp_a, sizeof(temp_a), "%c%s%s%s",
284             ((Message*)a)->active, ((Message*)a)->call_sign,
285             ((Message*)a)->from_call_sign,
286             ((Message*)a)->seq);
287     snprintf(temp_b, sizeof(temp_b), "%c%s%s%s",
288             ((Message*)b)->active, ((Message*)b)->call_sign,
289             ((Message*)b)->from_call_sign,
290             ((Message*)b)->seq);
291
292     return(strcmp(temp_a, temp_b));
293 }
294
295
296
297
298
299 int msg_comp_data(const void *a, const void *b) {
300     char temp_a[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+1];
301     char temp_b[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+1];
302
303     snprintf(temp_a, sizeof(temp_a), "%s%s%s",
304             msg_data[*(long*)a].call_sign, msg_data[*(long *)a].from_call_sign,
305             msg_data[*(long *)a].seq);
306     snprintf(temp_b, sizeof(temp_b), "%s%s%s", msg_data[*(long*)b].call_sign,
307             msg_data[*(long *)b].from_call_sign, msg_data[*(long *)b].seq);
308
309     return(strcmp(temp_a, temp_b));
310 }
311
312
313
314
315
316 void msg_input_database(Message *m_fill) {
317     void *m_ptr;
318     long i;
319     
320 //    fprintf(stderr, "DEBUG: Message: %s  %s\n", m_fill->call_sign, m_fill->message_line);
321     
322     if (msg_index_end == msg_index_max) {
323         for (i = 0; i < msg_index_end; i++) {
324
325             // Check for a record that is marked RECORD_NOTACTIVE.
326             // If found, use that record instead of malloc'ing a new
327             // one.
328             if (msg_data[msg_index[i]].active == RECORD_NOTACTIVE) {
329
330                 // Found an unused record.  Fill it in.
331                 memcpy(&msg_data[msg_index[i]], m_fill, sizeof(Message));
332
333 // Sort msg_data
334                 qsort(msg_data, (size_t)msg_index_end, sizeof(Message), msg_comp_active);
335
336                 for (i = 0; i < msg_index_end; i++) {
337                     msg_index[i] = i;
338                     if (msg_data[i].active == RECORD_NOTACTIVE) {
339                         msg_index_end = i;
340                         break;
341                     }
342                 }
343
344 // Sort msg_index
345                 qsort(msg_index, (size_t)msg_index_end, sizeof(long *), msg_comp_data);
346
347                 // All done with this message.
348                 return;
349             }
350         }
351
352         // Didn't find free message record.  Fetch some more space.
353         // Get more msg_data space.
354         m_ptr = realloc(msg_data, (msg_index_max+MSG_INCREMENT)*sizeof(Message));
355         if (m_ptr) {
356             msg_data = m_ptr;
357
358             // Get more msg_index space
359             m_ptr = realloc(msg_index, (msg_index_max+MSG_INCREMENT)*sizeof(Message *));
360             if (m_ptr) {
361                 msg_index = m_ptr;
362                 msg_index_max += MSG_INCREMENT;
363
364 //fprintf(stderr, "Max Message Array: %ld\n", msg_index_max);
365
366             }
367             else {
368                 // TODO
369                 //XtWarning("Unable to allocate more space for message index.\n");
370             }
371         }
372         else {
373                 // TODO 
374             //XtWarning("Unable to allocate more space for message database.\n");
375         }
376     }
377     if (msg_index_end < msg_index_max) {
378         msg_index[msg_index_end] = msg_index_end;
379
380         // Copy message data into new message record.
381         memcpy(&msg_data[msg_index_end++], m_fill, sizeof(Message));
382
383 // Sort msg_index
384         qsort(msg_index, (size_t)msg_index_end, sizeof(long *), msg_comp_data);
385     }
386 }
387
388
389
390
391
392 // Does a binary search through a sorted message database looking
393 // for a string match.
394 //
395 // If two or more messages match, this routine _should_ return the
396 // message with the latest timestamp.  This will ensure that earlier
397 // messages don't get mistaken for current messages, for the case
398 // where the remote station did a restart and is using the same
399 // sequence numbers over again.
400 //
401 long msg_find_data(Message *m_fill) {
402     long record_start, record_mid, record_end, return_record, done;
403     char tempfile[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+1];
404     char tempfill[MAX_CALLSIGN+MAX_CALLSIGN+MAX_MESSAGE_ORDER+1];
405
406
407     snprintf(tempfill, sizeof(tempfill), "%s%s%s",
408             m_fill->call_sign,
409             m_fill->from_call_sign,
410             m_fill->seq);
411
412     return_record = -1L;
413     if (msg_index && msg_index_end >= 1) {
414         /* more than one record */
415          record_start=0L;
416          record_end = (msg_index_end - 1);
417          record_mid=(record_end-record_start)/2;
418
419          done=0;
420          while (!done) {
421
422             /* get data for record start */
423             snprintf(tempfile, sizeof(tempfile), "%s%s%s",
424                     msg_data[msg_index[record_start]].call_sign,
425                     msg_data[msg_index[record_start]].from_call_sign,
426                     msg_data[msg_index[record_start]].seq);
427
428             if (strcmp(tempfill, tempfile) < 0) {
429                 /* filename comes before */
430                 /*fprintf(stderr,"Before No data found!!\n");*/
431                 done=1;
432                 break;
433             }
434             else { /* get data for record end */
435
436                 snprintf(tempfile, sizeof(tempfile), "%s%s%s",
437                         msg_data[msg_index[record_end]].call_sign,
438                         msg_data[msg_index[record_end]].from_call_sign,
439                         msg_data[msg_index[record_end]].seq);
440
441                 if (strcmp(tempfill,tempfile)>=0) { /* at end or beyond */
442                     if (strcmp(tempfill, tempfile) == 0) {
443                         return_record = record_end;
444 //fprintf(stderr,"record %ld",return_record);
445                     }
446
447                     done=1;
448                     break;
449                 }
450                 else if ((record_mid == record_start) || (record_mid == record_end)) {
451                     /* no mid for compare check to see if in the middle */
452                     done=1;
453                     snprintf(tempfile, sizeof(tempfile), "%s%s%s",
454                             msg_data[msg_index[record_mid]].call_sign,
455                             msg_data[msg_index[record_mid]].from_call_sign,
456                             msg_data[msg_index[record_mid]].seq);
457                     if (strcmp(tempfill,tempfile)==0) {
458                         return_record = record_mid;
459 //fprintf(stderr,"record: %ld",return_record);
460                     }
461                 }
462             }
463             if (!done) { /* get data for record mid */
464                 snprintf(tempfile, sizeof(tempfile), "%s%s%s",
465                         msg_data[msg_index[record_mid]].call_sign,
466                         msg_data[msg_index[record_mid]].from_call_sign,
467                         msg_data[msg_index[record_mid]].seq);
468
469                 if (strcmp(tempfill, tempfile) == 0) {
470                     return_record = record_mid;
471 //fprintf(stderr,"record %ld",return_record);
472                     done = 1;
473                     break;
474                 }
475
476                 if(strcmp(tempfill, tempfile)<0)
477                     record_end = record_mid;
478                 else
479                     record_start = record_mid;
480
481                 record_mid = record_start+(record_end-record_start)/2;
482             }
483         }
484     }
485     return(return_record);
486 }
487
488
489
490
491
492 void msg_replace_data(Message *m_fill, long record_num) {
493     memcpy(&msg_data[msg_index[record_num]], m_fill, sizeof(Message));
494 }
495
496
497
498
499
500 void msg_get_data(Message *m_fill, long record_num) {
501     memcpy(m_fill, &msg_data[msg_index[record_num]], sizeof(Message));
502 }
503
504
505
506
507
508 void msg_update_ack_stamp(long record_num) {
509
510     //fprintf(stderr,"Attempting to update ack stamp: %ld\n",record_num);
511     if ( (record_num >= 0) && (record_num < msg_index_end) ) {
512         msg_data[msg_index[record_num]].last_ack_sent = sec_now();
513         //fprintf(stderr,"Ack stamp: %ld\n",msg_data[msg_index[record_num]].last_ack_sent);
514     }
515     //fprintf(stderr,"\n\n\n*** Record: %ld ***\n\n\n",record_num);
516 }
517
518
519
520
521
522 // Called when we receive an ACK.  Sets the "acked" field in a
523 // Message which gets rid of the highlighting in the Send Message
524 // dialog for that message line.  This lets us know which messages
525 // have been acked and which have not.  If timeout is non-zero, then
526 // set acked to 2:  We use this in update_messages() to flag that
527 // "*TIMEOUT*" should prefix the string.  If cancelled is non-zero,
528 // set acked to 3:  We use this in update_messages() to flag that
529 // "*CANCELLED*" should prefix the string.
530 //
531 void msg_record_ack(char *to_call_sign,
532                     char *my_call,
533                     char *seq,
534                     int timeout,
535                     int cancel) {
536     Message m_fill;
537     long record;
538     int do_update = 0;
539
540
541     // Find the corresponding message in msg_data[i], set the
542     // "acked" field to one.
543
544     substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
545     (void)remove_trailing_asterisk(m_fill.call_sign);
546
547     substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
548     (void)remove_trailing_asterisk(m_fill.from_call_sign);
549
550     substr(m_fill.seq, seq, MAX_MESSAGE_ORDER);
551     (void)remove_trailing_spaces(m_fill.seq);
552     (void)remove_leading_spaces(m_fill.seq);
553
554     // Look for a message with the same to_call_sign, my_call,
555     // and seq number
556     record = msg_find_data(&m_fill);
557
558     if (record == -1L) { // No match yet, try another tactic.
559         if (seq[2] == '}' && strlen(seq) == 3) {
560
561             // Try it again without the trailing '}' character
562             m_fill.from_call_sign[2] = '\0';
563
564             // Look for a message with the same to_call_sign,
565             // my_call, and seq number (minus the trailing '}')
566             record = msg_find_data(&m_fill);
567         }
568     }
569
570     if(record != -1L) {     // Found a match!
571         // Only cause an update if this is the first ack.  This
572         // reduces dialog "flashing" a great deal
573         if ( msg_data[msg_index[record]].acked == 0 ) {
574
575             // Check for my callsign (including SSID).  If found,
576             // update any open message dialogs
577             if (is_my_call(msg_data[msg_index[record]].from_call_sign, 1) ) {
578
579                 //fprintf(stderr,"From: %s\tTo: %s\n",
580                 //    msg_data[msg_index[record]].from_call_sign,
581                 //    msg_data[msg_index[record]].call_sign);
582
583                 do_update++;
584             }
585         }
586         else {  // This message has already been acked.
587         }
588
589         if (cancel)
590             msg_data[msg_index[record]].acked = (char)3;
591         else if (timeout)
592             msg_data[msg_index[record]].acked = (char)2;
593         else
594             msg_data[msg_index[record]].acked = (char)1;
595
596         // Set the interval to zero so that we don't display it
597         // anymore in the dialog.  Same for tries.
598         msg_data[msg_index[record]].interval = 0;
599         msg_data[msg_index[record]].tries = 0;
600
601
602     }
603
604
605     if (do_update) {
606
607         update_messages(1); // Force an update
608
609         // Call check_popup_messages() here in order to pop up any
610         // closed Send Message dialogs.  For first ack's or
611         // CANCELLED messages it is less important, but for TIMEOUT
612         // messages it is very important.
613         //
614 // TODO
615 //        (void)check_popup_window(m_fill.call_sign, 2);  // Calls update_messages()
616     }
617 }
618
619
620
621
622
623 // Called when we receive a REJ packet (reject).  Sets the "acked"
624 // field in a Message to 4 to indicate that the message has been
625 // rejected by the remote station.  This gets rid of the
626 // highlighting in the Send Message dialog for that message line.
627 // This lets us know which messages have been rejected and which
628 // have not.  We use this in update_messages() to flag that
629 // "*REJECTED*" should prefix the string.
630 //
631 // The most common source of REJ packets would be from sending to a
632 // D700A who's buffers are full, so that it can't take another
633 // message.
634 //
635 void msg_record_rej(char *to_call_sign,
636                     char *my_call,
637                     char *seq) {
638     Message m_fill;
639     long record;
640     int do_update = 0;
641
642     // Find the corresponding message in msg_data[i], set the
643     // "acked" field to four.
644
645     substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
646     (void)remove_trailing_asterisk(m_fill.call_sign);
647
648     substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
649     (void)remove_trailing_asterisk(m_fill.from_call_sign);
650
651     substr(m_fill.seq, seq, MAX_MESSAGE_ORDER);
652     (void)remove_trailing_spaces(m_fill.seq);
653     (void)remove_leading_spaces(m_fill.seq);
654
655     // Look for a message with the same to_call_sign, my_call,
656     // and seq number
657     record = msg_find_data(&m_fill);
658
659     if (record == -1L) { // No match yet, try another tactic.
660         if (seq[2] == '}' && strlen(seq) == 3) {
661
662             // Try it again without the trailing '}' character
663             m_fill.from_call_sign[2] = '\0';
664
665             // Look for a message with the same to_call_sign,
666             // my_call, and seq number (minus the trailing '}')
667             record = msg_find_data(&m_fill);
668         }
669     }
670
671     if(record != -1L) {     // Found a match!
672         // Only cause an update if this is the first rej.  This
673         // reduces dialog "flashing" a great deal
674         if ( msg_data[msg_index[record]].acked == 0 ) {
675
676             // Check for my callsign (including SSID).  If found,
677             // update any open message dialogs
678             if (is_my_call(msg_data[msg_index[record]].from_call_sign, 1) ) {
679
680                 //fprintf(stderr,"From: %s\tTo: %s\n",
681                 //    msg_data[msg_index[record]].from_call_sign,
682                 //    msg_data[msg_index[record]].call_sign);
683
684                 do_update++;
685             }
686         }
687         else {  // This message has already been acked.
688         }
689
690         // Actually record the REJ here
691         msg_data[msg_index[record]].acked = (char)4;
692
693         // Set the interval to zero so that we don't display it
694         // anymore in the dialog.  Same for tries.
695         msg_data[msg_index[record]].interval = 0;
696         msg_data[msg_index[record]].tries = 0;
697
698     }
699
700
701     if (do_update) {
702
703         update_messages(1); // Force an update
704
705         // Call check_popup_messages() here in order to pop up any
706         // closed Send Message dialogs.  For first ack's or
707         // CANCELLED messages it is less important, but for TIMEOUT
708         // messages it is very important.
709         //
710 // TODO
711 //        (void)check_popup_window(m_fill.call_sign, 2);  // Calls update_messages()
712     }
713 }
714
715
716
717
718
719 // Called from check_and_transmit_messages().  Updates the interval
720 // field in our message record for the message currently being
721 // transmitted.  We'll use this in the Send Message dialog to
722 // display the current message interval.
723 //
724 void msg_record_interval_tries(char *to_call_sign,
725                     char *my_call,
726                     char *seq,
727                     time_t interval,
728                     int tries) {
729     Message m_fill;
730     long record;
731
732
733     // Find the corresponding message in msg_data[i]
734
735     substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
736     (void)remove_trailing_asterisk(m_fill.call_sign);
737
738     substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
739     (void)remove_trailing_asterisk(m_fill.from_call_sign);
740
741     substr(m_fill.seq, seq, MAX_MESSAGE_ORDER);
742     (void)remove_trailing_spaces(m_fill.seq);
743     (void)remove_leading_spaces(m_fill.seq);
744
745     // Look for a message with the same to_call_sign, my_call,
746     // and seq number
747     record = msg_find_data(&m_fill);
748     if(record != -1L) {     // Found a match!
749
750         msg_data[msg_index[record]].interval = interval;
751         msg_data[msg_index[record]].tries = tries;
752     }
753
754     update_messages(1); // Force an update
755 }
756
757
758
759
760
761 // Returns: time_t for last_ack_sent
762 //          -1 if the message doesn't pass our tests
763 //           0 if it is a new message.
764 //
765 // Also returns the record number found if not passed a NULL pointer
766 // in record_out or -1L if it's a new record.
767 //
768 time_t msg_data_add(char *call_sign, char *from_call, char *data,
769         char *seq, char type, TAprsPort port, long *record_out) {
770     Message m_fill;
771     long record;
772     char time_data[MAX_TIME];
773     int do_msg_update = 0;
774     time_t last_ack_sent;
775     int distance = -1;
776     char temp[10];
777     int group_message = 0;
778
779
780 //fprintf(stderr,"from:%s, to:%s, seq:%s\n", from_call, call_sign, seq);
781
782     // Set the default output condition.  We'll change this later if
783     // we need to.
784     if (record_out != NULL)
785         *record_out = -1l;
786
787     // Check for some reasonable string in call_sign parameter
788     if (call_sign == NULL || strlen(call_sign) == 0) {
789
790         return((time_t)-1l);
791     }
792 //else
793 //fprintf(stderr,"msg_data_add():call_sign: %s\n", call_sign);
794  
795     if ( (data != NULL) && (strlen(data) > MAX_MESSAGE_LENGTH) ) {
796
797         return((time_t)-1l);
798     }
799
800     substr(m_fill.call_sign, call_sign, MAX_CALLSIGN);
801     (void)remove_trailing_asterisk(m_fill.call_sign);
802
803     substr(m_fill.from_call_sign, from_call, MAX_CALLSIGN);
804     (void)remove_trailing_asterisk(m_fill.call_sign);
805
806     substr(m_fill.seq, seq, MAX_MESSAGE_ORDER);
807     (void)remove_trailing_spaces(m_fill.seq);
808     (void)remove_leading_spaces(m_fill.seq);
809
810 // If the sequence number is blank, then it may have been a query,
811 // directed query, or group message.  Assume it is a new message in
812 // each case and add it.
813
814     if (seq[0] != '\0') {   // Normal station->station messaging or
815                             // bulletins
816         // Look for a message with the same call_sign,
817         // from_call_sign, and seq number
818         record = msg_find_data(&m_fill);
819 //fprintf(stderr,"RECORD %ld  \n",record);
820 //fprintf(stderr,"Normal station->station message\n");
821     }
822     else {  // Group message/query/etc.
823         record = -1L;
824         group_message++;    // Flag it as a group message
825 //fprintf(stderr,"Group message/query/etc\n");
826     }
827     msg_clear_data(&m_fill);
828     if(record != -1L) { /* fill old data */
829         msg_get_data(&m_fill, record);
830         last_ack_sent = m_fill.last_ack_sent;
831         //fprintf(stderr,"Found: last_ack_sent: %ld\n",m_fill.last_ack_sent);
832
833         //fprintf(stderr,"Found a duplicate message.  Updating fields, seq %s\n",seq);
834
835         // If message is different this time, do an update to the
836         // send message window and update the sec_heard field.  The
837         // remote station must have restarted and is re-using the
838         // sequence numbers.  What a pain!
839         if (strcmp(m_fill.message_line,data) != 0) {
840             m_fill.sec_heard = sec_now();
841             last_ack_sent = (time_t)0;
842 //fprintf(stderr,"Message is different this time: Setting last_ack_sent to 0\n");
843  
844             if (type != MESSAGE_BULLETIN) { // Not a bulletin
845                 do_msg_update++;
846             }
847         }
848
849         // If message is the same, but the sec_heard field is quite
850         // old (more than 8 hours), the remote station must have
851         // restarted, is re-using the sequence numbers, and just
852         // happened to send the same message with the same sequence
853         // number.  Again, what a pain!  Either that, or we
854         // connected to a spigot with a _really_ long queue!
855         if (m_fill.sec_heard < (sec_now() - (8 * 60 * 60) )) {
856             m_fill.sec_heard = sec_now();
857             last_ack_sent = (time_t)0;
858 //fprintf(stderr,"Found >8hrs old: Setting last_ack_sent to 0\n");
859
860             if (type != MESSAGE_BULLETIN) { // Not a bulletin
861                 do_msg_update++;
862             }
863         }
864
865         // Check for zero time
866         if (m_fill.sec_heard == (time_t)0) {
867             m_fill.sec_heard = sec_now();
868             fprintf(stderr,"Zero time on a previous message.\n");
869         }
870     }
871     else {
872         // Only do this if it's a new message.  This keeps things
873         // more in sequence by not updating the time stamps
874         // constantly on old messages that don't get ack'ed.
875         m_fill.sec_heard = sec_now();
876         last_ack_sent = (time_t)0;
877         //fprintf(stderr,"New msg: Setting last_ack_sent to 0\n");
878
879         if (type != MESSAGE_BULLETIN) { // Not a bulletin
880 //fprintf(stderr,"Found new message\n");
881             do_msg_update++;    // Always do an update to the
882                                 // message window for new messages
883         }
884     }
885
886     /* FROM */
887     m_fill.port =port;
888     m_fill.active=RECORD_ACTIVE;
889     m_fill.type=type;
890     if (m_fill.heard_via_tnc != VIA_TNC)
891         m_fill.heard_via_tnc = (port == APRS_PORT_TTY) ? VIA_TNC : NOT_VIA_TNC;
892
893     distance = (int)(distance_from_my_station(from_call,temp, sizeof(temp)) + 0.9999);
894  
895     if (distance != 0) {    // Have a posit from the sending station
896         m_fill.position_known = 1;
897         //fprintf(stderr,"Position known: %s\n",from_call);
898     }
899     else {
900         //fprintf(stderr,"Position not known: %s\n",from_call);
901     }
902
903     substr(m_fill.call_sign,call_sign,MAX_CALLSIGN);
904     (void)remove_trailing_asterisk(m_fill.call_sign);
905
906     substr(m_fill.from_call_sign,from_call,MAX_CALLSIGN);
907     (void)remove_trailing_asterisk(m_fill.from_call_sign);
908
909     // Update the message field
910     substr(m_fill.message_line,data,MAX_MESSAGE_LENGTH);
911
912     substr(m_fill.seq,seq,MAX_MESSAGE_ORDER);
913     (void)remove_trailing_spaces(m_fill.seq);
914     (void)remove_leading_spaces(m_fill.seq);
915
916     // Create a timestamp from the current time
917     snprintf(m_fill.packet_time,
918         sizeof(m_fill.packet_time),
919         "%s",
920         get_time(time_data));
921
922     if(record == -1L) {     // No old record found
923         if (group_message)
924             m_fill.acked = 1;   // Group msgs/queries need no ack
925         else
926             m_fill.acked = 0;   // We can't have been acked yet
927
928         m_fill.interval = 0;
929         m_fill.tries = 0;
930
931         // We'll be sending an ack right away if this is a new
932         // message, so might as well set the time now so that we
933         // don't care about failing to set it in
934         // msg_update_ack_stamp due to the record number being -1.
935         m_fill.last_ack_sent = sec_now();
936
937         msg_input_database(&m_fill);    // Create a new entry
938         //fprintf(stderr,"No record found: Setting last_ack_sent to sec_now()00\n");
939     }
940     else {  // Old record found
941         //fprintf(stderr,"Replacing the message in the database, seq %s\n",seq);
942         msg_replace_data(&m_fill, record);  // Copy fields from m_fill to record
943     }
944
945     /* display messages */
946 // TODO
947  //   if (type == MESSAGE_MESSAGE)
948 //        all_messages(from,call_sign,from_call,data);
949
950     // Check for my callsign (including SSID).  If found, update any
951     // open message dialogs
952     if (       is_my_call(m_fill.from_call_sign, 1)
953             || is_my_call(m_fill.call_sign, 1) ) {
954
955         if (do_msg_update) {
956             update_messages(1); // Force an update
957         }
958     }
959  
960
961     // Return the important variables we'll need
962     if (record_out != NULL)
963         *record_out = record;
964
965 //fprintf(stderr,"\nrecord_out:%ld record %ld\n",*record_out,record);
966     return(last_ack_sent);
967
968 }   // End of msg_data_add()
969
970
971
972
973
974 // alert_data_add:  Function which adds NWS weather alerts to the
975 // alert hash.
976 //
977 // This function adds alerts directly to the alert hash, bypassing
978 // the message list and associated message-scan functions.
979 //
980 void alert_data_add(char *call_sign, char *from_call, char *data,
981         char *seq, char type, TAprsPort port) {
982     Message m_fill;
983     char time_data[MAX_TIME];
984
985
986
987 /*
988     if (log_wx_alert_data && from != DATA_VIA_FILE) {
989         char temp_msg[MAX_MESSAGE_LENGTH+1];
990
991         // Attempt to reconstruct the original weather alert packet
992         // here, minus the path.
993         snprintf(temp_msg,
994             sizeof(temp_msg),
995             "%s>APRS::%-9s:%s{%s",
996             from_call,
997             call_sign,
998             data,
999             seq);
1000         log_data( get_user_base_dir(LOGFILE_WX_ALERT), temp_msg);
1001 //        fprintf(stderr, "%s\n", temp_msg);
1002     }
1003 */
1004
1005     if ( (data != NULL) && (strlen(data) > MAX_MESSAGE_LENGTH) ) {
1006         return;
1007     }
1008
1009     substr(m_fill.call_sign, call_sign, MAX_CALLSIGN);
1010     (void)remove_trailing_asterisk(m_fill.call_sign);
1011
1012     substr(m_fill.from_call_sign, from_call, MAX_CALLSIGN);
1013     (void)remove_trailing_asterisk(m_fill.call_sign);
1014
1015     substr(m_fill.seq, seq, MAX_MESSAGE_ORDER);
1016     (void)remove_trailing_spaces(m_fill.seq);
1017     (void)remove_leading_spaces(m_fill.seq);
1018
1019     m_fill.sec_heard = sec_now();
1020
1021     /* FROM */
1022     m_fill.port=port;
1023     m_fill.active=RECORD_ACTIVE;
1024     m_fill.type=type;
1025
1026     // We don't have a value filled in yet here!
1027     //if (m_fill.heard_via_tnc != VIA_TNC)
1028     m_fill.heard_via_tnc = (port == APRS_PORT_TTY) ? VIA_TNC : NOT_VIA_TNC;
1029
1030     substr(m_fill.call_sign,call_sign,MAX_CALLSIGN);
1031     (void)remove_trailing_asterisk(m_fill.call_sign);
1032
1033     substr(m_fill.from_call_sign,from_call,MAX_CALLSIGN);
1034     (void)remove_trailing_asterisk(m_fill.from_call_sign);
1035
1036     // Update the message field
1037     substr(m_fill.message_line,data,MAX_MESSAGE_LENGTH);
1038
1039     substr(m_fill.seq,seq,MAX_MESSAGE_ORDER);
1040     (void)remove_trailing_spaces(m_fill.seq);
1041     (void)remove_leading_spaces(m_fill.seq);
1042
1043     // Create a timestamp from the current time
1044     snprintf(m_fill.packet_time,
1045         sizeof(m_fill.packet_time),
1046         "%s",
1047         get_time(time_data));
1048
1049     // Go try to add it to our alert hash.  alert_build_list() will
1050     // check for duplicates before adding it.
1051
1052 // TODO
1053 //    alert_build_list(&m_fill);
1054
1055     // This function fills in the Shapefile filename and index
1056     // so that we can later draw it.
1057 // TODO
1058 //    fill_in_new_alert_entries();
1059
1060
1061 }   // End of alert_data_add()
1062  
1063
1064
1065
1066
1067 // What I'd like to do for the following routine:  Use
1068 // XmTextGetInsertionPosition() or XmTextGetCursorPosition() to
1069 // find the last of the text.  Could also save the position for
1070 // each SendMessage window.  Compare the timestamps of messages
1071 // found with the last update time.  If newer, then add them to
1072 // the end.  This should stop the incessant scrolling.
1073
1074 // Another idea, easier method:  Create a buffer.  Snag out the
1075 // messages from the array and sort by time.  Put them into a
1076 // buffer.  Figure out the length of the text widget, and append
1077 // the extra length of the buffer onto the end of the text widget.
1078 // Once the message data is turned into a linked list, it might
1079 // be sorted already by time, so this window will look better
1080 // anyway.
1081
1082 // Calling update_messages with force == 1 will cause an update
1083 // no matter what message_update_time() says.
1084 void update_messages(int force) {
1085     // TODO
1086 }
1087 /*
1088 void update_messages(int force) {
1089     static XmTextPosition pos;
1090     char temp1[MAX_CALLSIGN+1];
1091     char temp2[500];
1092     char stemp[20];
1093     long i;
1094     int mw_p;
1095     char *temp_ptr;
1096
1097
1098     if ( message_update_time() || force) {
1099
1100 //fprintf(stderr,"update_messages()\n");
1101
1102         //fprintf(stderr,"Um %d\n",(int)sec_now() );
1103
1104         // go through all mw_p's! 
1105
1106         // Perform this for each message window
1107         for (mw_p=0; msg_index && mw_p < MAX_MESSAGE_WINDOWS; mw_p++) {
1108             //pos=0;
1109
1110 begin_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
1111
1112             if (mw[mw_p].send_message_dialog!=NULL) {
1113
1114 //fprintf(stderr,"\n");
1115
1116 //fprintf(stderr,"found send_message_dialog\n");
1117
1118                 // Clear the text from message window
1119                 XmTextReplace(mw[mw_p].send_message_text,
1120                     (XmTextPosition) 0,
1121                     XmTextGetLastPosition(mw[mw_p].send_message_text),
1122                     "");
1123
1124                 // Snag the callsign you're dealing with from the message dialogue
1125                 if (mw[mw_p].send_message_call_data != NULL) {
1126                     temp_ptr = XmTextFieldGetString(mw[mw_p].send_message_call_data);
1127                     snprintf(temp1,
1128                         sizeof(temp1),
1129                         "%s",
1130                         temp_ptr);
1131                     XtFree(temp_ptr);
1132
1133                     new_message_data--;
1134                     if (new_message_data<0)
1135                         new_message_data=0;
1136
1137                     if(strlen(temp1)>0) {   // We got a callsign from the dialog so
1138                         // create a linked list of the message indexes in time-sorted order
1139
1140                         typedef struct _index_record {
1141                             int index;
1142                             time_t sec_heard;
1143                             struct _index_record *next;
1144                         } index_record;
1145                         index_record *head = NULL;
1146                         index_record *p_prev = NULL;
1147                         index_record *p_next = NULL;
1148
1149                         // Allocate the first record (a dummy record)
1150                         head = (index_record *)malloc(sizeof(index_record));
1151                         CHECKMALLOC(head);
1152
1153                         head->index = -1;
1154                         head->sec_heard = (time_t)0;
1155                         head->next = NULL;
1156
1157                         (void)remove_trailing_spaces(temp1);
1158                         (void)to_upper(temp1);
1159
1160                         pos = 0;
1161                         // Loop through looking for messages to/from
1162                         // that callsign (including SSID)
1163                         for (i = 0; i < msg_index_end; i++) {
1164                             if (msg_data[msg_index[i]].active == RECORD_ACTIVE
1165                                     && (strcmp(temp1, msg_data[msg_index[i]].from_call_sign) == 0
1166                                         || strcmp(temp1,msg_data[msg_index[i]].call_sign) == 0)
1167                                     && (is_my_call(msg_data[msg_index[i]].from_call_sign, 1)
1168                                         || is_my_call(msg_data[msg_index[i]].call_sign, 1)
1169                                         || mw[mw_p].message_group ) ) {
1170                                 int done = 0;
1171
1172                                 // Message matches our parameters so
1173                                 // save the relevant data about the
1174                                 // message in our linked list.  Compare
1175                                 // the sec_heard field to see whether
1176                                 // we're higher or lower, and insert the
1177                                 // record at the correct spot in the
1178                                 // list.  We end up with a time-sorted
1179                                 // list.
1180                                 p_prev  = head;
1181                                 p_next = p_prev->next;
1182                                 while (!done && (p_next != NULL)) {  // Loop until end of list or record inserted
1183
1184                                     //fprintf(stderr,"Looping, looking for insertion spot\n");
1185
1186                                     if (p_next->sec_heard <= msg_data[msg_index[i]].sec_heard) {
1187                                         // Advance one record
1188                                         p_prev = p_next;
1189                                         p_next = p_prev->next;
1190                                     }
1191                                     else {  // We found the correct insertion spot
1192                                         done++;
1193                                     }
1194                                 }
1195
1196                                 //fprintf(stderr,"Inserting\n");
1197
1198                                 // Add the record in between p_prev and
1199                                 // p_next, even if we're at the end of
1200                                 // the list (in that case p_next will be
1201                                 // NULL.
1202                                 p_prev->next = (index_record *)malloc(sizeof(index_record));
1203                                 CHECKMALLOC(p_prev->next);
1204
1205                                 p_prev->next->next = p_next; // Link to rest of records or NULL
1206                                 p_prev->next->index = i;
1207                                 p_prev->next->sec_heard = msg_data[msg_index[i]].sec_heard;
1208 // Remember to free this entire linked list before exiting the loop for
1209 // this message window!
1210                             }
1211                         }
1212                         // Done processing the entire list for this
1213                         // message window.
1214
1215                         //fprintf(stderr,"Done inserting/looping\n");
1216
1217                         if (head->next != NULL) {   // We have messages to display
1218                             int done = 0;
1219
1220                             //fprintf(stderr,"We have messages to display\n");
1221
1222                             // Run through the linked list and dump the
1223                             // info out.  It's now in time-sorted order.
1224
1225 // Another optimization would be to keep a count of records added, then
1226 // later when we were dumping it out to the window, only dump the last
1227 // XX records out.
1228
1229                             p_prev = head->next;    // Skip the first dummy record
1230                             p_next = p_prev->next;
1231                             while (!done && (p_prev != NULL)) {  // Loop until end of list
1232                                 int j = p_prev->index;  // Snag the index out of the record
1233                                 char prefix[50];
1234                                 char interval_str[50];
1235                                 int offset = 22;    // Offset for highlighting
1236
1237
1238                                 //fprintf(stderr,"\nLooping through, reading messages\n");
1239  
1240 //fprintf(stderr,"acked: %d\n",msg_data[msg_index[j]].acked);
1241  
1242                                 // Message matches so snag the important pieces into a string
1243                                 snprintf(stemp, sizeof(stemp),
1244                                     "%c%c/%c%c %c%c:%c%c",
1245                                     msg_data[msg_index[j]].packet_time[0],
1246                                     msg_data[msg_index[j]].packet_time[1],
1247                                     msg_data[msg_index[j]].packet_time[2],
1248                                     msg_data[msg_index[j]].packet_time[3],
1249                                     msg_data[msg_index[j]].packet_time[8],
1250                                     msg_data[msg_index[j]].packet_time[9],
1251                                     msg_data[msg_index[j]].packet_time[10],
1252                                     msg_data[msg_index[j]].packet_time[11]
1253                                 );
1254
1255 // Somewhere in here we appear to be losing the first message.  It
1256 // doesn't get written to the window later in the QSO.  Same for
1257 // closing the window and re-opening it, putting the same callsign
1258 // in and pressing "New Call" button.  First message is missing.
1259
1260                                 // Label the message line with who sent it.
1261                                 // If acked = 2 a timeout has occurred
1262                                 // If acked = 3 a cancel has occurred
1263                                 if (msg_data[msg_index[j]].acked == 2) {
1264                                     snprintf(prefix,
1265                                         sizeof(prefix),
1266                                         "%s ",
1267                                         langcode("WPUPMSB016") ); // "*TIMEOUT*"
1268                                 }
1269                                 else if (msg_data[msg_index[j]].acked == 3) {
1270                                     snprintf(prefix,
1271                                         sizeof(prefix),
1272                                         "%s ",
1273                                         langcode("WPUPMSB017") ); // "*CANCELLED*"
1274                                 }
1275                                 else if (msg_data[msg_index[j]].acked == 4) {
1276                                     snprintf(prefix,
1277                                         sizeof(prefix),
1278                                         "%s ",
1279                                         langcode("WPUPMSB018") ); // "*REJECTED*"
1280                                 }
1281                                 else prefix[0] = '\0';
1282
1283                                 if (msg_data[msg_index[j]].interval) {
1284                                     snprintf(interval_str,
1285                                         sizeof(interval_str),
1286                                         ">%d/%lds",
1287                                         msg_data[msg_index[j]].tries + 1,
1288                                         (long)msg_data[msg_index[j]].interval);
1289
1290                                     // Don't highlight the interval
1291                                     // value
1292                                     offset = offset + strlen(interval_str);
1293                                 }
1294                                 else {
1295                                     interval_str[0] = '\0';
1296                                 }
1297
1298                                 snprintf(temp2, sizeof(temp2),
1299                                     "%s %-9s%s>%s%s\n",
1300                                     // Debug code.  Trying to find sorting error
1301                                     //"%ld  %s  %-9s>%s\n",
1302                                     //msg_data[msg_index[j]].sec_heard,
1303                                     stemp,
1304                                     msg_data[msg_index[j]].from_call_sign,
1305                                     interval_str,
1306                                     prefix,
1307                                     msg_data[msg_index[j]].message_line);
1308
1309 //fprintf(stderr,"message: %s\n", msg_data[msg_index[j]].message_line);
1310 //fprintf(stderr,"update_messages: %s|%s", temp1, temp2);
1311  
1312                                 // Replace the text from pos to pos+strlen(temp2) by the string "temp2"
1313                                 if (mw[mw_p].send_message_text != NULL) {
1314
1315                                     // Insert the text at the end
1316 //                                    XmTextReplace(mw[mw_p].send_message_text,
1317 //                                            pos,
1318 //                                            pos+strlen(temp2),
1319 //                                            temp2);
1320
1321                                     XmTextInsert(mw[mw_p].send_message_text,
1322                                             pos,
1323                                             temp2);
1324  
1325                                     // Set highlighting based on the
1326                                     // "acked" field.  Callsign
1327                                     // match here includes SSID.
1328 //fprintf(stderr,"acked: %d\t",msg_data[msg_index[j]].acked);
1329                                     if ( (msg_data[msg_index[j]].acked == 0)    // Not acked yet
1330                                             && ( is_my_call(msg_data[msg_index[j]].from_call_sign, 1)) ) {
1331 //fprintf(stderr,"Setting underline\t");
1332                                         XmTextSetHighlight(mw[mw_p].send_message_text,
1333                                             pos+offset,
1334                                             pos+strlen(temp2),
1335                                             //XmHIGHLIGHT_SECONDARY_SELECTED); // Underlining
1336                                             XmHIGHLIGHT_SELECTED);         // Reverse Video
1337                                     }
1338                                     else {  // Message was acked, get rid of highlighting
1339 //fprintf(stderr,"Setting normal\t");
1340                                         XmTextSetHighlight(mw[mw_p].send_message_text,
1341                                             pos+offset,
1342                                             pos+strlen(temp2),
1343                                             XmHIGHLIGHT_NORMAL);
1344                                     }
1345
1346 //fprintf(stderr,"Text: %s\n",temp2); 
1347
1348                                     pos += strlen(temp2);
1349
1350                                 }
1351
1352                                 // Advance to the next record in the list
1353                                 p_prev = p_next;
1354                                 if (p_next != NULL)
1355                                     p_next = p_prev->next;
1356
1357                             }   // End of while
1358                         }   // End of if
1359                         else {  // No messages matched, list is empty
1360                         }
1361
1362 // What does this do?  Move all of the text?
1363 //                        if (pos > 0) {
1364 //                            if (mw[mw_p].send_message_text != NULL) {
1365 //                                XmTextReplace(mw[mw_p].send_message_text,
1366 //                                        --pos,
1367 //                                        XmTextGetLastPosition(mw[mw_p].send_message_text),
1368 //                                        "");
1369 //                            }
1370 //                        }
1371
1372                         //fprintf(stderr,"Free'ing list\n");
1373
1374                         // De-allocate the linked list
1375                         p_prev = head;
1376                         while (p_prev != NULL) {
1377
1378                             //fprintf(stderr,"You're free!\n");
1379
1380                             p_next = p_prev->next;
1381                             free(p_prev);
1382                             p_prev = p_next;
1383                         }
1384
1385                         // Show the last added message in the window
1386                         XmTextShowPosition(mw[mw_p].send_message_text,
1387                             pos);
1388                     }
1389                 }
1390             }
1391
1392 end_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
1393
1394         }
1395         last_message_update = sec_now();
1396
1397 //fprintf(stderr,"Message index end: %ld\n",msg_index_end);
1398  
1399     }
1400 }
1401 */
1402
1403
1404
1405
1406 void mdelete_messages_from(char *from) {
1407     long i;
1408
1409     // Mark message records with RECORD_NOTACTIVE.  This will mark
1410     // them for re-use.
1411     for (i = 0; msg_index && i < msg_index_end; i++)
1412         if (strcmp(msg_data[i].call_sign, _aprs_mycall) == 0 
1413                         && strcmp(msg_data[i].from_call_sign, from) == 0)
1414             msg_data[i].active = RECORD_NOTACTIVE;
1415 }
1416
1417
1418
1419
1420
1421 void mdelete_messages_to(char *to) {
1422     long i;
1423
1424     // Mark message records with RECORD_NOTACTIVE.  This will mark
1425     // them for re-use.
1426     for (i = 0; msg_index && i < msg_index_end; i++)
1427         if (strcmp(msg_data[i].call_sign, to) == 0)
1428             msg_data[i].active = RECORD_NOTACTIVE;
1429 }
1430
1431
1432
1433
1434
1435 void mdelete_messages(char *to_from) {
1436     long i;
1437
1438     // Mark message records with RECORD_NOTACTIVE.  This will mark
1439     // them for re-use.
1440     for (i = 0; msg_index && i < msg_index_end; i++)
1441         if (strcmp(msg_data[i].call_sign, to_from) == 0 || strcmp(msg_data[i].from_call_sign, to_from) == 0)
1442             msg_data[i].active = RECORD_NOTACTIVE;
1443 }
1444
1445
1446
1447
1448
1449 void mdata_delete_type(const char msg_type, const time_t reference_time) {
1450     long i;
1451
1452     // Mark message records with RECORD_NOTACTIVE.  This will mark
1453     // them for re-use.
1454     for (i = 0; msg_index && i < msg_index_end; i++)
1455
1456         if ((msg_type == '\0' || msg_type == msg_data[i].type)
1457                 && msg_data[i].active == RECORD_ACTIVE
1458                 && msg_data[i].sec_heard < reference_time)
1459
1460             msg_data[i].active = RECORD_NOTACTIVE;
1461 }
1462
1463
1464
1465
1466
1467 void check_message_remove(time_t curr_sec) {       // called in timing loop
1468
1469     // Time to check for old messages again?  (Currently every ten
1470     // minutes)
1471 #ifdef EXPIRE_DEBUG
1472     if ( last_message_remove < (curr_sec - DEBUG_MESSAGE_REMOVE_CYCLE) ) {
1473 #else // EXPIRE_DEBUG
1474     if ( last_message_remove < (curr_sec - MESSAGE_REMOVE_CYCLE) ) {
1475 #endif
1476
1477         // Yes it is.  Mark all messages that are older than
1478         // sec_remove with the RECORD_NOTACTIVE flag.  This will
1479         // mark them for re-use.
1480 #ifdef EXPIRE_DEBUG
1481         mdata_delete_type('\0', curr_sec-DEBUG_MESSAGE_REMOVE);
1482 #else   // EXPIRE_DEBUG
1483         mdata_delete_type('\0', curr_sec-_aprs_sec_remove);
1484 #endif
1485
1486         last_message_remove = curr_sec;
1487     }
1488
1489     // Should we sort them at this point so that the unused ones are
1490     // near the end?  It looks like the message input functions do
1491     // this, so I guess we don't need to do it here.
1492 }
1493
1494
1495
1496
1497
1498 void mscan_file(char msg_type, void (*function)(Message *)) {
1499     long i;
1500
1501     for (i = 0; msg_index && i < msg_index_end; i++)
1502         if ((msg_type == '\0' || msg_type == msg_data[msg_index[i]].type) &&
1503                 msg_data[msg_index[i]].active == RECORD_ACTIVE)
1504             function(&msg_data[msg_index[i]]);
1505 }
1506
1507
1508
1509
1510
1511 void mprint_record(Message *m_fill) {
1512 /*
1513     fprintf(stderr,
1514         "%-9s>%-9s %s:%5s %s:%c :%s\n",
1515         m_fill->from_call_sign,
1516         m_fill->call_sign,
1517         langcode("WPUPMSB013"), // "seq"
1518         m_fill->seq,
1519         langcode("WPUPMSB014"), // "type"
1520         m_fill->type,
1521         m_fill->message_line);
1522 */
1523 }
1524
1525
1526
1527
1528
1529 void mdisplay_file(char msg_type) {
1530     fprintf(stderr,"\n\n");
1531     mscan_file(msg_type, mprint_record);
1532     fprintf(stderr,"\tmsg_index_end %ld, msg_index_max %ld\n", msg_index_end, msg_index_max);
1533 }
1534
1535
1536
1537
1538
1539 int decode_message(gchar *call,gchar *path,gchar *message,gint port,gint third_party) 
1540 {
1541     char *temp_ptr;
1542     char ipacket_message[300];
1543     char message_plus_acks[MAX_MESSAGE_LENGTH + 10];
1544     char from_call[MAX_CALLSIGN+1];
1545     char ack[20];
1546     int ok, len;
1547     char addr[9+1];
1548     char addr9[9+1];
1549     char msg_id[5+1];
1550     char orig_msg_id[5+1];
1551     char ack_string[6];
1552     int done;
1553     int reply_ack = 0;
1554     int to_my_call = 0;
1555     int to_my_base_call = 0;
1556     int from_my_call = 0;
1557
1558
1559     // :xxxxxxxxx:____0-67____             message              printable, except '|', '~', '{'
1560     // :BLNn     :____0-67____             general bulletin     printable, except '|', '~'
1561     // :BLNnxxxxx:____0-67____           + Group Bulletin
1562     // :BLNX     :____0-67____             Announcement
1563     // :NWS-xxxxx:____0-67____             NWS Service Bulletin
1564     // :NWS_xxxxx:____0-67____             NWS Service Bulletin
1565     // :xxxxxxxxx:ackn1-5n               + ack
1566     // :xxxxxxxxx:rejn1-5n               + rej
1567     // :xxxxxxxxx:____0-67____{n1-5n     + message
1568     // :NTS....
1569     //  01234567890123456
1570     // 01234567890123456    old
1571     // we get message with already extracted data ID
1572
1573
1574     if (is_my_call(call, 1) ) { // Check SSID also
1575         from_my_call++;
1576     }
1577
1578     ack_string[0] = '\0';   // Clear out the Reply/Ack result string
1579
1580     len = (int)strlen(message);
1581     ok = (int)(len > 9 && message[9] == ':');
1582
1583     if (ok) {
1584         
1585 //fprintf(stderr,"DEBUG: decode_message: from %s: %s\n", call, message);
1586 //return 0;
1587         
1588         substr(addr9,message,9); // extract addressee
1589         snprintf(addr,
1590             sizeof(addr),
1591             "%s",
1592             addr9);
1593         (void)remove_trailing_spaces(addr);
1594
1595         if (is_my_call(addr,1)) { // Check includes SSID
1596             to_my_call++;
1597         }
1598
1599         if (is_my_call(addr,0)) { // Check ignores SSID.  We use
1600                                   // this to catch messages to some
1601                                   // of our other SSID's
1602             to_my_base_call++;
1603         }
1604
1605         message = message + 10; // pointer to message text
1606
1607         // Save the message text and the acks/reply-acks before we
1608         // extract the acks below.
1609         snprintf(message_plus_acks,
1610             sizeof(message_plus_acks),
1611             "%s",
1612             message);
1613
1614         temp_ptr = strrchr(message,'{'); // look for message ID after
1615                                          //*last* { in message.
1616         msg_id[0] = '\0';
1617         if (temp_ptr != NULL) {
1618             substr(msg_id,temp_ptr+1,5); // extract message ID, could be non-digit
1619             temp_ptr[0] = '\0';          // adjust message end (chops off message ID)
1620         }
1621
1622         // Save the original msg_id away.
1623         snprintf(orig_msg_id,
1624             sizeof(orig_msg_id),
1625             "%s",
1626             msg_id);
1627
1628         // Check for Reply/Ack protocol in msg_id, which looks like
1629         // this:  "{XX}BB", where XX is the sequence number for the
1630         // message, and BB is the ack for the previous message from
1631         // my station.  I've also seen this from APRS+: "{XX}B", so
1632         // perhaps this is also possible "{X}B" or "{X}BB}".  We can
1633         // also get auto-reply responses from APRS+ that just have
1634         // "}X" or "}XX" at the end.  We decode those as well.
1635         //
1636
1637         temp_ptr = strstr(msg_id,"}"); // look for Reply Ack in msg_id
1638
1639         if (temp_ptr != NULL) { // Found Reply/Ack protocol!
1640  
1641             reply_ack++;
1642
1643 // Put this code into the UI message area as well (if applicable).
1644
1645             // Separate out the extra ack so that we can deal with
1646             // it properly.
1647             snprintf(ack_string,
1648                 sizeof(ack_string),
1649                 "%s",
1650                 temp_ptr+1); // After the '}' character!
1651
1652             // Terminate it here so that rest of decode works
1653             // properly.  We can get duplicate messages
1654             // otherwise.
1655             //
1656 // Note that we modify msg_id here.  Use orig_msg_id if we need the
1657 // unmodified version (full REPLY-ACK version) later.
1658             //
1659             temp_ptr[0] = '\0'; // adjust msg_id end
1660
1661         }
1662         else {  // Look for Reply Ack in message without sequence
1663                 // number
1664             temp_ptr = strstr(message,"}");
1665
1666             if (temp_ptr != NULL) {
1667                 int yy = 0;
1668
1669                 reply_ack++;
1670
1671 // Put this code into the UI message area as well (if applicable).
1672                 snprintf(ack_string,
1673                     sizeof(ack_string),
1674                     "%s",
1675                     temp_ptr+1);    // After the '}' character!
1676
1677                 ack_string[yy] = '\0';  // Terminate the string
1678
1679                 // Terminate it here so that rest of decode works
1680                 // properly.  We can get duplicate messages
1681                 // otherwise.
1682                 temp_ptr[0] = '\0'; // adjust message end
1683             } 
1684         }
1685
1686         done = 0;
1687     }
1688     else {
1689         done = 1;                               // fall through...
1690     }
1691
1692     len = (int)strlen(message);
1693     //--------------------------------------------------------------------------
1694     if (!done && len > 3 && strncmp(message,"ack",3) == 0) {              // ACK
1695
1696         // Received an ACK packet.  Note that these can carry the
1697         // REPLY-ACK protocol or a single ACK sequence number plus
1698         // perhaps an extra '}' on the end.  They should have one of
1699         // these formats:
1700         //      ack1        Normal ACK
1701         //      ackY        Normal ACK
1702         //      ack23       Normal ACK
1703         //      ackfH       Normal ACK
1704         //      ack23{      REPLY-ACK Protocol
1705         //      ack2Q}3d    REPLY-ACK Protocol
1706
1707         substr(msg_id,message+3,5);
1708         // fprintf(stderr,"ACK: %s: |%s| |%s|\n",call,addr,msg_id);
1709
1710         if (to_my_call) { // Check SSID also
1711
1712             // Note:  This function handles REPLY-ACK protocol just
1713             // fine, stripping off the 2nd ack if present.  It uses
1714             // only the first sequence number.
1715             clear_acked_message(call,addr,msg_id);  // got an ACK for me
1716
1717             // This one also handles REPLY-ACK protocol just fine.
1718             msg_record_ack(call,addr,msg_id,0,0);   // Record the ack for this message
1719         }
1720         else {  // ACK is for another station
1721             // Now if I have Igate on and I allow to retransmit station data
1722             // check if this message is to a person I have heard on my TNC within an X
1723             // time frame. If if is a station I heard and all the conditions are ok
1724             // spit the ACK out on the TNC -FG
1725 /*
1726  * TODO - Add igate support
1727             if (operate_as_an_igate>1
1728                     && from==DATA_VIA_NET
1729                     && !from_my_call     // Check SSID also
1730                     && port != -1) {    // Not from a log file
1731                 char short_path[100];
1732
1733                 shorten_path(path,short_path,sizeof(short_path));
1734
1735                 // Only send '}' and the ack_string if it's not
1736                 // empty, else just end the packet with the message
1737                 // string.  This keeps us from appending a '}' when
1738                 // it's not called for.
1739                 snprintf(ipacket_message,
1740                     sizeof(ipacket_message),
1741                     "}%s>%s,TCPIP,%s*::%s:%s",
1742  
1743                     call,
1744                     short_path,
1745                     my_callsign,
1746                     addr9,
1747                     message_plus_acks);
1748
1749
1750                 output_igate_rf(call,
1751                     addr,
1752                     path,
1753                     ipacket_message,
1754                     port,
1755                     third_party,
1756                     NULL);
1757
1758                 igate_msgs_tx++;
1759             }
1760 */
1761         }
1762         done = 1;
1763     }
1764     //--------------------------------------------------------------------------
1765     if (!done && len > 3 && strncmp(message,"rej",3) == 0) {              // REJ
1766
1767         substr(msg_id,message+3,5);
1768
1769         if (to_my_call) {   // Check SSID also
1770
1771             // Note:  This function handles REPLY-ACK protocol just
1772             // fine, stripping off the 2nd ack if present.  It uses
1773             // only the first sequence number.
1774             clear_acked_message(call,addr,msg_id);  // got an REJ for me
1775
1776             // This one also handles REPLY-ACK protocol just fine.
1777             msg_record_rej(call,addr,msg_id);   // Record the REJ for this message
1778         }
1779         else {  // REJ is for another station
1780             /* Now if I have Igate on and I allow to retransmit station data           */
1781             /* check if this message is to a person I have heard on my TNC within an X */
1782             /* time frame. If if is a station I heard and all the conditions are ok    */
1783             /* spit the REJ out on the TNC                                             */
1784                 
1785 /*
1786  * TODO - Add igate support
1787             if (operate_as_an_igate>1
1788                     && from==DATA_VIA_NET
1789                     && !from_my_call    // Check SSID also
1790                     && port != -1) {    // Not from a log file
1791                 char short_path[100];
1792
1793                 shorten_path(path,short_path,sizeof(short_path));
1794
1795                 // Only send '}' and the rej_string if it's not
1796                 // empty, else just end the packet with the message
1797                 // string.  This keeps us from appending a '}' when
1798                 // it's not called for.
1799                 snprintf(ipacket_message,
1800                     sizeof(ipacket_message),
1801                     "}%s>%s,TCPIP,%s*::%s:%s",
1802  
1803                     call,
1804                     short_path,
1805                     my_callsign,
1806                     addr9,
1807                     message_plus_acks);
1808
1809
1810                 output_igate_rf(call,
1811                     addr,
1812                     path,
1813                     ipacket_message,
1814                     port,
1815                     third_party,
1816                     NULL);
1817
1818                 igate_msgs_tx++;
1819             }
1820 */
1821         }
1822
1823         done = 1;
1824     }
1825     
1826     //--------------------------------------------------------------------------
1827     if (!done && strncmp(addr,"BLN",3) == 0) {                       // Bulletin
1828         // fprintf(stderr,"found BLN: |%s| |%s|\n",addr,message);
1829         bulletin_data_add(addr,call,message,"",MESSAGE_BULLETIN,port);
1830         done = 1;
1831     }
1832
1833     //--------------------------------------------------------------------------
1834     if (!done && strlen(msg_id) > 0 && to_my_call) {         // Message for me (including SSID check)
1835                                                              // with msg_id (sequence number)
1836         time_t last_ack_sent;
1837         long record;
1838
1839
1840 // Remember to put this code into the UI message area as well (if
1841 // applicable).
1842
1843         // Check for Reply/Ack
1844         if (reply_ack && strlen(ack_string) != 0) { // Have a free-ride ack to deal with
1845
1846             clear_acked_message(call,addr,ack_string);  // got an ACK for me
1847
1848             msg_record_ack(call,addr,ack_string,0,0);   // Record the ack for this message
1849         }
1850
1851         // Save the ack 'cuz we might need it while talking to this
1852         // station.  We need it to implement Reply/Ack protocol.
1853
1854 // Note that msg_id has already been truncated by this point.
1855 // orig_msg_id contains the full REPLY-ACK text.
1856
1857 //fprintf(stderr, "store_most_recent_ack()\n");
1858         store_most_recent_ack(call,msg_id);
1859  
1860         // fprintf(stderr,"found Msg w line to me: |%s| |%s|\n",message,msg_id);
1861         last_ack_sent = msg_data_add(addr,
1862                             call,
1863                             message,
1864                             msg_id,
1865                             MESSAGE_MESSAGE,
1866                             port, 
1867                             &record); // id_fixed
1868
1869         // Here we need to know if it is a new message or an old.
1870         // If we've already received it, we don't want to kick off
1871         // the alerts or pop up the Send Message dialog again.  If
1872         // last_ack_sent == (time_t)0, then it is a new message.
1873         //
1874         if (last_ack_sent == (time_t)0l && record == -1l) { // Msg we've never received before
1875
1876             new_message_data += 1;
1877
1878             // Note that the check_popup_window() function will
1879             // re-create a Send Message dialog if one doesn't exist
1880             // for this QSO.  Only call it for the first message
1881             // line or the first ack, not for any repeats.
1882             //
1883             (void)check_popup_window(call, 2);  // Calls update_messages()
1884
1885             //update_messages(1); // Force an update
1886
1887
1888         }
1889
1890         // Try to only send an ack out once per 30 seconds at the
1891         // fastest.
1892 //WE7U
1893 // Does this 30-second check work?
1894         //
1895         if ( (last_ack_sent != (time_t)-1l)   // Not an error
1896                 && (last_ack_sent + 30 ) < sec_now()
1897                 && !satellite_ack_mode // Disable separate ack's for satellite work
1898                 && port != -1 ) {   // Not from a log file
1899
1900             char path[MAX_LINE_SIZE+1];
1901
1902             // Update the last_ack_sent field for the message
1903             msg_update_ack_stamp(record);
1904
1905             pad_callsign(from_call,call);         /* ack the message */
1906
1907
1908             // Attempt to snag a custom path out of the Send Message
1909             // dialog, if set.  If not set, path will contain '\0';
1910             get_send_message_path(call, path, MAX_LINE_SIZE+1);
1911 //fprintf(stderr,"Path: %s\n", path);
1912
1913
1914             // In this case we want to send orig_msg_id back, not
1915             // the (possibly) truncated msg_id.  This is per Bob B's
1916             // Reply/Ack spec, sent to xastir-dev on Nov 14, 2001.
1917             snprintf(ack, sizeof(ack), ":%s:ack%s",from_call,orig_msg_id);
1918
1919 //WE7U
1920 // Need to figure out the reverse path for this one instead of
1921 // passing a NULL for the path?  Probably not, as auto-calculation
1922 // of paths isn't a good idea.
1923 //
1924 // What we need to do here is check whether we have a custom path
1925 // set for this QSO.  If so, pass that path along as the transmit
1926 // path.  messages.h:Message_Window struct has the send_message_path
1927 // variable in it.  If a Message_Window still exists for this QSO
1928 // then we can snag the user-entered path from there.  If the struct
1929 // has already been destroyed then we have nowhere to snag the
1930 // custom path from and have to rely on the default paths in each
1931 // interface properties dialog instead.  Then again, we _could_ snag
1932 // the path out of the last received message in the message database
1933 // for that case.  Might be better to disable the Close button, or
1934 // warn the user that the custom path will be lost if they close the
1935 // Send Message dialog.
1936
1937
1938             // Send out the immediate ACK
1939             if (path[0] == '\0')
1940                 transmit_message_data(call,ack,NULL);
1941             else
1942                 transmit_message_data(call,ack,path);
1943
1944
1945             if (record != -1l) { // Msg we've received before
1946
1947                 // It's a message that we've received before,
1948                 // consider sending an extra ACK in about 30 seconds
1949                 // to try to get it to the remote station.  Perhaps
1950                 // another one in 60 seconds as well.
1951
1952                 if (path[0] == '\0') {
1953                     transmit_message_data_delayed(call,ack,NULL,sec_now()+30);
1954                     transmit_message_data_delayed(call,ack,NULL,sec_now()+60);
1955                     transmit_message_data_delayed(call,ack,NULL,sec_now()+120);
1956                 }
1957                 else {
1958                     transmit_message_data_delayed(call,ack,path,sec_now()+30);
1959                     transmit_message_data_delayed(call,ack,path,sec_now()+60);
1960                     transmit_message_data_delayed(call,ack,path,sec_now()+120);
1961                 }
1962             }
1963
1964 /*
1965  * TODO 
1966             if (auto_reply == 1) {
1967
1968                 snprintf(ipacket_message,
1969                     sizeof(ipacket_message), "AA:%s", auto_reply_message);
1970
1971                 if (!from_my_call) // Check SSID also
1972                     output_message(my_callsign, call, ipacket_message, "");
1973             }
1974 */
1975         }
1976
1977
1978         done = 1;
1979     }
1980
1981     //--------------------------------------------------------------------------
1982     if (!done && strlen(msg_id) == 0 && to_my_call) {   // Message for me (including SSID check)
1983                                                         // but without message-ID.
1984         // These should appear in a Send Message dialog and should
1985         // NOT get ack'ed.  Kenwood radios send this message type as
1986         // an auto-answer or a buffer-full message.  They look
1987         // something like:
1988         //
1989         //      :WE7U-13 :Not at keyboard.
1990         //
1991
1992         time_t last_ack_sent;
1993         long record;
1994
1995
1996         if (len > 2
1997                 && message[0] == '?'
1998                 && port != -1   // Not from a log file
1999                 && to_my_call) { // directed query (check SSID also)
2000             // Smallest query known is "?WX".
2001             done = process_directed_query(call,path,message+1,port);
2002         }
2003
2004         // fprintf(stderr,"found Msg w line to me: |%s| |%s|\n",message,msg_id);
2005         last_ack_sent = msg_data_add(addr,
2006                             call,
2007                             message,
2008                             msg_id,
2009                             MESSAGE_MESSAGE,
2010                             port,
2011                             &record); // id_fixed
2012
2013         // Here we need to know if it is a new message or an old.
2014         // If we've already received it, we don't want to kick off
2015         // the alerts or pop up the Send Message dialog again.  If
2016         // last_ack_sent == (time_t)0, then it is a new message.
2017         //
2018         if (last_ack_sent == (time_t)0l && record == -1l) { // Msg we've never received before
2019
2020             new_message_data += 1;
2021
2022             // Note that the check_popup_window() function will
2023             // re-create a Send Message dialog if one doesn't exist
2024             // for this QSO.  Only call it for the first message
2025             // line or the first ack, not for any repeats.
2026             //
2027 //fprintf(stderr,"***check_popup_window 1\n");
2028             (void)check_popup_window(call, 2);  // Calls update_messages()
2029
2030             //update_messages(1); // Force an update
2031
2032         }
2033
2034         // Update the last_ack_sent field for the message, even
2035         // though we won't be sending an ack in response.
2036         msg_update_ack_stamp(record);
2037
2038
2039         done = 1;
2040     }
2041
2042     //--------------------------------------------------------------------------
2043     if (!done
2044             && ( (strncmp(addr,"NWS-",4) == 0)          // NWS weather alert
2045               || (strncmp(addr,"NWS_",4) == 0) ) ) {    // NWS weather alert compressed
2046
2047         // could have sort of line number
2048         //fprintf(stderr,"found NWS: |%s| |%s| |%s|\n",addr,message,msg_id);
2049
2050         (void)alert_data_add(addr,
2051             call,
2052             message,
2053             msg_id,
2054             MESSAGE_NWS,
2055             port);
2056
2057         done = 1;
2058         
2059 /*
2060  * TODO - add igate support
2061         if (operate_as_an_igate>1
2062                 && from==DATA_VIA_NET
2063                 && !from_my_call // Check SSID also
2064                 && port != -1) { // Not from a log file
2065             char short_path[100];
2066
2067             shorten_path(path,short_path,sizeof(short_path));
2068
2069             snprintf(ipacket_message,
2070                 sizeof(ipacket_message),
2071                 "}%s>%s,TCPIP,%s*::%s:%s",
2072                 call,
2073                 short_path,
2074                 my_callsign,
2075                 addr9,
2076                 message);
2077
2078             output_nws_igate_rf(call,
2079                 path,
2080                 ipacket_message,
2081                 port,
2082                 third_party);
2083         }
2084 */
2085     }
2086
2087     //--------------------------------------------------------------------------
2088     if (!done && strncmp(addr,"SKY",3) == 0) {  // NWS weather alert additional info
2089
2090         // could have sort of line number
2091         //fprintf(stderr,"found SKY: |%s| |%s| |%s|\n",addr,message,msg_id);
2092
2093                 // We don't wish to record these in memory.  They cause an infinite
2094                 // loop in the current code and a massive memory leak.
2095                 return(1);  // Tell the calling program that the packet was ok so
2096                             // that it doesn't add it with data_add() itself!
2097                 
2098
2099         done = 1;
2100 /*
2101  * TODO - add igate support
2102         if (operate_as_an_igate>1
2103                 && from==DATA_VIA_NET
2104                 && !from_my_call    // Check SSID also
2105                 && port != -1) { // Not from a log file
2106             char short_path[100];
2107
2108             shorten_path(path,short_path,sizeof(short_path));
2109
2110             snprintf(ipacket_message,
2111                 sizeof(ipacket_message),
2112                 "}%s>%s,TCPIP,%s*::%s:%s",
2113                 call,
2114                 short_path,
2115                 my_callsign,
2116                 addr9,
2117                 message);
2118
2119             output_nws_igate_rf(call,
2120                 path,
2121                 ipacket_message,
2122                 port,
2123                 third_party);
2124         }
2125 */
2126     }
2127
2128     //--------------------------------------------------------------------------
2129     if (!done && strlen(msg_id) > 0) {  // Other message with linenumber.  This
2130                                         // is either a message for someone else
2131                                         // or a message for another one of my
2132                                         // SSID's.
2133         long record_out;
2134         time_t last_ack_sent;
2135         char message_plus_note[MAX_MESSAGE_LENGTH + 30];
2136  
2137
2138         if (to_my_base_call && !from_my_call) {
2139             // Special case:  We saw a message w/msg_id that was to
2140             // one of our other SSID's, but it was not from
2141             // ourselves.  That last bit (!from_my_call) is
2142             // important in the case where we're working an event
2143             // with several stations using the same callsign.
2144             //
2145             // Store as if it came to my callsign, with a zeroed-out
2146             // msg_id so we can't try to ack it.  We also need some
2147             // other indication in the "Send Message" dialog as to
2148             // what's happening.  Perhaps add the original callsign
2149             // to the message itself in a note at the start?
2150             //
2151             snprintf(message_plus_note,
2152                 sizeof(message_plus_note),
2153                 "(Sent to:%s) %s",
2154                 addr,
2155                 message);
2156             last_ack_sent = msg_data_add(_aprs_mycall,
2157                 call,
2158                 message_plus_note,
2159                 "",
2160                 MESSAGE_MESSAGE,
2161                 port,
2162                 &record_out);
2163         }
2164         else {  // Normal case, messaging between other people
2165             last_ack_sent = msg_data_add(addr,
2166                 call,
2167                 message,
2168                 msg_id,
2169                 MESSAGE_MESSAGE,
2170                 port,
2171                 &record_out);
2172         }
2173  
2174         new_message_data += look_for_open_group_data(addr);
2175  
2176         // Note that the check_popup_window() function will
2177         // re-create a Send Message dialog if one doesn't exist for
2178         // this QSO.  Only call it for the first message line or the
2179         // first ack, not for any repeats.
2180         //
2181         if (last_ack_sent == (time_t)0l && record_out == -1l) { // Msg we've never received before
2182
2183             // Callsign check here also checks SSID for exact match
2184 // We need to do an SSID-non-specific check here so that we can pick
2185 // up messages intended for other stations of ours.
2186
2187             if ((to_my_base_call && check_popup_window(call, 2) != -1)
2188                     || check_popup_window(call, 0) != -1
2189                     || check_popup_window(addr, 1) != -1) {
2190                 update_messages(1); // Force an update
2191             }
2192         }
2193
2194         /* Now if I have Igate on and I allow to retransmit station data           */
2195         /* check if this message is to a person I have heard on my TNC within an X */
2196         /* time frame. If if is a station I heard and all the conditions are ok    */
2197         /* spit the message out on the TNC -FG                                     */
2198   /*
2199    * TODO - add igate support
2200         if (operate_as_an_igate>1
2201                 && last_ack_sent != (time_t)-1l
2202                 && from==DATA_VIA_NET
2203                 && !from_my_call        // Check SSID also
2204                 && !to_my_call          // Check SSID also
2205                 && port != -1) {    // Not from a log file
2206             char short_path[100];
2207
2208             shorten_path(path,short_path,sizeof(short_path));
2209             snprintf(ipacket_message,
2210                 sizeof(ipacket_message),
2211                 "}%s>%s,TCPIP,%s*::%s:%s",
2212                 call,
2213                 short_path,
2214                 my_callsign,
2215                 addr9,
2216                 message_plus_acks);
2217
2218
2219             output_igate_rf(call,
2220                 addr,
2221                 path,
2222                 ipacket_message,
2223                 port,
2224                 third_party,
2225                 NULL);
2226
2227             igate_msgs_tx++;
2228         }
2229 */
2230         done = 1;
2231     }
2232
2233     //--------------------------------------------------------------------------
2234
2235     if (!done) {                                   // message without line number
2236         long record_out;
2237         time_t last_ack_sent;
2238
2239
2240         last_ack_sent = msg_data_add(addr,
2241             call,
2242             message,
2243             "",
2244             MESSAGE_MESSAGE,
2245             port,
2246             &record_out);
2247
2248         new_message_data++;      // ??????
2249
2250         // Note that the check_popup_window() function will
2251         // re-create a Send Message dialog if one doesn't exist for
2252         // this QSO.  Only call it for the first message line or the
2253         // first ack, not for any repeats.
2254         //
2255         if (last_ack_sent == (time_t)0l && record_out == -1l) { // Msg we've never received before
2256             if (check_popup_window(addr, 1) != -1) {
2257                 //update_messages(1); // Force an update
2258             }
2259         }
2260
2261         // Could be response to a query.  Popup a messsage.
2262
2263 // Check addr for my_call and !third_party, then check later in the
2264 // packet for my_call if it is a third_party message?  Depends on
2265 // what the packet looks like by this point.
2266         if ( last_ack_sent != (time_t)-1l
2267                 && (message[0] != '?')
2268                 && to_my_call ) { // Check SSID also
2269
2270             // We no longer wish to have both popups and the Send
2271             // Group Message dialogs come up for every query
2272             // response, so we use popup_message() here instead of
2273             // popup_message_always() so that by default we'll see
2274             // the below message in STDERR.  If --with-errorpopups
2275             // has been configured in, we'll get a popup as well.
2276             // Send Group Message dialogs work well for multi-line
2277             // query responses, so we'll leave it that way.
2278             //
2279 // TODO
2280 //            popup_message(langcode("POPEM00018"),message);
2281
2282             // Check for Reply/Ack.  APRS+ sends an AA: response back
2283             // for auto-reply, with an embedded free-ride Ack.
2284             if (strlen(ack_string) != 0) {  // Have an extra ack to deal with
2285
2286                 clear_acked_message(call,addr,ack_string);  // got an ACK for me
2287
2288                 msg_record_ack(call,addr,ack_string,0,0);   // Record the ack for this message
2289             }
2290         }
2291  
2292         // done = 1;
2293     }
2294
2295     //--------------------------------------------------------------------------
2296
2297     if (ok)
2298         (void)data_add(STATION_CALL_DATA,
2299             call,
2300             path,
2301             message,
2302             port,
2303             NULL,
2304             third_party,
2305             0,  // Not a packet from my station
2306             0); // Not my object/item
2307
2308
2309     return(ok);
2310 }
2311
2312 #endif //INCLUDE_APRS