comparison src/proxy.c @ 14089:10e8eb6a4910

[gaim-migrate @ 16712] Pretty large commit here. Basically I got sick of having to verify that gc is still valid on all the callback functions for gaim_proxy_connect(). The fix for this for gaim_proxy_connect() to return something that allows the connection attempt to be canceled. It's not quite there yet, but this is a good first step. I changed gaim_proxy_connect() to return a reference to a new GaimProxyConnectInfo (this used to be called PHB). Eventually this can be passed to a function that'll cancel the connection attempt. I also decided to add an error_cb instead of using connect_cb and passing a file descriptor of -1. And proxy.c will also pass an error message to callers which should explain the reason that the connection attempt failed. Oh, and proxy.c now never calls gaim_connection_error() committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sat, 12 Aug 2006 10:12:43 +0000
parents 8bda65b88e49
children 983fbec46eb0
comparison
equal deleted inserted replaced
14088:223570831b0b 14089:10e8eb6a4910
36 #include "ntlm.h" 36 #include "ntlm.h"
37 #include "prefs.h" 37 #include "prefs.h"
38 #include "proxy.h" 38 #include "proxy.h"
39 #include "util.h" 39 #include "util.h"
40 40
41 static GaimProxyInfo *global_proxy_info = NULL; 41 /* Does anyone know what PHB stands for? */
42
43 struct PHB { 42 struct PHB {
44 GaimInputFunction func; 43 GaimProxyConnectFunction connect_cb;
44 GaimProxyErrorFunction error_cb;
45 gpointer data; 45 gpointer data;
46 char *host; 46 char *host;
47 int port; 47 int port;
48 gint inpa; 48 guint inpa;
49 GaimProxyInfo *gpi; 49 GaimProxyInfo *gpi;
50 GaimAccount *account;
51 GSList *hosts; 50 GSList *hosts;
52 guchar *write_buffer; 51 guchar *write_buffer;
53 gsize write_buf_len; 52 gsize write_buf_len;
54 gsize written_len; 53 gsize written_len;
55 GaimInputFunction read_cb; 54 GaimInputFunction read_cb;
56 guchar *read_buffer; 55 guchar *read_buffer;
57 gsize read_buf_len; 56 gsize read_buf_len;
58 gsize read_len; 57 gsize read_len;
59 }; 58 };
60
61 static void try_connect(struct PHB *);
62 59
63 static const char *socks5errors[] = { 60 static const char *socks5errors[] = {
64 "succeeded\n", 61 "succeeded\n",
65 "general SOCKS server failure\n", 62 "general SOCKS server failure\n",
66 "connection not allowed by ruleset\n", 63 "connection not allowed by ruleset\n",
70 "TTL expired\n", 67 "TTL expired\n",
71 "Command not supported\n", 68 "Command not supported\n",
72 "Address type not supported\n" 69 "Address type not supported\n"
73 }; 70 };
74 71
72 static GaimProxyInfo *global_proxy_info = NULL;
73 static GSList *phbs = NULL;
74
75 static void try_connect(struct PHB *);
76
75 /************************************************************************** 77 /**************************************************************************
76 * Proxy structure API 78 * Proxy structure API
77 **************************************************************************/ 79 **************************************************************************/
78 GaimProxyInfo * 80 GaimProxyInfo *
79 gaim_proxy_info_new(void) 81 gaim_proxy_info_new(void)
253 } 255 }
254 /************************************************************************** 256 /**************************************************************************
255 * Proxy API 257 * Proxy API
256 **************************************************************************/ 258 **************************************************************************/
257 259
260 static void
261 gaim_proxy_phb_destroy(struct PHB *phb)
262 {
263 phbs = g_slist_remove(phbs, phb);
264
265 if (phb->inpa > 0)
266 gaim_input_remove(phb->inpa);
267
268 while (phb->hosts != NULL)
269 {
270 /* Discard the length... */
271 phb->hosts = g_slist_remove(phb->hosts, phb->hosts->data);
272 /* Free the address... */
273 g_free(phb->hosts->data);
274 phb->hosts = g_slist_remove(phb->hosts, phb->hosts->data);
275 }
276
277 g_free(phb->host);
278 g_free(phb->write_buffer);
279 g_free(phb->read_buffer);
280 g_free(phb);
281 }
282
283 static void
284 gaim_proxy_phb_connected(struct PHB *phb, int fd)
285 {
286 phb->connect_cb(phb->data, fd);
287 gaim_proxy_phb_destroy(phb);
288 }
289
290 /**
291 * @param error An error message explaining why the connection
292 * failed. This will be passed to the callback function
293 * specified in the call to gaim_proxy_connect().
294 */
295 static void
296 gaim_proxy_phb_error(struct PHB *phb, const gchar *error_message)
297 {
298 if (phb->error_cb == NULL)
299 {
300 /*
301 * TODO
302 * While we're transitioning to the new gaim_proxy_connect()
303 * code, not all callers supply an error_cb. If this is the
304 * case then they're expecting connect_cb to be called with
305 * an fd of -1 in the case of an error. Once all callers have
306 * been changed this whole if statement should be removed.
307 */
308 phb->connect_cb(phb->data, -1);
309 gaim_proxy_phb_destroy(phb);
310 return;
311 }
312
313 phb->error_cb(phb->data, error_message);
314 gaim_proxy_phb_destroy(phb);
315 }
316
258 #if defined(__unix__) || defined(__APPLE__) 317 #if defined(__unix__) || defined(__APPLE__)
259 318
260 /* 319 /*
261 * This structure represents both a pending DNS request and 320 * This structure represents both a pending DNS request and
262 * a free child process. 321 * a free child process.
263 */ 322 */
264 typedef struct { 323 typedef struct {
265 char *host; 324 char *host;
266 int port; 325 int port;
267 dns_callback_t callback; 326 GaimProxyDnsConnectFunction callback;
268 gpointer data; 327 gpointer data;
269 gint inpa; 328 guint inpa;
270 int fd_in, fd_out; 329 int fd_in, fd_out;
271 pid_t dns_pid; 330 pid_t dns_pid;
272 } pending_dns_request_t; 331 } pending_dns_request_t;
273 332
274 static GSList *free_dns_children = NULL; 333 static GSList *free_dns_children = NULL;
283 int port; 342 int port;
284 } dns_params_t; 343 } dns_params_t;
285 344
286 typedef struct { 345 typedef struct {
287 dns_params_t params; 346 dns_params_t params;
288 dns_callback_t callback; 347 GaimProxyDnsConnectFunction callback;
289 gpointer data; 348 gpointer data;
290 } queued_dns_request_t; 349 } queued_dns_request_t;
291 350
292 /* 351 /*
293 * Begin the DNS resolver child process functions. 352 * Begin the DNS resolver child process functions.
692 /* 751 /*
693 * End the functions for dealing with the DNS child processes. 752 * End the functions for dealing with the DNS child processes.
694 */ 753 */
695 754
696 int 755 int
697 gaim_gethostbyname_async(const char *hostname, int port, dns_callback_t callback, gpointer data) 756 gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data)
698 { 757 {
699 pending_dns_request_t *req = NULL; 758 pending_dns_request_t *req = NULL;
700 dns_params_t dns_params; 759 dns_params_t dns_params;
701 gchar *host_temp; 760 gchar *host_temp;
702 gboolean show_debug; 761 gboolean show_debug;
765 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ 824 #elif defined _WIN32 /* end __unix__ || __APPLE__ */
766 825
767 typedef struct _dns_tdata { 826 typedef struct _dns_tdata {
768 char *hostname; 827 char *hostname;
769 int port; 828 int port;
770 dns_callback_t callback; 829 GaimProxyDnsConnectFunction callback;
771 gpointer data; 830 gpointer data;
772 GSList *hosts; 831 GSList *hosts;
773 char *errmsg; 832 char *errmsg;
774 } dns_tdata; 833 } dns_tdata;
775 834
841 return 0; 900 return 0;
842 } 901 }
843 902
844 int 903 int
845 gaim_gethostbyname_async(const char *hostname, int port, 904 gaim_gethostbyname_async(const char *hostname, int port,
846 dns_callback_t callback, gpointer data) 905 GaimProxyDnsConnectFunction callback, gpointer data)
847 { 906 {
848 dns_tdata *td; 907 dns_tdata *td;
849 struct sockaddr_in sin; 908 struct sockaddr_in sin;
850 GError* err = NULL; 909 GError* err = NULL;
851 910
880 939
881 typedef struct { 940 typedef struct {
882 gpointer data; 941 gpointer data;
883 size_t addrlen; 942 size_t addrlen;
884 struct sockaddr *addr; 943 struct sockaddr *addr;
885 dns_callback_t callback; 944 GaimProxyDnsConnectFunction callback;
886 } pending_dns_request_t; 945 } pending_dns_request_t;
887 946
888 static gboolean host_resolved(gpointer data) 947 static gboolean host_resolved(gpointer data)
889 { 948 {
890 pending_dns_request_t *req = (pending_dns_request_t*)data; 949 pending_dns_request_t *req = (pending_dns_request_t*)data;
896 return FALSE; 955 return FALSE;
897 } 956 }
898 957
899 int 958 int
900 gaim_gethostbyname_async(const char *hostname, int port, 959 gaim_gethostbyname_async(const char *hostname, int port,
901 dns_callback_t callback, gpointer data) 960 GaimProxyDnsConnectFunction callback, gpointer data)
902 { 961 {
903 struct sockaddr_in sin; 962 struct sockaddr_in sin;
904 pending_dns_request_t *req; 963 pending_dns_request_t *req;
905 964
906 if (!inet_aton(hostname, &sin.sin_addr)) { 965 if (!inet_aton(hostname, &sin.sin_addr)) {
953 */ 1012 */
954 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); 1013 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
955 if (ret == 0 && error == EINPROGRESS) 1014 if (ret == 0 && error == EINPROGRESS)
956 return; /* we'll be called again later */ 1015 return; /* we'll be called again later */
957 if (ret < 0 || error != 0) { 1016 if (ret < 0 || error != 0) {
958 if(ret!=0) error = errno; 1017 if (ret!=0)
1018 error = errno;
959 close(source); 1019 close(source);
960 gaim_input_remove(phb->inpa); 1020 gaim_input_remove(phb->inpa);
1021 phb->inpa = 0;
961 1022
962 gaim_debug_error("proxy", 1023 gaim_debug_error("proxy",
963 "getsockopt SO_ERROR check: %s\n", strerror(error)); 1024 "getsockopt SO_ERROR check: %s\n", strerror(error));
964 1025
965 try_connect(phb); 1026 try_connect(phb);
966 return; 1027 return;
967 } 1028 }
968 1029
969 gaim_input_remove(phb->inpa); 1030 gaim_input_remove(phb->inpa);
970 1031 phb->inpa = 0;
971 if (phb->account == NULL || 1032
972 gaim_account_get_connection(phb->account) != NULL) { 1033 gaim_proxy_phb_connected(phb, source);
973
974 phb->func(phb->data, source, GAIM_INPUT_READ);
975 }
976
977 g_free(phb->host);
978 g_free(phb);
979 } 1034 }
980 1035
981 static gboolean clean_connect(gpointer data) 1036 static gboolean clean_connect(gpointer data)
982 { 1037 {
983 struct PHB *phb = data; 1038 struct PHB *phb = data;
984 1039
985 if (phb->account == NULL || 1040 gaim_proxy_phb_connected(phb, phb->port);
986 gaim_account_get_connection(phb->account) != NULL) {
987
988 phb->func(phb->data, phb->port, GAIM_INPUT_READ);
989 }
990
991 g_free(phb->host);
992 g_free(phb);
993 1041
994 return FALSE; 1042 return FALSE;
995 } 1043 }
996 1044
997 1045
1035 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1083 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1036 gaim_debug_error("proxy", "getsockopt failed.\n"); 1084 gaim_debug_error("proxy", "getsockopt failed.\n");
1037 close(fd); 1085 close(fd);
1038 return -1; 1086 return -1;
1039 } 1087 }
1088 /* TODO: Why is the following line so strange? */
1040 phb->port = fd; /* bleh */ 1089 phb->port = fd; /* bleh */
1041 gaim_timeout_add(50, clean_connect, phb); /* we do this because we never 1090 gaim_timeout_add(10, clean_connect, phb); /* we do this because we never
1042 want to call our callback 1091 want to call our callback
1043 before we return. */ 1092 before we return. */
1044 } 1093 }
1045 1094
1046 return fd; 1095 return fd;
1057 1106
1058 if(ret < 0 && errno == EAGAIN) 1107 if(ret < 0 && errno == EAGAIN)
1059 return; 1108 return;
1060 else if(ret < 0) { 1109 else if(ret < 0) {
1061 gaim_input_remove(phb->inpa); 1110 gaim_input_remove(phb->inpa);
1111 phb->inpa = 0;
1062 close(source); 1112 close(source);
1063 g_free(phb->write_buffer); 1113 g_free(phb->write_buffer);
1064 phb->write_buffer = NULL; 1114 phb->write_buffer = NULL;
1065 try_connect(phb); 1115 try_connect(phb);
1066 return; 1116 return;
1077 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, phb->read_cb, phb); 1127 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, phb->read_cb, phb);
1078 } 1128 }
1079 1129
1080 #define HTTP_GOODSTRING "HTTP/1.0 200" 1130 #define HTTP_GOODSTRING "HTTP/1.0 200"
1081 #define HTTP_GOODSTRING2 "HTTP/1.1 200" 1131 #define HTTP_GOODSTRING2 "HTTP/1.1 200"
1082
1083 static void
1084 http_complete(struct PHB *phb, gint source)
1085 {
1086 gaim_debug_info("http proxy", "proxy connection established\n");
1087 if(!phb->account || phb->account->gc) {
1088 phb->func(phb->data, source, GAIM_INPUT_READ);
1089 }
1090 g_free(phb->host);
1091 g_free(phb);
1092 }
1093
1094 1132
1095 /* read the response to the CONNECT request, if we are requesting a non-port-80 tunnel */ 1133 /* read the response to the CONNECT request, if we are requesting a non-port-80 tunnel */
1096 static void 1134 static void
1097 http_canread(gpointer data, gint source, GaimInputCondition cond) 1135 http_canread(gpointer data, gint source, GaimInputCondition cond)
1098 { 1136 {
1099 int len, headers_len, status = 0; 1137 int len, headers_len, status = 0;
1100 gboolean error; 1138 gboolean error;
1101 struct PHB *phb = data; 1139 struct PHB *phb = data;
1102 guchar *p; 1140 guchar *p;
1103 gsize max_read; 1141 gsize max_read;
1142 gchar *msg;
1104 1143
1105 if(phb->read_buffer == NULL) { 1144 if(phb->read_buffer == NULL) {
1106 phb->read_buf_len = 8192; 1145 phb->read_buf_len = 8192;
1107 phb->read_buffer = g_malloc(phb->read_buf_len); 1146 phb->read_buffer = g_malloc(phb->read_buf_len);
1108 phb->read_len = 0; 1147 phb->read_len = 0;
1114 len = read(source, p, max_read); 1153 len = read(source, p, max_read);
1115 if(len < 0 && errno == EAGAIN) 1154 if(len < 0 && errno == EAGAIN)
1116 return; 1155 return;
1117 else if(len <= 0) { 1156 else if(len <= 0) {
1118 close(source); 1157 close(source);
1119 source = -1; 1158 gaim_proxy_phb_error(phb, _("Lost connection with server for an unknown reason."));
1120 g_free(phb->read_buffer);
1121 phb->read_buffer = NULL;
1122 gaim_input_remove(phb->inpa);
1123 phb->inpa = 0;
1124 http_complete(phb, source);
1125 return; 1159 return;
1126 } else { 1160 } else {
1127 phb->read_len += len; 1161 phb->read_len += len;
1128 } 1162 }
1129 p[len] = '\0'; 1163 p[len] = '\0';
1135 headers_len = len; 1169 headers_len = len;
1136 else 1170 else
1137 return; 1171 return;
1138 1172
1139 error = strncmp((const char *)phb->read_buffer, "HTTP/", 5) != 0; 1173 error = strncmp((const char *)phb->read_buffer, "HTTP/", 5) != 0;
1140 if(!error) { 1174 if (!error)
1175 {
1141 int major; 1176 int major;
1142 p = phb->read_buffer + 5; 1177 p = phb->read_buffer + 5;
1143 major = strtol((const char *)p, (char **)&p, 10); 1178 major = strtol((const char *)p, (char **)&p, 10);
1144 error = (major == 0) || (*p != '.'); 1179 error = (major == 0) || (*p != '.');
1145 if(!error) { 1180 if(!error) {
1155 } 1190 }
1156 } 1191 }
1157 1192
1158 /* Read the contents */ 1193 /* Read the contents */
1159 p = (guchar *)g_strrstr((const gchar *)phb->read_buffer, "Content-Length: "); 1194 p = (guchar *)g_strrstr((const gchar *)phb->read_buffer, "Content-Length: ");
1160 if(p != NULL) { 1195 if (p != NULL)
1196 {
1161 gchar *tmp; 1197 gchar *tmp;
1162 int len = 0; 1198 int len = 0;
1163 char tmpc; 1199 char tmpc;
1164 p += strlen("Content-Length: "); 1200 p += strlen("Content-Length: ");
1165 tmp = strchr((const char *)p, '\r'); 1201 tmp = strchr((const char *)p, '\r');
1178 if (read(source, &tmpc, 1) < 0 && errno != EAGAIN) 1214 if (read(source, &tmpc, 1) < 0 && errno != EAGAIN)
1179 break; 1215 break;
1180 } 1216 }
1181 } 1217 }
1182 1218
1183 if(error) { 1219 if (error)
1184 gaim_debug_error("proxy", 1220 {
1185 "Unable to parse proxy's response: %s\n", 1221 close(source);
1222 msg = g_strdup_printf("Unable to parse response from HTTP proxy: %s\n",
1186 phb->read_buffer); 1223 phb->read_buffer);
1187 close(source); 1224 gaim_proxy_phb_error(phb, msg);
1188 source = -1; 1225 g_free(msg);
1189 g_free(phb->read_buffer); 1226 return;
1190 phb->read_buffer = NULL; 1227 }
1191 gaim_input_remove(phb->inpa); 1228 else if (status != 200)
1192 phb->inpa = 0; 1229 {
1193 http_complete(phb, source);
1194 return;
1195 } else if(status != 200) {
1196 gaim_debug_error("proxy", 1230 gaim_debug_error("proxy",
1197 "Proxy server replied with:\n%s\n", 1231 "Proxy server replied with:\n%s\n",
1198 phb->read_buffer); 1232 phb->read_buffer);
1199 1233
1200 1234
1201 /* XXX: why in the hell are we calling gaim_connection_error() here? */
1202 if(status == 407 /* Proxy Auth */) { 1235 if(status == 407 /* Proxy Auth */) {
1203 gchar *ntlm; 1236 gchar *ntlm;
1204 if((ntlm = g_strrstr((const gchar *)phb->read_buffer, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */ 1237 if((ntlm = g_strrstr((const gchar *)phb->read_buffer, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */
1205 gchar *tmp = ntlm; 1238 gchar *tmp = ntlm;
1206 guint8 *nonce; 1239 guint8 *nonce;
1207 gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi); 1240 gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi);
1208 gchar *username; 1241 gchar *username;
1209 gchar *request; 1242 gchar *request;
1210 gchar *response; 1243 gchar *response;
1211 if(!(username = strchr(domain, '\\'))) { 1244 username = strchr(domain, '\\');
1212 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1245 if (username == NULL)
1246 {
1213 close(source); 1247 close(source);
1214 source = -1; 1248 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status);
1215 if(phb->account) 1249 gaim_proxy_phb_error(phb, msg);
1216 gaim_connection_error(phb->account->gc, msg);
1217 else
1218 gaim_debug_error("http proxy", "%s\n", msg);
1219 g_free(msg); 1250 g_free(msg);
1220 gaim_input_remove(phb->inpa);
1221 g_free(phb->read_buffer);
1222 g_free(phb->host);
1223 g_free(phb);
1224 return; 1251 return;
1225 } 1252 }
1226 *username = '\0'; 1253 *username = '\0';
1227 username++; 1254 username++;
1228 ntlm += strlen("Proxy-Authenticate: NTLM "); 1255 ntlm += strlen("Proxy-Authenticate: NTLM ");
1262 } else if((ntlm = g_strrstr((const char *)phb->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ 1289 } else if((ntlm = g_strrstr((const char *)phb->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */
1263 gchar request[2048]; 1290 gchar request[2048];
1264 gchar *domain = (gchar*) gaim_proxy_info_get_username(phb->gpi); 1291 gchar *domain = (gchar*) gaim_proxy_info_get_username(phb->gpi);
1265 gchar *username; 1292 gchar *username;
1266 int request_len; 1293 int request_len;
1267 if(!(username = strchr(domain, '\\'))) { 1294 username = strchr(domain, '\\');
1268 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1295 if (username == NULL)
1296 {
1269 close(source); 1297 close(source);
1270 source = -1; 1298 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status);
1271 if(phb->account) 1299 gaim_proxy_phb_error(phb, msg);
1272 gaim_connection_error(phb->account->gc, msg);
1273 else
1274 gaim_debug_error("http proxy", "%s\n", msg);
1275 g_free(msg); 1300 g_free(msg);
1276 gaim_input_remove(phb->inpa);
1277 g_free(phb->read_buffer);
1278 g_free(phb->host);
1279 g_free(phb);
1280 return; 1301 return;
1281 } 1302 }
1282 *username = '\0'; 1303 *username = '\0';
1283 1304
1284 request_len = g_snprintf(request, sizeof(request), 1305 request_len = g_snprintf(request, sizeof(request),
1311 GAIM_INPUT_WRITE, proxy_do_write, phb); 1332 GAIM_INPUT_WRITE, proxy_do_write, phb);
1312 1333
1313 proxy_do_write(phb, source, cond); 1334 proxy_do_write(phb, source, cond);
1314 return; 1335 return;
1315 } else { 1336 } else {
1316 char *msg = g_strdup_printf(_("Proxy connection error %d"), status);
1317 close(source); 1337 close(source);
1318 source = -1; 1338 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status);
1319 if(phb->account) 1339 gaim_proxy_phb_error(phb, msg);
1320 gaim_connection_error(phb->account->gc, msg);
1321 else
1322 gaim_debug_error("http proxy", "%s\n", msg);
1323 g_free(msg); 1340 g_free(msg);
1324 gaim_input_remove(phb->inpa);
1325 g_free(phb->read_buffer);
1326 g_free(phb->host);
1327 g_free(phb);
1328 return; 1341 return;
1329 } 1342 }
1330 } 1343 }
1331 if(status == 403 /* Forbidden */ ) { 1344 if(status == 403 /* Forbidden */ ) {
1332 gchar *msg = g_strdup_printf(_("Access denied: proxy server forbids port %d tunnelling."), phb->port); 1345 msg = g_strdup_printf(_("Access denied: HTTP proxy server forbids port %d tunnelling."), phb->port);
1333 if(phb->account) 1346 gaim_proxy_phb_error(phb, msg);
1334 gaim_connection_error(phb->account->gc, msg);
1335 else
1336 gaim_debug_error("http proxy", "%s\n", msg);
1337 g_free(msg); 1347 g_free(msg);
1338 gaim_input_remove(phb->inpa);
1339 g_free(phb->read_buffer);
1340 g_free(phb->host);
1341 g_free(phb);
1342 } else { 1348 } else {
1343 char *msg = g_strdup_printf(_("Proxy connection error %d"), status); 1349 msg = g_strdup_printf(_("HTTP proxy connection error %d"), status);
1344 if(phb->account) 1350 gaim_proxy_phb_error(phb, msg);
1345 gaim_connection_error(phb->account->gc, msg);
1346 else
1347 gaim_debug_error("http proxy", "%s\n", msg);
1348 g_free(msg); 1351 g_free(msg);
1349 gaim_input_remove(phb->inpa);
1350 g_free(phb->read_buffer);
1351 g_free(phb->host);
1352 g_free(phb);
1353 } 1352 }
1354 } else { 1353 } else {
1355 gaim_input_remove(phb->inpa); 1354 gaim_input_remove(phb->inpa);
1355 phb->inpa = 0;
1356 g_free(phb->read_buffer); 1356 g_free(phb->read_buffer);
1357 phb->read_buffer = NULL; 1357 phb->read_buffer = NULL;
1358 http_complete(phb, source); 1358 gaim_debug_info("proxy", "HTTP proxy connection established\n");
1359 gaim_proxy_phb_connected(phb, source);
1359 return; 1360 return;
1360 } 1361 }
1361 } 1362 }
1362 1363
1363 1364
1372 int error = ETIMEDOUT; 1373 int error = ETIMEDOUT;
1373 1374
1374 gaim_debug_info("http proxy", "Connected.\n"); 1375 gaim_debug_info("http proxy", "Connected.\n");
1375 1376
1376 if (phb->inpa > 0) 1377 if (phb->inpa > 0)
1378 {
1377 gaim_input_remove(phb->inpa); 1379 gaim_input_remove(phb->inpa);
1380 phb->inpa = 0;
1381 }
1378 1382
1379 len = sizeof(error); 1383 len = sizeof(error);
1380 1384
1381 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1385 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1382 close(source); 1386 close(source);
1454 if (phb->port != 80) { 1458 if (phb->port != 80) {
1455 /* we need to do CONNECT first */ 1459 /* we need to do CONNECT first */
1456 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, 1460 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE,
1457 http_canwrite, phb); 1461 http_canwrite, phb);
1458 } else { 1462 } else {
1459 http_complete(phb, fd); 1463 gaim_debug_info("proxy", "HTTP proxy connection established\n");
1464 gaim_proxy_phb_connected(phb, fd);
1460 } 1465 }
1461 } else { 1466 } else {
1462 close(fd); 1467 close(fd);
1463 return -1; 1468 return -1;
1464 } 1469 }
1505 1510
1506 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + phb->read_len < 4)) 1511 if ((len < 0 && errno == EAGAIN) || (len > 0 && len + phb->read_len < 4))
1507 return; 1512 return;
1508 else if (len + phb->read_len >= 4) { 1513 else if (len + phb->read_len >= 4) {
1509 if (phb->read_buffer[1] == 90) { 1514 if (phb->read_buffer[1] == 90) {
1510 if (phb->account == NULL || 1515 gaim_proxy_phb_connected(phb, source);
1511 gaim_account_get_connection(phb->account) != NULL) {
1512
1513 phb->func(phb->data, source, GAIM_INPUT_READ);
1514 }
1515
1516 gaim_input_remove(phb->inpa);
1517 g_free(phb->read_buffer);
1518 g_free(phb->host);
1519 g_free(phb);
1520 return; 1516 return;
1521 } 1517 }
1522 } 1518 }
1523 1519
1524 gaim_input_remove(phb->inpa); 1520 gaim_input_remove(phb->inpa);
1521 phb->inpa = 0;
1525 g_free(phb->read_buffer); 1522 g_free(phb->read_buffer);
1526 phb->read_buffer = NULL; 1523 phb->read_buffer = NULL;
1527 1524
1528 close(source); 1525 close(source);
1529 1526
1540 int error = ETIMEDOUT; 1537 int error = ETIMEDOUT;
1541 1538
1542 gaim_debug_info("socks4 proxy", "Connected.\n"); 1539 gaim_debug_info("socks4 proxy", "Connected.\n");
1543 1540
1544 if (phb->inpa > 0) 1541 if (phb->inpa > 0)
1542 {
1545 gaim_input_remove(phb->inpa); 1543 gaim_input_remove(phb->inpa);
1544 phb->inpa = 0;
1545 }
1546 1546
1547 len = sizeof(error); 1547 len = sizeof(error);
1548 1548
1549 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 1549 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1550 close(source); 1550 close(source);
1660 return; 1660 return;
1661 else if(len <= 0) { 1661 else if(len <= 0) {
1662 gaim_debug_warning("socks5 proxy", "or not...\n"); 1662 gaim_debug_warning("socks5 proxy", "or not...\n");
1663 close(source); 1663 close(source);
1664 gaim_input_remove(phb->inpa); 1664 gaim_input_remove(phb->inpa);
1665 phb->inpa = 0;
1665 g_free(phb->read_buffer); 1666 g_free(phb->read_buffer);
1666 phb->read_buffer = NULL; 1667 phb->read_buffer = NULL;
1667 try_connect(phb); 1668 try_connect(phb);
1668 return; 1669 return;
1669 } 1670 }
1677 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); 1678 gaim_debug_error("socks5 proxy", socks5errors[buf[1]]);
1678 else 1679 else
1679 gaim_debug_error("socks5 proxy", "Bad data.\n"); 1680 gaim_debug_error("socks5 proxy", "Bad data.\n");
1680 close(source); 1681 close(source);
1681 gaim_input_remove(phb->inpa); 1682 gaim_input_remove(phb->inpa);
1683 phb->inpa = 0;
1682 g_free(phb->read_buffer); 1684 g_free(phb->read_buffer);
1683 phb->read_buffer = NULL; 1685 phb->read_buffer = NULL;
1684 try_connect(phb); 1686 try_connect(phb);
1685 return; 1687 return;
1686 } 1688 }
1713 return; 1715 return;
1714 1716
1715 /* Skip past BND.PORT */ 1717 /* Skip past BND.PORT */
1716 buf += 2; 1718 buf += 2;
1717 1719
1718 if (phb->account == NULL || 1720 gaim_proxy_phb_connected(phb, source);
1719 gaim_account_get_connection(phb->account) != NULL) {
1720
1721 phb->func(phb->data, source, GAIM_INPUT_READ);
1722 }
1723
1724 gaim_input_remove(phb->inpa);
1725 g_free(phb->read_buffer);
1726 g_free(phb->host);
1727 g_free(phb);
1728 } 1721 }
1729 1722
1730 static void 1723 static void
1731 s5_sendconnect(gpointer data, gint source) 1724 s5_sendconnect(gpointer data, int source)
1732 { 1725 {
1733 struct PHB *phb = data; 1726 struct PHB *phb = data;
1734 int hlen = strlen(phb->host); 1727 int hlen = strlen(phb->host);
1735 phb->write_buf_len = 5 + hlen + 2; 1728 phb->write_buf_len = 5 + hlen + 2;
1736 phb->write_buffer = g_malloc(phb->write_buf_len); 1729 phb->write_buffer = g_malloc(phb->write_buf_len);
1747 1740
1748 phb->read_cb = s5_canread_again; 1741 phb->read_cb = s5_canread_again;
1749 1742
1750 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb); 1743 phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb);
1751 proxy_do_write(phb, source, GAIM_INPUT_WRITE); 1744 proxy_do_write(phb, source, GAIM_INPUT_WRITE);
1752
1753 } 1745 }
1754 1746
1755 static void 1747 static void
1756 s5_readauth(gpointer data, gint source, GaimInputCondition cond) 1748 s5_readauth(gpointer data, gint source, GaimInputCondition cond)
1757 { 1749 {
1771 if(len < 0 && errno == EAGAIN) 1763 if(len < 0 && errno == EAGAIN)
1772 return; 1764 return;
1773 else if(len <= 0) { 1765 else if(len <= 0) {
1774 close(source); 1766 close(source);
1775 gaim_input_remove(phb->inpa); 1767 gaim_input_remove(phb->inpa);
1768 phb->inpa = 0;
1776 g_free(phb->read_buffer); 1769 g_free(phb->read_buffer);
1777 phb->read_buffer = NULL; 1770 phb->read_buffer = NULL;
1778 try_connect(phb); 1771 try_connect(phb);
1779 return; 1772 return;
1780 } 1773 }
1782 1775
1783 if (phb->read_len < 2) 1776 if (phb->read_len < 2)
1784 return; 1777 return;
1785 1778
1786 gaim_input_remove(phb->inpa); 1779 gaim_input_remove(phb->inpa);
1780 phb->inpa = 0;
1787 1781
1788 if ((phb->read_buffer[0] != 0x01) || (phb->read_buffer[1] != 0x00)) { 1782 if ((phb->read_buffer[0] != 0x01) || (phb->read_buffer[1] != 0x00)) {
1789 close(source); 1783 close(source);
1790 g_free(phb->read_buffer); 1784 g_free(phb->read_buffer);
1791 phb->read_buffer = NULL; 1785 phb->read_buffer = NULL;
1863 if(len < 0 && errno == EAGAIN) 1857 if(len < 0 && errno == EAGAIN)
1864 return; 1858 return;
1865 else if(len <= 0) { 1859 else if(len <= 0) {
1866 close(source); 1860 close(source);
1867 gaim_input_remove(phb->inpa); 1861 gaim_input_remove(phb->inpa);
1862 phb->inpa = 0;
1868 g_free(phb->read_buffer); 1863 g_free(phb->read_buffer);
1869 phb->read_buffer = NULL; 1864 phb->read_buffer = NULL;
1870 try_connect(phb); 1865 try_connect(phb);
1871 return; 1866 return;
1872 } 1867 }
1878 cmdbuf = phb->read_buffer; 1873 cmdbuf = phb->read_buffer;
1879 1874
1880 if (*cmdbuf != 0x01) { 1875 if (*cmdbuf != 0x01) {
1881 close(source); 1876 close(source);
1882 gaim_input_remove(phb->inpa); 1877 gaim_input_remove(phb->inpa);
1878 phb->inpa = 0;
1883 g_free(phb->read_buffer); 1879 g_free(phb->read_buffer);
1884 phb->read_buffer = NULL; 1880 phb->read_buffer = NULL;
1885 try_connect(phb); 1881 try_connect(phb);
1886 return; 1882 return;
1887 } 1883 }
1899 switch (cmdbuf[0]) { 1895 switch (cmdbuf[0]) {
1900 case 0x00: 1896 case 0x00:
1901 /* Did auth work? */ 1897 /* Did auth work? */
1902 if (buf[0] == 0x00) { 1898 if (buf[0] == 0x00) {
1903 gaim_input_remove(phb->inpa); 1899 gaim_input_remove(phb->inpa);
1900 phb->inpa = 0;
1904 g_free(phb->read_buffer); 1901 g_free(phb->read_buffer);
1905 phb->read_buffer = NULL; 1902 phb->read_buffer = NULL;
1906 /* Success */ 1903 /* Success */
1907 s5_sendconnect(phb, source); 1904 s5_sendconnect(phb, source);
1908 return; 1905 return;
1911 gaim_debug_warning("proxy", 1908 gaim_debug_warning("proxy",
1912 "socks5 CHAP authentication " 1909 "socks5 CHAP authentication "
1913 "failed. Disconnecting..."); 1910 "failed. Disconnecting...");
1914 close(source); 1911 close(source);
1915 gaim_input_remove(phb->inpa); 1912 gaim_input_remove(phb->inpa);
1913 phb->inpa = 0;
1916 g_free(phb->read_buffer); 1914 g_free(phb->read_buffer);
1917 phb->read_buffer = NULL; 1915 phb->read_buffer = NULL;
1918 try_connect(phb); 1916 try_connect(phb);
1919 return; 1917 return;
1920 } 1918 }
1955 "as supporting. This is a violation " 1953 "as supporting. This is a violation "
1956 "of the socks5 CHAP specification. " 1954 "of the socks5 CHAP specification. "
1957 "Disconnecting..."); 1955 "Disconnecting...");
1958 close(source); 1956 close(source);
1959 gaim_input_remove(phb->inpa); 1957 gaim_input_remove(phb->inpa);
1958 phb->inpa = 0;
1960 g_free(phb->read_buffer); 1959 g_free(phb->read_buffer);
1961 phb->read_buffer = NULL; 1960 phb->read_buffer = NULL;
1962 try_connect(phb); 1961 try_connect(phb);
1963 return; 1962 return;
1964 } 1963 }
1990 if(len < 0 && errno == EAGAIN) 1989 if(len < 0 && errno == EAGAIN)
1991 return; 1990 return;
1992 else if(len <= 0) { 1991 else if(len <= 0) {
1993 close(source); 1992 close(source);
1994 gaim_input_remove(phb->inpa); 1993 gaim_input_remove(phb->inpa);
1994 phb->inpa = 0;
1995 g_free(phb->read_buffer); 1995 g_free(phb->read_buffer);
1996 phb->read_buffer = NULL; 1996 phb->read_buffer = NULL;
1997 try_connect(phb); 1997 try_connect(phb);
1998 return; 1998 return;
1999 } 1999 }
2001 2001
2002 if (phb->read_len < 2) 2002 if (phb->read_len < 2)
2003 return; 2003 return;
2004 2004
2005 gaim_input_remove(phb->inpa); 2005 gaim_input_remove(phb->inpa);
2006 phb->inpa = 0;
2006 2007
2007 if ((phb->read_buffer[0] != 0x05) || (phb->read_buffer[1] == 0xff)) { 2008 if ((phb->read_buffer[0] != 0x05) || (phb->read_buffer[1] == 0xff)) {
2008 close(source); 2009 close(source);
2009 g_free(phb->read_buffer); 2010 g_free(phb->read_buffer);
2010 phb->read_buffer = NULL; 2011 phb->read_buffer = NULL;
2092 int error = ETIMEDOUT; 2093 int error = ETIMEDOUT;
2093 2094
2094 gaim_debug_info("socks5 proxy", "Connected.\n"); 2095 gaim_debug_info("socks5 proxy", "Connected.\n");
2095 2096
2096 if (phb->inpa > 0) 2097 if (phb->inpa > 0)
2098 {
2097 gaim_input_remove(phb->inpa); 2099 gaim_input_remove(phb->inpa);
2100 phb->inpa = 0;
2101 }
2098 2102
2099 len = sizeof(error); 2103 len = sizeof(error);
2100 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 2104 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
2101 close(source); 2105 close(source);
2102 2106
2223 if (ret > 0) 2227 if (ret > 0)
2224 break; 2228 break;
2225 } 2229 }
2226 2230
2227 if (ret < 0) { 2231 if (ret < 0) {
2228 if (phb->account == NULL || 2232 gaim_proxy_phb_error(phb, _("TODO"));
2229 gaim_account_get_connection(phb->account) != NULL) {
2230
2231 phb->func(phb->data, -1, GAIM_INPUT_READ);
2232 }
2233
2234 g_free(phb->host);
2235 g_free(phb);
2236 } 2233 }
2237 } 2234 }
2238 2235
2239 static void 2236 static void
2240 connection_host_resolved(GSList *hosts, gpointer data, 2237 connection_host_resolved(GSList *hosts, gpointer data,
2311 } 2308 }
2312 2309
2313 return gpi; 2310 return gpi;
2314 } 2311 }
2315 2312
2316 /* 2313 GaimProxyConnectInfo *
2317 * TODO: It would be really good if this returned some sort of handle
2318 * that we could use to cancel the connection. As it is now,
2319 * each callback has to check to make sure gc is still valid.
2320 * And that is ugly.
2321 */
2322 int
2323 gaim_proxy_connect(GaimAccount *account, const char *host, int port, 2314 gaim_proxy_connect(GaimAccount *account, const char *host, int port,
2324 GaimInputFunction func, gpointer data) 2315 GaimProxyConnectFunction connect_cb,
2316 GaimProxyErrorFunction error_cb, gpointer data)
2325 { 2317 {
2326 const char *connecthost = host; 2318 const char *connecthost = host;
2327 int connectport = port; 2319 int connectport = port;
2328 struct PHB *phb; 2320 struct PHB *phb;
2329 2321
2330 g_return_val_if_fail(host != NULL, -1); 2322 g_return_val_if_fail(host != NULL, NULL);
2331 g_return_val_if_fail(port != 0 && port != -1, -1); 2323 g_return_val_if_fail(port > 0, NULL);
2332 g_return_val_if_fail(func != NULL, -1); 2324 g_return_val_if_fail(connect_cb != NULL, NULL);
2325 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */
2333 2326
2334 phb = g_new0(struct PHB, 1); 2327 phb = g_new0(struct PHB, 1);
2335 2328 phb->connect_cb = connect_cb;
2336 phb->func = func; 2329 phb->error_cb = error_cb;
2337 phb->data = data; 2330 phb->data = data;
2338 phb->host = g_strdup(host); 2331 phb->host = g_strdup(host);
2339 phb->port = port; 2332 phb->port = port;
2340 phb->account = account;
2341 phb->gpi = gaim_proxy_get_setup(account); 2333 phb->gpi = gaim_proxy_get_setup(account);
2342 2334
2343 if ((gaim_proxy_info_get_type(phb->gpi) != GAIM_PROXY_NONE) && 2335 if ((gaim_proxy_info_get_type(phb->gpi) != GAIM_PROXY_NONE) &&
2344 (gaim_proxy_info_get_host(phb->gpi) == NULL || 2336 (gaim_proxy_info_get_host(phb->gpi) == NULL ||
2345 gaim_proxy_info_get_port(phb->gpi) <= 0)) { 2337 gaim_proxy_info_get_port(phb->gpi) <= 0)) {
2346 2338
2347 gaim_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid.")); 2339 gaim_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
2348 g_free(phb->host); 2340 gaim_proxy_phb_destroy(phb);
2349 g_free(phb); 2341 return NULL;
2350 return -1;
2351 } 2342 }
2352 2343
2353 switch (gaim_proxy_info_get_type(phb->gpi)) 2344 switch (gaim_proxy_info_get_type(phb->gpi))
2354 { 2345 {
2355 case GAIM_PROXY_NONE: 2346 case GAIM_PROXY_NONE:
2362 connecthost = gaim_proxy_info_get_host(phb->gpi); 2353 connecthost = gaim_proxy_info_get_host(phb->gpi);
2363 connectport = gaim_proxy_info_get_port(phb->gpi); 2354 connectport = gaim_proxy_info_get_port(phb->gpi);
2364 break; 2355 break;
2365 2356
2366 default: 2357 default:
2367 g_free(phb->host); 2358 gaim_proxy_phb_destroy(phb);
2368 g_free(phb); 2359 return NULL;
2369 return -1; 2360 }
2370 } 2361
2371 2362 if (gaim_gethostbyname_async(connecthost,
2372 return gaim_gethostbyname_async(connecthost, connectport, 2363 connectport, connection_host_resolved, phb) != 0)
2373 connection_host_resolved, phb); 2364 {
2374 } 2365 gaim_proxy_phb_destroy(phb);
2375 2366 return NULL;
2376 int 2367 }
2368
2369 phbs = g_slist_prepend(phbs, phb);
2370
2371 return phb;
2372 }
2373
2374 /*
2375 * Combine some of this code with gaim_proxy_connect()
2376 */
2377 GaimProxyConnectInfo *
2377 gaim_proxy_connect_socks5(GaimProxyInfo *gpi, const char *host, int port, 2378 gaim_proxy_connect_socks5(GaimProxyInfo *gpi, const char *host, int port,
2378 GaimInputFunction func, gpointer data) 2379 GaimProxyConnectFunction connect_cb,
2380 GaimProxyErrorFunction error_cb, gpointer data)
2379 { 2381 {
2380 struct PHB *phb; 2382 struct PHB *phb;
2381 2383
2384 g_return_val_if_fail(host != NULL, NULL);
2385 g_return_val_if_fail(port > 0, NULL);
2386 g_return_val_if_fail(connect_cb != NULL, NULL);
2387 /* g_return_val_if_fail(error_cb != NULL, NULL); *//* TODO: Soon! */
2388
2382 phb = g_new0(struct PHB, 1); 2389 phb = g_new0(struct PHB, 1);
2383 phb->gpi = gpi; 2390 phb->connect_cb = connect_cb;
2384 phb->func = func; 2391 phb->error_cb = error_cb;
2385 phb->data = data; 2392 phb->data = data;
2386 phb->host = g_strdup(host); 2393 phb->host = g_strdup(host);
2387 phb->port = port; 2394 phb->port = port;
2388 2395 phb->gpi = gpi;
2389 return gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), 2396
2390 gaim_proxy_info_get_port(gpi), connection_host_resolved, phb); 2397 if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi),
2398 gaim_proxy_info_get_port(gpi), connection_host_resolved, phb) != 0)
2399 {
2400 gaim_proxy_phb_destroy(phb);
2401 return NULL;
2402 }
2403
2404 phbs = g_slist_prepend(phbs, phb);
2405
2406 return phb;
2391 } 2407 }
2392 2408
2393 2409
2394 static void 2410 static void
2395 proxy_pref_cb(const char *name, GaimPrefType type, 2411 proxy_pref_cb(const char *name, GaimPrefType type,
2421 gaim_proxy_info_set_port(info, GPOINTER_TO_INT(value)); 2437 gaim_proxy_info_set_port(info, GPOINTER_TO_INT(value));
2422 else if (!strcmp(name, "/core/proxy/username")) 2438 else if (!strcmp(name, "/core/proxy/username"))
2423 gaim_proxy_info_set_username(info, value); 2439 gaim_proxy_info_set_username(info, value);
2424 else if (!strcmp(name, "/core/proxy/password")) 2440 else if (!strcmp(name, "/core/proxy/password"))
2425 gaim_proxy_info_set_password(info, value); 2441 gaim_proxy_info_set_password(info, value);
2442 }
2443
2444 void *
2445 gaim_proxy_get_handle()
2446 {
2447 static int handle;
2448
2449 return &handle;
2426 } 2450 }
2427 2451
2428 void 2452 void
2429 gaim_proxy_init(void) 2453 gaim_proxy_init(void)
2430 { 2454 {
2457 if(!g_thread_supported()) 2481 if(!g_thread_supported())
2458 g_thread_init(NULL); 2482 g_thread_init(NULL);
2459 #endif 2483 #endif
2460 } 2484 }
2461 2485
2462 void * 2486 void
2463 gaim_proxy_get_handle() 2487 gaim_proxy_uninit(void)
2464 { 2488 {
2465 static int handle; 2489 while (phbs != NULL)
2466 2490 gaim_proxy_phb_destroy(phbs->data);
2467 return &handle; 2491 }
2468 }