comparison libpurple/upnp.c @ 15440:56a2a0bb290a

Fix a crash when a network_listen_range process is canceled before its UPnP port mapping completes, which occurs regularly on certain routers when file transfers are initiated and then quickly finished. Much thanks to Elliott Harris and Eric Richie for their hard work with me hunting this down and fixing it.
author Evan Schoenberg <evan.s@dreskin.net>
date Sun, 28 Jan 2007 15:05:23 +0000
parents 5fe8042783c1
children 58ff9a0ffce0
comparison
equal deleted inserted replaced
15438:a415922e2882 15440:56a2a0bb290a
129 gchar service_type[25]; 129 gchar service_type[25];
130 int retry_count; 130 int retry_count;
131 gchar *full_url; 131 gchar *full_url;
132 } UPnPDiscoveryData; 132 } UPnPDiscoveryData;
133 133
134 typedef struct { 134 struct _UPnPMappingAddRemove
135 {
135 unsigned short portmap; 136 unsigned short portmap;
136 gchar protocol[4]; 137 gchar protocol[4];
137 gboolean add; 138 gboolean add;
138 GaimUPnPCallback cb; 139 GaimUPnPCallback cb;
139 gpointer cb_data; 140 gpointer cb_data;
140 } UPnPMappingAddRemove; 141 guint tima; /* gaim_timeout_add handle */
142 GaimUtilFetchUrlData *gfud;
143 };
141 144
142 static GaimUPnPControlInfo control_info = { 145 static GaimUPnPControlInfo control_info = {
143 GAIM_UPNP_STATUS_UNDISCOVERED, 146 GAIM_UPNP_STATUS_UNDISCOVERED,
144 NULL, "\0", "\0", "\0", 0}; 147 NULL, "\0", "\0", "\0", 0};
145 148
664 control_info.status = GAIM_UPNP_STATUS_DISCOVERING; 667 control_info.status = GAIM_UPNP_STATUS_DISCOVERING;
665 668
666 gaim_upnp_discover_send_broadcast(dd); 669 gaim_upnp_discover_send_broadcast(dd);
667 } 670 }
668 671
669 static void 672 static GaimUtilFetchUrlData*
670 gaim_upnp_generate_action_message_and_send(const gchar* actionName, 673 gaim_upnp_generate_action_message_and_send(const gchar* actionName,
671 const gchar* actionParams, GaimUtilFetchUrlCallback cb, 674 const gchar* actionParams, GaimUtilFetchUrlCallback cb,
672 gpointer cb_data) 675 gpointer cb_data)
673 { 676 {
674 677 GaimUtilFetchUrlData* gfud;
675 gchar* soapMessage; 678 gchar* soapMessage;
676 gchar* totalSendMessage; 679 gchar* totalSendMessage;
677 gchar* pathOfControl; 680 gchar* pathOfControl;
678 gchar* addressOfControl; 681 gchar* addressOfControl;
679 int port = 0; 682 int port = 0;
701 control_info.service_type, actionName, 704 control_info.service_type, actionName,
702 strlen(soapMessage), soapMessage); 705 strlen(soapMessage), soapMessage);
703 g_free(pathOfControl); 706 g_free(pathOfControl);
704 g_free(soapMessage); 707 g_free(soapMessage);
705 708
706 gaim_util_fetch_url_request(control_info.control_url, FALSE, NULL, TRUE, 709 gfud = gaim_util_fetch_url_request(control_info.control_url, FALSE, NULL, TRUE,
707 totalSendMessage, TRUE, cb, cb_data); 710 totalSendMessage, TRUE, cb, cb_data);
708 711
709 g_free(totalSendMessage); 712 g_free(totalSendMessage);
710 g_free(addressOfControl); 713 g_free(addressOfControl);
714
715 return gfud;
711 } 716 }
712 717
713 const gchar * 718 const gchar *
714 gaim_upnp_get_public_ip() 719 gaim_upnp_get_public_ip()
715 { 720 {
882 action_params = g_strdup_printf( 887 action_params = g_strdup_printf(
883 DELETE_PORT_MAPPING_PARAMS, 888 DELETE_PORT_MAPPING_PARAMS,
884 ar->portmap, ar->protocol); 889 ar->portmap, ar->protocol);
885 } 890 }
886 891
887 gaim_upnp_generate_action_message_and_send(action_name, 892 ar->gfud = gaim_upnp_generate_action_message_and_send(action_name,
888 action_params, done_port_mapping_cb, ar); 893 action_params, done_port_mapping_cb, ar);
889 894
890 g_free(action_params); 895 g_free(action_params);
891 return; 896 return;
892 } 897 }
893 898
902 { 907 {
903 do_port_mapping_cb(FALSE, data); 908 do_port_mapping_cb(FALSE, data);
904 return FALSE; 909 return FALSE;
905 } 910 }
906 911
907 void 912 void gaim_upnp_cancel_port_mapping(UPnPMappingAddRemove *ar)
913 {
914 GSList *l;
915
916 /* Remove ar from discovery_callbacks if present; it was inserted after a cb.
917 * The same cb may be in the list multple times, so be careful to remove the one assocaited with ar. */
918 l = discovery_callbacks;
919 while (l)
920 {
921 if (l->next && (l->next->data == ar)) {
922 discovery_callbacks = g_slist_delete_link(discovery_callbacks, l->next);
923 discovery_callbacks = g_slist_delete_link(discovery_callbacks, l);
924 }
925
926 l = l->next;
927 }
928
929 if (ar->tima > 0)
930 gaim_timeout_remove(ar->tima);
931
932 if (ar->gfud)
933 gaim_util_fetch_url_cancel(ar->gfud);
934
935 g_free(ar);
936 }
937
938 UPnPMappingAddRemove *
908 gaim_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol, 939 gaim_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
909 GaimUPnPCallback cb, gpointer cb_data) 940 GaimUPnPCallback cb, gpointer cb_data)
910 { 941 {
911 UPnPMappingAddRemove *ar; 942 UPnPMappingAddRemove *ar;
912 943
923 * the internal IP lookup won't be complete */ 954 * the internal IP lookup won't be complete */
924 discovery_callbacks = g_slist_append( 955 discovery_callbacks = g_slist_append(
925 discovery_callbacks, do_port_mapping_cb); 956 discovery_callbacks, do_port_mapping_cb);
926 discovery_callbacks = g_slist_append( 957 discovery_callbacks = g_slist_append(
927 discovery_callbacks, ar); 958 discovery_callbacks, ar);
928 return; 959 return ar;
929 } 960 }
930 961
931 /* If we haven't had a successful UPnP discovery, check if 5 minutes has 962 /* If we haven't had a successful UPnP discovery, check if 5 minutes has
932 * elapsed since the last try, try again */ 963 * elapsed since the last try, try again */
933 if(control_info.status == GAIM_UPNP_STATUS_UNDISCOVERED || 964 if(control_info.status == GAIM_UPNP_STATUS_UNDISCOVERED ||
934 (control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER 965 (control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER
935 && (time(NULL) - control_info.lookup_time) > 300)) { 966 && (time(NULL) - control_info.lookup_time) > 300)) {
936 gaim_upnp_discover(do_port_mapping_cb, ar); 967 gaim_upnp_discover(do_port_mapping_cb, ar);
937 return; 968 return ar;
938 } else if(control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER) { 969 } else if(control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER) {
939 if (cb) { 970 if (cb) {
940 /* Asynchronously trigger a failed response */ 971 /* Asynchronously trigger a failed response */
941 gaim_timeout_add(10, fire_port_mapping_failure_cb, ar); 972 ar->tima = gaim_timeout_add(10, fire_port_mapping_failure_cb, ar);
942 } else { 973 } else {
943 /* No need to do anything if nobody expects a response*/ 974 /* No need to do anything if nobody expects a response*/
944 g_free(ar); 975 g_free(ar);
976 ar = NULL;
945 } 977 }
946 return; 978 return ar;
947 } 979 }
948 980
949 do_port_mapping_cb(TRUE, ar); 981 do_port_mapping_cb(TRUE, ar);
950 } 982 return ar;
951 983 }
952 void 984
985 UPnPMappingAddRemove *
953 gaim_upnp_remove_port_mapping(unsigned short portmap, const char* protocol, 986 gaim_upnp_remove_port_mapping(unsigned short portmap, const char* protocol,
954 GaimUPnPCallback cb, gpointer cb_data) 987 GaimUPnPCallback cb, gpointer cb_data)
955 { 988 {
956 UPnPMappingAddRemove *ar; 989 UPnPMappingAddRemove *ar;
957 990
966 if(control_info.status == GAIM_UPNP_STATUS_DISCOVERING) { 999 if(control_info.status == GAIM_UPNP_STATUS_DISCOVERING) {
967 discovery_callbacks = g_slist_append( 1000 discovery_callbacks = g_slist_append(
968 discovery_callbacks, do_port_mapping_cb); 1001 discovery_callbacks, do_port_mapping_cb);
969 discovery_callbacks = g_slist_append( 1002 discovery_callbacks = g_slist_append(
970 discovery_callbacks, ar); 1003 discovery_callbacks, ar);
971 return; 1004 return ar;
972 } 1005 }
973 1006
974 /* If we haven't had a successful UPnP discovery, check if 5 minutes has 1007 /* If we haven't had a successful UPnP discovery, check if 5 minutes has
975 * elapsed since the last try, try again */ 1008 * elapsed since the last try, try again */
976 if(control_info.status == GAIM_UPNP_STATUS_UNDISCOVERED || 1009 if(control_info.status == GAIM_UPNP_STATUS_UNDISCOVERED ||
977 (control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER 1010 (control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER
978 && (time(NULL) - control_info.lookup_time) > 300)) { 1011 && (time(NULL) - control_info.lookup_time) > 300)) {
979 gaim_upnp_discover(do_port_mapping_cb, ar); 1012 gaim_upnp_discover(do_port_mapping_cb, ar);
980 return; 1013 return ar;
981 } else if(control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER) { 1014 } else if(control_info.status == GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER) {
982 if (cb) { 1015 if (cb) {
983 /* Asynchronously trigger a failed response */ 1016 /* Asynchronously trigger a failed response */
984 gaim_timeout_add(10, fire_port_mapping_failure_cb, ar); 1017 ar->tima = gaim_timeout_add(10, fire_port_mapping_failure_cb, ar);
985 } else { 1018 } else {
986 /* No need to do anything if nobody expects a response*/ 1019 /* No need to do anything if nobody expects a response*/
987 g_free(ar); 1020 g_free(ar);
1021 ar = NULL;
988 } 1022 }
989 return; 1023 return ar;
990 } 1024 }
991 1025
992 do_port_mapping_cb(TRUE, ar); 1026 do_port_mapping_cb(TRUE, ar);
993 } 1027 return ar;
1028 }