3 * This file is part of Maemo Mapper.
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.
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.
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/>.
19 * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008):
21 * * XASTIR, Amateur Station Tracking and Information Reporting
22 * Copyright (C) 1999,2000 Frank Giannandrea
23 * Copyright (C) 2000-2007 The Xastir Group
34 #include "aprs_message.h"
39 /////////////////////////////////////////// Messages ///////////////////////////////////////////
44 static long msg_index_max;
46 int log_wx_alert_data = 0;
48 Message *msg_data; // Array containing all messages,
49 // including ones we've transmitted (via
50 // loopback in the code)
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;
57 time_t last_message_remove; // last time we did a check for message removing
59 // How often update_messages() will run, in seconds.
60 // This is necessary because routines like UpdateTime()
61 // call update_messages() VERY OFTEN.
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;
72 char *remove_trailing_spaces(char *data);
73 void update_messages(int force);
78 void clear_acked_message(char *from, char *to, char *seq) {
79 // TODO - replace stub
82 int check_popup_window(char *from_call_sign, int group) {
83 // TODO - replace stub
87 void bulletin_data_add(char *call_sign, char *from_call, char *data, char *seq, char type, TAprsPort port)
89 // TODO - replace stub
92 int look_for_open_group_data(char *to) {
93 // TODO - replace stub
97 void get_send_message_path(char *callsign, char *path, int path_size)
99 // TODO - replace stub
102 void transmit_message_data_delayed(char *to, char *message,
103 char *path, time_t when) {
104 // TODO - replace stub
107 int process_directed_query(char *call,char *path,char *message,TAprsPort port) {
108 // TODO - replace stub
112 void transmit_message_data(char *to, char *message, char *path)
114 // TODO - replace stub
117 void popup_message(char *banner, char *message)
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) {
127 char call[MAX_CALLSIGN+1];
134 remove_trailing_spaces(call);
136 // Get a copy of "ack". We might need to change it.
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
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.
154 // Search for matching callsign through linked list
156 while ( !done && (p != NULL) ) {
157 if (strcasecmp(call,p->callsign) == 0) {
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);
169 else { // Not found. Add a new record to the beginning of the
171 //fprintf(stderr,"New callsign %s, adding to list. Ack: %s\n",call,new_ack);
172 p = (ack_record *)malloc(sizeof(ack_record));
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;
186 // Gets latest ack by callsign
187 char *get_most_recent_ack(char *callsign) {
190 char call[MAX_CALLSIGN+1];
196 remove_trailing_spaces(call);
198 // Search for matching callsign through linked list
200 while ( !done && (p != NULL) ) {
201 if (strcasecmp(call,p->callsign) == 0) {
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);
214 //fprintf(stderr,"Callsign %s not found\n",call);
223 void init_message_data(void) { // called at start of main
225 new_message_data = 0;
226 last_message_remove = sec_now();
234 void msg_clear_data(Message *clear) {
237 unsigned char *data_ptr;
239 data_ptr = (unsigned char *)clear;
240 size=sizeof(Message);
249 void msg_copy_data(Message *to, Message *from) {
252 unsigned char *data_ptr;
253 unsigned char *data_ptr_from;
255 data_ptr = (unsigned char *)to;
256 data_ptr_from = (unsigned char *)from;
257 size=sizeof(Message);
259 *data_ptr++ = *data_ptr_from++;
261 #endif /* MSG_DEBUG */
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) )
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];
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,
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,
292 return(strcmp(temp_a, temp_b));
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];
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);
309 return(strcmp(temp_a, temp_b));
316 void msg_input_database(Message *m_fill) {
320 // fprintf(stderr, "DEBUG: Message: %s %s\n", m_fill->call_sign, m_fill->message_line);
322 if (msg_index_end == msg_index_max) {
323 for (i = 0; i < msg_index_end; i++) {
325 // Check for a record that is marked RECORD_NOTACTIVE.
326 // If found, use that record instead of malloc'ing a new
328 if (msg_data[msg_index[i]].active == RECORD_NOTACTIVE) {
330 // Found an unused record. Fill it in.
331 memcpy(&msg_data[msg_index[i]], m_fill, sizeof(Message));
334 qsort(msg_data, (size_t)msg_index_end, sizeof(Message), msg_comp_active);
336 for (i = 0; i < msg_index_end; i++) {
338 if (msg_data[i].active == RECORD_NOTACTIVE) {
345 qsort(msg_index, (size_t)msg_index_end, sizeof(long *), msg_comp_data);
347 // All done with this message.
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));
358 // Get more msg_index space
359 m_ptr = realloc(msg_index, (msg_index_max+MSG_INCREMENT)*sizeof(Message *));
362 msg_index_max += MSG_INCREMENT;
364 //fprintf(stderr, "Max Message Array: %ld\n", msg_index_max);
369 //XtWarning("Unable to allocate more space for message index.\n");
374 //XtWarning("Unable to allocate more space for message database.\n");
377 if (msg_index_end < msg_index_max) {
378 msg_index[msg_index_end] = msg_index_end;
380 // Copy message data into new message record.
381 memcpy(&msg_data[msg_index_end++], m_fill, sizeof(Message));
384 qsort(msg_index, (size_t)msg_index_end, sizeof(long *), msg_comp_data);
392 // Does a binary search through a sorted message database looking
393 // for a string match.
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.
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];
407 snprintf(tempfill, sizeof(tempfill), "%s%s%s",
409 m_fill->from_call_sign,
413 if (msg_index && msg_index_end >= 1) {
414 /* more than one record */
416 record_end = (msg_index_end - 1);
417 record_mid=(record_end-record_start)/2;
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);
428 if (strcmp(tempfill, tempfile) < 0) {
429 /* filename comes before */
430 /*fprintf(stderr,"Before No data found!!\n");*/
434 else { /* get data for record end */
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);
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);
450 else if ((record_mid == record_start) || (record_mid == record_end)) {
451 /* no mid for compare check to see if in the middle */
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);
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);
469 if (strcmp(tempfill, tempfile) == 0) {
470 return_record = record_mid;
471 //fprintf(stderr,"record %ld",return_record);
476 if(strcmp(tempfill, tempfile)<0)
477 record_end = record_mid;
479 record_start = record_mid;
481 record_mid = record_start+(record_end-record_start)/2;
485 return(return_record);
492 void msg_replace_data(Message *m_fill, long record_num) {
493 memcpy(&msg_data[msg_index[record_num]], m_fill, sizeof(Message));
500 void msg_get_data(Message *m_fill, long record_num) {
501 memcpy(m_fill, &msg_data[msg_index[record_num]], sizeof(Message));
508 void msg_update_ack_stamp(long record_num) {
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);
515 //fprintf(stderr,"\n\n\n*** Record: %ld ***\n\n\n",record_num);
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.
531 void msg_record_ack(char *to_call_sign,
541 // Find the corresponding message in msg_data[i], set the
542 // "acked" field to one.
544 substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
545 (void)remove_trailing_asterisk(m_fill.call_sign);
547 substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
548 (void)remove_trailing_asterisk(m_fill.from_call_sign);
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);
554 // Look for a message with the same to_call_sign, my_call,
556 record = msg_find_data(&m_fill);
558 if (record == -1L) { // No match yet, try another tactic.
559 if (seq[2] == '}' && strlen(seq) == 3) {
561 // Try it again without the trailing '}' character
562 m_fill.from_call_sign[2] = '\0';
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);
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 ) {
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) ) {
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);
586 else { // This message has already been acked.
590 msg_data[msg_index[record]].acked = (char)3;
592 msg_data[msg_index[record]].acked = (char)2;
594 msg_data[msg_index[record]].acked = (char)1;
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;
607 update_messages(1); // Force an update
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.
615 // (void)check_popup_window(m_fill.call_sign, 2); // Calls update_messages()
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.
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
635 void msg_record_rej(char *to_call_sign,
642 // Find the corresponding message in msg_data[i], set the
643 // "acked" field to four.
645 substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
646 (void)remove_trailing_asterisk(m_fill.call_sign);
648 substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
649 (void)remove_trailing_asterisk(m_fill.from_call_sign);
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);
655 // Look for a message with the same to_call_sign, my_call,
657 record = msg_find_data(&m_fill);
659 if (record == -1L) { // No match yet, try another tactic.
660 if (seq[2] == '}' && strlen(seq) == 3) {
662 // Try it again without the trailing '}' character
663 m_fill.from_call_sign[2] = '\0';
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);
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 ) {
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) ) {
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);
687 else { // This message has already been acked.
690 // Actually record the REJ here
691 msg_data[msg_index[record]].acked = (char)4;
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;
703 update_messages(1); // Force an update
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.
711 // (void)check_popup_window(m_fill.call_sign, 2); // Calls update_messages()
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.
724 void msg_record_interval_tries(char *to_call_sign,
733 // Find the corresponding message in msg_data[i]
735 substr(m_fill.call_sign, to_call_sign, MAX_CALLSIGN);
736 (void)remove_trailing_asterisk(m_fill.call_sign);
738 substr(m_fill.from_call_sign, my_call, MAX_CALLSIGN);
739 (void)remove_trailing_asterisk(m_fill.from_call_sign);
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);
745 // Look for a message with the same to_call_sign, my_call,
747 record = msg_find_data(&m_fill);
748 if(record != -1L) { // Found a match!
750 msg_data[msg_index[record]].interval = interval;
751 msg_data[msg_index[record]].tries = tries;
754 update_messages(1); // Force an update
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.
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.
768 time_t msg_data_add(char *call_sign, char *from_call, char *data,
769 char *seq, char type, TAprsPort port, long *record_out) {
772 char time_data[MAX_TIME];
773 int do_msg_update = 0;
774 time_t last_ack_sent;
777 int group_message = 0;
780 //fprintf(stderr,"from:%s, to:%s, seq:%s\n", from_call, call_sign, seq);
782 // Set the default output condition. We'll change this later if
784 if (record_out != NULL)
787 // Check for some reasonable string in call_sign parameter
788 if (call_sign == NULL || strlen(call_sign) == 0) {
793 //fprintf(stderr,"msg_data_add():call_sign: %s\n", call_sign);
795 if ( (data != NULL) && (strlen(data) > MAX_MESSAGE_LENGTH) ) {
800 substr(m_fill.call_sign, call_sign, MAX_CALLSIGN);
801 (void)remove_trailing_asterisk(m_fill.call_sign);
803 substr(m_fill.from_call_sign, from_call, MAX_CALLSIGN);
804 (void)remove_trailing_asterisk(m_fill.call_sign);
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);
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.
814 if (seq[0] != '\0') { // Normal station->station messaging or
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");
822 else { // Group message/query/etc.
824 group_message++; // Flag it as a group message
825 //fprintf(stderr,"Group message/query/etc\n");
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);
833 //fprintf(stderr,"Found a duplicate message. Updating fields, seq %s\n",seq);
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");
844 if (type != MESSAGE_BULLETIN) { // Not a bulletin
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");
860 if (type != MESSAGE_BULLETIN) { // Not a bulletin
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");
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");
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
888 m_fill.active=RECORD_ACTIVE;
890 if (m_fill.heard_via_tnc != VIA_TNC)
891 m_fill.heard_via_tnc = (port == APRS_PORT_TTY) ? VIA_TNC : NOT_VIA_TNC;
893 distance = (int)(distance_from_my_station(from_call,temp, sizeof(temp)) + 0.9999);
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);
900 //fprintf(stderr,"Position not known: %s\n",from_call);
903 substr(m_fill.call_sign,call_sign,MAX_CALLSIGN);
904 (void)remove_trailing_asterisk(m_fill.call_sign);
906 substr(m_fill.from_call_sign,from_call,MAX_CALLSIGN);
907 (void)remove_trailing_asterisk(m_fill.from_call_sign);
909 // Update the message field
910 substr(m_fill.message_line,data,MAX_MESSAGE_LENGTH);
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);
916 // Create a timestamp from the current time
917 snprintf(m_fill.packet_time,
918 sizeof(m_fill.packet_time),
920 get_time(time_data));
922 if(record == -1L) { // No old record found
924 m_fill.acked = 1; // Group msgs/queries need no ack
926 m_fill.acked = 0; // We can't have been acked yet
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();
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");
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
945 /* display messages */
947 // if (type == MESSAGE_MESSAGE)
948 // all_messages(from,call_sign,from_call,data);
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) ) {
956 update_messages(1); // Force an update
961 // Return the important variables we'll need
962 if (record_out != NULL)
963 *record_out = record;
965 //fprintf(stderr,"\nrecord_out:%ld record %ld\n",*record_out,record);
966 return(last_ack_sent);
968 } // End of msg_data_add()
974 // alert_data_add: Function which adds NWS weather alerts to the
977 // This function adds alerts directly to the alert hash, bypassing
978 // the message list and associated message-scan functions.
980 void alert_data_add(char *call_sign, char *from_call, char *data,
981 char *seq, char type, TAprsPort port) {
983 char time_data[MAX_TIME];
988 if (log_wx_alert_data && from != DATA_VIA_FILE) {
989 char temp_msg[MAX_MESSAGE_LENGTH+1];
991 // Attempt to reconstruct the original weather alert packet
992 // here, minus the path.
995 "%s>APRS::%-9s:%s{%s",
1000 log_data( get_user_base_dir(LOGFILE_WX_ALERT), temp_msg);
1001 // fprintf(stderr, "%s\n", temp_msg);
1005 if ( (data != NULL) && (strlen(data) > MAX_MESSAGE_LENGTH) ) {
1009 substr(m_fill.call_sign, call_sign, MAX_CALLSIGN);
1010 (void)remove_trailing_asterisk(m_fill.call_sign);
1012 substr(m_fill.from_call_sign, from_call, MAX_CALLSIGN);
1013 (void)remove_trailing_asterisk(m_fill.call_sign);
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);
1019 m_fill.sec_heard = sec_now();
1023 m_fill.active=RECORD_ACTIVE;
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;
1030 substr(m_fill.call_sign,call_sign,MAX_CALLSIGN);
1031 (void)remove_trailing_asterisk(m_fill.call_sign);
1033 substr(m_fill.from_call_sign,from_call,MAX_CALLSIGN);
1034 (void)remove_trailing_asterisk(m_fill.from_call_sign);
1036 // Update the message field
1037 substr(m_fill.message_line,data,MAX_MESSAGE_LENGTH);
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);
1043 // Create a timestamp from the current time
1044 snprintf(m_fill.packet_time,
1045 sizeof(m_fill.packet_time),
1047 get_time(time_data));
1049 // Go try to add it to our alert hash. alert_build_list() will
1050 // check for duplicates before adding it.
1053 // alert_build_list(&m_fill);
1055 // This function fills in the Shapefile filename and index
1056 // so that we can later draw it.
1058 // fill_in_new_alert_entries();
1061 } // End of alert_data_add()
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.
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
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) {
1088 void update_messages(int force) {
1089 static XmTextPosition pos;
1090 char temp1[MAX_CALLSIGN+1];
1098 if ( message_update_time() || force) {
1100 //fprintf(stderr,"update_messages()\n");
1102 //fprintf(stderr,"Um %d\n",(int)sec_now() );
1104 // go through all mw_p's!
1106 // Perform this for each message window
1107 for (mw_p=0; msg_index && mw_p < MAX_MESSAGE_WINDOWS; mw_p++) {
1110 begin_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
1112 if (mw[mw_p].send_message_dialog!=NULL) {
1114 //fprintf(stderr,"\n");
1116 //fprintf(stderr,"found send_message_dialog\n");
1118 // Clear the text from message window
1119 XmTextReplace(mw[mw_p].send_message_text,
1121 XmTextGetLastPosition(mw[mw_p].send_message_text),
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);
1134 if (new_message_data<0)
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
1140 typedef struct _index_record {
1143 struct _index_record *next;
1145 index_record *head = NULL;
1146 index_record *p_prev = NULL;
1147 index_record *p_next = NULL;
1149 // Allocate the first record (a dummy record)
1150 head = (index_record *)malloc(sizeof(index_record));
1154 head->sec_heard = (time_t)0;
1157 (void)remove_trailing_spaces(temp1);
1158 (void)to_upper(temp1);
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 ) ) {
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
1181 p_next = p_prev->next;
1182 while (!done && (p_next != NULL)) { // Loop until end of list or record inserted
1184 //fprintf(stderr,"Looping, looking for insertion spot\n");
1186 if (p_next->sec_heard <= msg_data[msg_index[i]].sec_heard) {
1187 // Advance one record
1189 p_next = p_prev->next;
1191 else { // We found the correct insertion spot
1196 //fprintf(stderr,"Inserting\n");
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
1202 p_prev->next = (index_record *)malloc(sizeof(index_record));
1203 CHECKMALLOC(p_prev->next);
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!
1212 // Done processing the entire list for this
1215 //fprintf(stderr,"Done inserting/looping\n");
1217 if (head->next != NULL) { // We have messages to display
1220 //fprintf(stderr,"We have messages to display\n");
1222 // Run through the linked list and dump the
1223 // info out. It's now in time-sorted order.
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
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
1234 char interval_str[50];
1235 int offset = 22; // Offset for highlighting
1238 //fprintf(stderr,"\nLooping through, reading messages\n");
1240 //fprintf(stderr,"acked: %d\n",msg_data[msg_index[j]].acked);
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]
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.
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) {
1267 langcode("WPUPMSB016") ); // "*TIMEOUT*"
1269 else if (msg_data[msg_index[j]].acked == 3) {
1273 langcode("WPUPMSB017") ); // "*CANCELLED*"
1275 else if (msg_data[msg_index[j]].acked == 4) {
1279 langcode("WPUPMSB018") ); // "*REJECTED*"
1281 else prefix[0] = '\0';
1283 if (msg_data[msg_index[j]].interval) {
1284 snprintf(interval_str,
1285 sizeof(interval_str),
1287 msg_data[msg_index[j]].tries + 1,
1288 (long)msg_data[msg_index[j]].interval);
1290 // Don't highlight the interval
1292 offset = offset + strlen(interval_str);
1295 interval_str[0] = '\0';
1298 snprintf(temp2, sizeof(temp2),
1300 // Debug code. Trying to find sorting error
1301 //"%ld %s %-9s>%s\n",
1302 //msg_data[msg_index[j]].sec_heard,
1304 msg_data[msg_index[j]].from_call_sign,
1307 msg_data[msg_index[j]].message_line);
1309 //fprintf(stderr,"message: %s\n", msg_data[msg_index[j]].message_line);
1310 //fprintf(stderr,"update_messages: %s|%s", temp1, temp2);
1312 // Replace the text from pos to pos+strlen(temp2) by the string "temp2"
1313 if (mw[mw_p].send_message_text != NULL) {
1315 // Insert the text at the end
1316 // XmTextReplace(mw[mw_p].send_message_text,
1318 // pos+strlen(temp2),
1321 XmTextInsert(mw[mw_p].send_message_text,
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,
1335 //XmHIGHLIGHT_SECONDARY_SELECTED); // Underlining
1336 XmHIGHLIGHT_SELECTED); // Reverse Video
1338 else { // Message was acked, get rid of highlighting
1339 //fprintf(stderr,"Setting normal\t");
1340 XmTextSetHighlight(mw[mw_p].send_message_text,
1343 XmHIGHLIGHT_NORMAL);
1346 //fprintf(stderr,"Text: %s\n",temp2);
1348 pos += strlen(temp2);
1352 // Advance to the next record in the list
1355 p_next = p_prev->next;
1359 else { // No messages matched, list is empty
1362 // What does this do? Move all of the text?
1364 // if (mw[mw_p].send_message_text != NULL) {
1365 // XmTextReplace(mw[mw_p].send_message_text,
1367 // XmTextGetLastPosition(mw[mw_p].send_message_text),
1372 //fprintf(stderr,"Free'ing list\n");
1374 // De-allocate the linked list
1376 while (p_prev != NULL) {
1378 //fprintf(stderr,"You're free!\n");
1380 p_next = p_prev->next;
1385 // Show the last added message in the window
1386 XmTextShowPosition(mw[mw_p].send_message_text,
1392 end_critical_section(&send_message_dialog_lock, "db.c:update_messages" );
1395 last_message_update = sec_now();
1397 //fprintf(stderr,"Message index end: %ld\n",msg_index_end);
1406 void mdelete_messages_from(char *from) {
1409 // Mark message records with RECORD_NOTACTIVE. This will mark
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;
1421 void mdelete_messages_to(char *to) {
1424 // Mark message records with RECORD_NOTACTIVE. This will mark
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;
1435 void mdelete_messages(char *to_from) {
1438 // Mark message records with RECORD_NOTACTIVE. This will mark
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;
1449 void mdata_delete_type(const char msg_type, const time_t reference_time) {
1452 // Mark message records with RECORD_NOTACTIVE. This will mark
1454 for (i = 0; msg_index && i < msg_index_end; i++)
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)
1460 msg_data[i].active = RECORD_NOTACTIVE;
1467 void check_message_remove(time_t curr_sec) { // called in timing loop
1469 // Time to check for old messages again? (Currently every ten
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) ) {
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.
1481 mdata_delete_type('\0', curr_sec-DEBUG_MESSAGE_REMOVE);
1482 #else // EXPIRE_DEBUG
1483 mdata_delete_type('\0', curr_sec-_aprs_sec_remove);
1486 last_message_remove = curr_sec;
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.
1498 void mscan_file(char msg_type, void (*function)(Message *)) {
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]]);
1511 void mprint_record(Message *m_fill) {
1514 "%-9s>%-9s %s:%5s %s:%c :%s\n",
1515 m_fill->from_call_sign,
1517 langcode("WPUPMSB013"), // "seq"
1519 langcode("WPUPMSB014"), // "type"
1521 m_fill->message_line);
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);
1539 int decode_message(gchar *call,gchar *path,gchar *message,gint port,gint third_party)
1542 char ipacket_message[300];
1543 char message_plus_acks[MAX_MESSAGE_LENGTH + 10];
1544 char from_call[MAX_CALLSIGN+1];
1550 char orig_msg_id[5+1];
1555 int to_my_base_call = 0;
1556 int from_my_call = 0;
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
1569 // 01234567890123456
1570 // 01234567890123456 old
1571 // we get message with already extracted data ID
1574 if (is_my_call(call, 1) ) { // Check SSID also
1578 ack_string[0] = '\0'; // Clear out the Reply/Ack result string
1580 len = (int)strlen(message);
1581 ok = (int)(len > 9 && message[9] == ':');
1585 //fprintf(stderr,"DEBUG: decode_message: from %s: %s\n", call, message);
1588 substr(addr9,message,9); // extract addressee
1593 (void)remove_trailing_spaces(addr);
1595 if (is_my_call(addr,1)) { // Check includes SSID
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
1605 message = message + 10; // pointer to message text
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),
1614 temp_ptr = strrchr(message,'{'); // look for message ID after
1615 //*last* { in message.
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)
1622 // Save the original msg_id away.
1623 snprintf(orig_msg_id,
1624 sizeof(orig_msg_id),
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.
1637 temp_ptr = strstr(msg_id,"}"); // look for Reply Ack in msg_id
1639 if (temp_ptr != NULL) { // Found Reply/Ack protocol!
1643 // Put this code into the UI message area as well (if applicable).
1645 // Separate out the extra ack so that we can deal with
1647 snprintf(ack_string,
1650 temp_ptr+1); // After the '}' character!
1652 // Terminate it here so that rest of decode works
1653 // properly. We can get duplicate messages
1656 // Note that we modify msg_id here. Use orig_msg_id if we need the
1657 // unmodified version (full REPLY-ACK version) later.
1659 temp_ptr[0] = '\0'; // adjust msg_id end
1662 else { // Look for Reply Ack in message without sequence
1664 temp_ptr = strstr(message,"}");
1666 if (temp_ptr != NULL) {
1671 // Put this code into the UI message area as well (if applicable).
1672 snprintf(ack_string,
1675 temp_ptr+1); // After the '}' character!
1677 ack_string[yy] = '\0'; // Terminate the string
1679 // Terminate it here so that rest of decode works
1680 // properly. We can get duplicate messages
1682 temp_ptr[0] = '\0'; // adjust message end
1689 done = 1; // fall through...
1692 len = (int)strlen(message);
1693 //--------------------------------------------------------------------------
1694 if (!done && len > 3 && strncmp(message,"ack",3) == 0) { // ACK
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
1704 // ack23{ REPLY-ACK Protocol
1705 // ack2Q}3d REPLY-ACK Protocol
1707 substr(msg_id,message+3,5);
1708 // fprintf(stderr,"ACK: %s: |%s| |%s|\n",call,addr,msg_id);
1710 if (to_my_call) { // Check SSID also
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
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
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
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];
1733 shorten_path(path,short_path,sizeof(short_path));
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",
1750 output_igate_rf(call,
1764 //--------------------------------------------------------------------------
1765 if (!done && len > 3 && strncmp(message,"rej",3) == 0) { // REJ
1767 substr(msg_id,message+3,5);
1769 if (to_my_call) { // Check SSID also
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
1776 // This one also handles REPLY-ACK protocol just fine.
1777 msg_record_rej(call,addr,msg_id); // Record the REJ for this message
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 */
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];
1793 shorten_path(path,short_path,sizeof(short_path));
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",
1810 output_igate_rf(call,
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);
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;
1840 // Remember to put this code into the UI message area as well (if
1843 // Check for Reply/Ack
1844 if (reply_ack && strlen(ack_string) != 0) { // Have a free-ride ack to deal with
1846 clear_acked_message(call,addr,ack_string); // got an ACK for me
1848 msg_record_ack(call,addr,ack_string,0,0); // Record the ack for this message
1851 // Save the ack 'cuz we might need it while talking to this
1852 // station. We need it to implement Reply/Ack protocol.
1854 // Note that msg_id has already been truncated by this point.
1855 // orig_msg_id contains the full REPLY-ACK text.
1857 //fprintf(stderr, "store_most_recent_ack()\n");
1858 store_most_recent_ack(call,msg_id);
1860 // fprintf(stderr,"found Msg w line to me: |%s| |%s|\n",message,msg_id);
1861 last_ack_sent = msg_data_add(addr,
1867 &record); // id_fixed
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.
1874 if (last_ack_sent == (time_t)0l && record == -1l) { // Msg we've never received before
1876 new_message_data += 1;
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.
1883 (void)check_popup_window(call, 2); // Calls update_messages()
1885 //update_messages(1); // Force an update
1890 // Try to only send an ack out once per 30 seconds at the
1893 // Does this 30-second check work?
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
1900 char path[MAX_LINE_SIZE+1];
1902 // Update the last_ack_sent field for the message
1903 msg_update_ack_stamp(record);
1905 pad_callsign(from_call,call); /* ack the message */
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);
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);
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.
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.
1938 // Send out the immediate ACK
1939 if (path[0] == '\0')
1940 transmit_message_data(call,ack,NULL);
1942 transmit_message_data(call,ack,path);
1945 if (record != -1l) { // Msg we've received before
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.
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);
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);
1966 if (auto_reply == 1) {
1968 snprintf(ipacket_message,
1969 sizeof(ipacket_message), "AA:%s", auto_reply_message);
1971 if (!from_my_call) // Check SSID also
1972 output_message(my_callsign, call, ipacket_message, "");
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
1989 // :WE7U-13 :Not at keyboard.
1992 time_t last_ack_sent;
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);
2004 // fprintf(stderr,"found Msg w line to me: |%s| |%s|\n",message,msg_id);
2005 last_ack_sent = msg_data_add(addr,
2011 &record); // id_fixed
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.
2018 if (last_ack_sent == (time_t)0l && record == -1l) { // Msg we've never received before
2020 new_message_data += 1;
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.
2027 //fprintf(stderr,"***check_popup_window 1\n");
2028 (void)check_popup_window(call, 2); // Calls update_messages()
2030 //update_messages(1); // Force an update
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);
2042 //--------------------------------------------------------------------------
2044 && ( (strncmp(addr,"NWS-",4) == 0) // NWS weather alert
2045 || (strncmp(addr,"NWS_",4) == 0) ) ) { // NWS weather alert compressed
2047 // could have sort of line number
2048 //fprintf(stderr,"found NWS: |%s| |%s| |%s|\n",addr,message,msg_id);
2050 (void)alert_data_add(addr,
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];
2067 shorten_path(path,short_path,sizeof(short_path));
2069 snprintf(ipacket_message,
2070 sizeof(ipacket_message),
2071 "}%s>%s,TCPIP,%s*::%s:%s",
2078 output_nws_igate_rf(call,
2087 //--------------------------------------------------------------------------
2088 if (!done && strncmp(addr,"SKY",3) == 0) { // NWS weather alert additional info
2090 // could have sort of line number
2091 //fprintf(stderr,"found SKY: |%s| |%s| |%s|\n",addr,message,msg_id);
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!
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];
2108 shorten_path(path,short_path,sizeof(short_path));
2110 snprintf(ipacket_message,
2111 sizeof(ipacket_message),
2112 "}%s>%s,TCPIP,%s*::%s:%s",
2119 output_nws_igate_rf(call,
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
2134 time_t last_ack_sent;
2135 char message_plus_note[MAX_MESSAGE_LENGTH + 30];
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.
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?
2151 snprintf(message_plus_note,
2152 sizeof(message_plus_note),
2156 last_ack_sent = msg_data_add(_aprs_mycall,
2164 else { // Normal case, messaging between other people
2165 last_ack_sent = msg_data_add(addr,
2174 new_message_data += look_for_open_group_data(addr);
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.
2181 if (last_ack_sent == (time_t)0l && record_out == -1l) { // Msg we've never received before
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.
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
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 */
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];
2208 shorten_path(path,short_path,sizeof(short_path));
2209 snprintf(ipacket_message,
2210 sizeof(ipacket_message),
2211 "}%s>%s,TCPIP,%s*::%s:%s",
2219 output_igate_rf(call,
2233 //--------------------------------------------------------------------------
2235 if (!done) { // message without line number
2237 time_t last_ack_sent;
2240 last_ack_sent = msg_data_add(addr,
2248 new_message_data++; // ??????
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.
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
2261 // Could be response to a query. Popup a messsage.
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
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.
2280 // popup_message(langcode("POPEM00018"),message);
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
2286 clear_acked_message(call,addr,ack_string); // got an ACK for me
2288 msg_record_ack(call,addr,ack_string,0,0); // Record the ack for this message
2295 //--------------------------------------------------------------------------
2298 (void)data_add(STATION_CALL_DATA,
2305 0, // Not a packet from my station
2306 0); // Not my object/item
2312 #endif //INCLUDE_APRS