Bug 682832 - Don't ignore Gnome 3 proxy settings. r=karlt
authorjhorak@redhat.com
Mon, 05 Dec 2011 15:45:54 +0100
changeset 83098 2f33758829d2e432e18d7f280e94cb936ae458a1
parent 83097 28bdd260c3bc11d632df4123aa4b2da755a550d2
child 83099 fafaf614791f698e9f66902560206e37065eee39
child 83113 c7bba86f5e02f46a1c46aa891bce3efebbebaf56
push idunknown
push userunknown
push dateunknown
reviewerskarlt
bugs682832
milestone11.0a1
Bug 682832 - Don't ignore Gnome 3 proxy settings. r=karlt
toolkit/system/gnome/nsGSettingsService.cpp
toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
xpcom/system/nsIGSettingsService.idl
--- a/toolkit/system/gnome/nsGSettingsService.cpp
+++ b/toolkit/system/gnome/nsGSettingsService.cpp
@@ -39,44 +39,48 @@
 #include "mozilla/Util.h"
 
 #include "nsGSettingsService.h"
 #include "nsStringAPI.h"
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 #include "prlink.h"
 #include "nsComponentManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsISupportsPrimitives.h"
 
 #include <glib.h>
 #include <glib-object.h>
 
 using namespace mozilla;
 
 typedef struct _GSettings GSettings;
 typedef struct _GVariantType GVariantType;
 typedef struct _GVariant GVariant;
 
 #ifndef G_VARIANT_TYPE_INT32
 # define G_VARIANT_TYPE_INT32        ((const GVariantType *) "i")
 # define G_VARIANT_TYPE_BOOLEAN      ((const GVariantType *) "b")
 # define G_VARIANT_TYPE_STRING       ((const GVariantType *) "s")
 # define G_VARIANT_TYPE_OBJECT_PATH  ((const GVariantType *) "o")
 # define G_VARIANT_TYPE_SIGNATURE    ((const GVariantType *) "g")
+# define G_VARIANT_TYPE_STRING_ARRAY ((const GVariantType *) "as")
 #endif
 
 #define GSETTINGS_FUNCTIONS \
   FUNC(g_settings_new, GSettings *, (const char* schema)) \
   FUNC(g_settings_list_schemas, const char * const *, (void)) \
   FUNC(g_settings_list_keys, char **, (GSettings* settings)) \
   FUNC(g_settings_get_value, GVariant *, (GSettings* settings, const char* key)) \
   FUNC(g_settings_set_value, gboolean, (GSettings* settings, const char* key, GVariant* value)) \
   FUNC(g_settings_range_check, gboolean, (GSettings* settings, const char* key, GVariant* value)) \
   FUNC(g_variant_get_int32, gint32, (GVariant* variant)) \
   FUNC(g_variant_get_boolean, gboolean, (GVariant* variant)) \
   FUNC(g_variant_get_string, const char *, (GVariant* value, gsize* length)) \
+  FUNC(g_variant_get_strv, const char **, (GVariant* value, gsize* length)) \
   FUNC(g_variant_is_of_type, gboolean, (GVariant* value, const GVariantType* type)) \
   FUNC(g_variant_new_int32, GVariant *, (gint32 value)) \
   FUNC(g_variant_new_boolean, GVariant *, (gboolean value)) \
   FUNC(g_variant_new_string, GVariant *, (const char* string)) \
   FUNC(g_variant_unref, void, (GVariant* value))
 
 #define FUNC(name, type, params) \
   typedef type (*_##name##_fn) params; \
@@ -90,16 +94,17 @@ GSETTINGS_FUNCTIONS
 #define g_settings_list_schemas _g_settings_list_schemas
 #define g_settings_list_keys _g_settings_list_keys
 #define g_settings_get_value _g_settings_get_value
 #define g_settings_set_value _g_settings_set_value
 #define g_settings_range_check _g_settings_range_check
 #define g_variant_get_int32 _g_variant_get_int32
 #define g_variant_get_boolean _g_variant_get_boolean
 #define g_variant_get_string _g_variant_get_string
+#define g_variant_get_strv _g_variant_get_strv
 #define g_variant_is_of_type _g_variant_is_of_type
 #define g_variant_new_int32 _g_variant_new_int32
 #define g_variant_new_boolean _g_variant_new_boolean
 #define g_variant_new_string _g_variant_new_string
 #define g_variant_unref _g_variant_unref
 
 static PRLibrary *gioLib = nsnull;
 
@@ -271,16 +276,59 @@ nsGSettingsCollection::GetInt(const nsAC
 // allow a template (ArrayLength) to be instantiated based on a local type.
 // Boo-urns!
 typedef void (*nsGSettingsFunc)();
 struct nsGSettingsDynamicFunction {
   const char *functionName;
   nsGSettingsFunc *function;
 };
 
+NS_IMETHODIMP
+nsGSettingsCollection::GetStringList(const nsACString& aKey, nsIArray** aResult)
+{
+  if (!KeyExists(aKey))
+    return NS_ERROR_INVALID_ARG;
+
+  nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID));
+  if (!items) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  GVariant *value = g_settings_get_value(mSettings,
+                                         PromiseFlatCString(aKey).get());
+
+  if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) {
+    g_variant_unref(value);
+    return NS_ERROR_FAILURE;
+  }
+
+  const gchar ** gs_strings = g_variant_get_strv(value, NULL);
+  if (!gs_strings) {
+    // empty array
+    NS_ADDREF(*aResult = items);
+    g_variant_unref(value);
+    return NS_OK;
+  }
+
+  const gchar** p_gs_strings = gs_strings;
+  while (*p_gs_strings != NULL)
+  {
+    nsCOMPtr<nsISupportsCString> obj(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+    if (obj) {
+      obj->SetData(nsDependentCString(*p_gs_strings));
+      items->AppendElement(obj, false);
+    }
+    p_gs_strings++;
+  }
+  g_free(gs_strings);
+  NS_ADDREF(*aResult = items);
+  g_variant_unref(value);
+  return NS_OK;
+}
+
 nsresult
 nsGSettingsService::Init()
 {
 #define FUNC(name, type, params) { #name, (nsGSettingsFunc *)&_##name },
   static const nsGSettingsDynamicFunction kGSettingsSymbols[] = {
     GSETTINGS_FUNCTIONS
   };
 #undef FUNC
--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
@@ -44,62 +44,84 @@
 #include "nsIURI.h"
 #include "nsReadableUtils.h"
 #include "nsArrayUtils.h"
 #include "prnetdb.h"
 #include "prenv.h"
 #include "nsPrintfCString.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPrimitives.h"
+#include "nsIGSettingsService.h"
 
 class nsUnixSystemProxySettings : public nsISystemProxySettings {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISYSTEMPROXYSETTINGS
 
   nsUnixSystemProxySettings() {}
   nsresult Init();
 
 private:
   ~nsUnixSystemProxySettings() {}
   
   nsCOMPtr<nsIGConfService> mGConf;
+  nsCOMPtr<nsIGSettingsService> mGSettings;
   bool IsProxyMode(const char* aMode);
   nsresult SetProxyResultFromGConf(const char* aKeyBase, const char* aType, nsACString& aResult);
   nsresult GetProxyFromGConf(const nsACString& aScheme, const nsACString& aHost, PRInt32 aPort, nsACString& aResult);
+  nsresult GetProxyFromGSettings(const nsACString& aScheme, const nsACString& aHost, PRInt32 aPort, nsACString& aResult);
+  nsresult SetProxyResultFromGSettings(const char* aKeyBase, const char* aType, nsACString& aResult);
 };
 
 NS_IMPL_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings)
 
 nsresult
 nsUnixSystemProxySettings::Init()
 {
   mGConf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
+  mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
   return NS_OK;
 }
 
 bool
 nsUnixSystemProxySettings::IsProxyMode(const char* aMode)
 {
   nsCAutoString mode;
   return NS_SUCCEEDED(mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/mode"), mode)) &&
          mode.EqualsASCII(aMode);
 }
 
 nsresult
 nsUnixSystemProxySettings::GetPACURI(nsACString& aResult)
 {
-  if (!mGConf || !IsProxyMode("auto")) {
-    // Return an empty string in this case
-    aResult.Truncate();
-    return NS_OK;
+  if (mGSettings) {
+    nsCOMPtr<nsIGSettingsCollection> proxy_settings;
+    mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"), 
+                                       getter_AddRefs(proxy_settings));
+    if (proxy_settings) {
+      nsCString proxyMode;
+      // Check if mode is auto
+      nsresult rv = proxy_settings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
+      if (rv == NS_OK && proxyMode.Equals("auto")) {
+        return proxy_settings->GetString(NS_LITERAL_CSTRING("autoconfig-url"), aResult);
+      }
+      /* The org.gnome.system.proxy schema has been found, but auto mode is not set.
+       * Don't try the GConf and return empty string. */
+      aResult.Truncate();
+      return NS_OK;
+    }
   }
 
-  return mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/autoconfig_url"),
-                           aResult);
+  if (mGConf && IsProxyMode("auto")) {
+    return mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/autoconfig_url"),
+                             aResult);
+  }
+  // Return an empty string when auto mode is not set.
+  aResult.Truncate();
+  return NS_OK;
 }
 
 static bool
 IsInNoProxyList(const nsACString& aHost, PRInt32 aPort, const char* noProxyVal)
 {
   NS_ASSERTION(aPort >= 0, "Negative port?");
   
   nsCAutoString noProxy(noProxyVal);
@@ -226,17 +248,48 @@ nsUnixSystemProxySettings::SetProxyResul
     return NS_ERROR_FAILURE;
   
   nsCAutoString portKey;
   portKey.AppendASCII(aKeyBase);
   portKey.AppendLiteral("port");
   PRInt32 port;
   rv = mGConf->GetInt(portKey, &port);
   NS_ENSURE_SUCCESS(rv, rv);
+
+  /* When port is 0, proxy is not considered as enabled even if host is set. */
+  if (port == 0)
+    return NS_ERROR_FAILURE;
+
+  SetProxyResult(aType, host, port, aResult);
+  return NS_OK;
+}
+
+nsresult
+nsUnixSystemProxySettings::SetProxyResultFromGSettings(const char* aKeyBase, const char* aType,
+                                                       nsACString& aResult)
+{
+  nsCOMPtr<nsIGSettingsCollection> proxy_settings;
+  nsresult rv = mGSettings->GetCollectionForSchema(nsDependentCString(aKeyBase),
+                                                   getter_AddRefs(proxy_settings));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString host;
+  rv = proxy_settings->GetString(NS_LITERAL_CSTRING("host"), host);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (host.IsEmpty())
+    return NS_ERROR_FAILURE;
+  
+  PRInt32 port;
+  rv = proxy_settings->GetInt(NS_LITERAL_CSTRING("port"), &port);
+  NS_ENSURE_SUCCESS(rv, rv);
     
+  /* When port is 0, proxy is not considered as enabled even if host is set. */
+  if (port == 0)
+    return NS_ERROR_FAILURE;
+
   SetProxyResult(aType, host, port, aResult);
   return NS_OK;
 }
 
 /* copied from nsProtocolProxyService.cpp --- we should share this! */
 static void
 proxy_MaskIPv6Addr(PRIPv6Addr &addr, PRUint16 mask_len)
 {
@@ -266,36 +319,36 @@ proxy_MaskIPv6Addr(PRIPv6Addr &addr, PRU
             PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
   }
 }
 
 static bool ConvertToIPV6Addr(const nsACString& aName,
                                 PRIPv6Addr* aAddr)
 {
   PRNetAddr addr;
+  // try to convert hostname to IP
   if (PR_StringToNetAddr(PromiseFlatCString(aName).get(), &addr) != PR_SUCCESS)
     return false;
 
-  PRIPv6Addr ipv6;
   // convert parsed address to IPv6
   if (addr.raw.family == PR_AF_INET) {
     // convert to IPv4-mapped address
-    PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
+    PR_ConvertIPv4AddrToIPv6(addr.inet.ip, aAddr);
   } else if (addr.raw.family == PR_AF_INET6) {
     // copy the address
-    memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
+    memcpy(aAddr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
   } else {
     return false;
   }
   
   return true;
 }
 
-static bool GConfIgnoreHost(const nsACString& aIgnore,
-                              const nsACString& aHost)
+static bool HostIgnoredByProxy(const nsACString& aIgnore,
+                               const nsACString& aHost)
 {
   if (aIgnore.Equals(aHost, nsCaseInsensitiveCStringComparator()))
     return true;
 
   if (aIgnore.First() == '*' &&
       StringEndsWith(aHost, nsDependentCSubstring(aIgnore, 1),
                      nsCaseInsensitiveCStringComparator()))
     return true;
@@ -316,18 +369,19 @@ static bool GConfIgnoreHost(const nsACSt
     if (err != 0) {
       mask = 128;
     }
     --slash;
   } else {
     slash = end;
   }
 
+  nsDependentCSubstring ignoreStripped(start, slash);
   PRIPv6Addr ignoreAddr, hostAddr;
-  if (!ConvertToIPV6Addr(aIgnore, &ignoreAddr) ||
+  if (!ConvertToIPV6Addr(ignoreStripped, &ignoreAddr) ||
       !ConvertToIPV6Addr(aHost, &hostAddr))
     return false;
 
   proxy_MaskIPv6Addr(ignoreAddr, mask);
   proxy_MaskIPv6Addr(hostAddr, mask);
   
   return memcmp(&ignoreAddr, &hostAddr, sizeof(PRIPv6Addr)) == 0;
 }
@@ -350,17 +404,17 @@ nsUnixSystemProxySettings::GetProxyFromG
                                          getter_AddRefs(ignoreList))) && ignoreList) {
     PRUint32 len = 0;
     ignoreList->GetLength(&len);
     for (PRUint32 i = 0; i < len; ++i) {
       nsCOMPtr<nsISupportsString> str = do_QueryElementAt(ignoreList, i);
       if (str) {
         nsAutoString s;
         if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
-          if (GConfIgnoreHost(NS_ConvertUTF16toUTF8(s), aHost)) {
+          if (HostIgnoredByProxy(NS_ConvertUTF16toUTF8(s), aHost)) {
             aResult.AppendLiteral("DIRECT");
             return NS_OK;
           }
         }
       }
     }
   }
 
@@ -387,34 +441,104 @@ nsUnixSystemProxySettings::GetProxyFromG
   
   if (NS_FAILED(rv)) {
     aResult.AppendLiteral("DIRECT");
   }
   return NS_OK;
 }
 
 nsresult
+nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme,
+                                                 const nsACString& aHost,
+                                                 PRInt32 aPort,
+                                                 nsACString& aResult)
+{
+  nsCOMPtr<nsIGSettingsCollection> proxy_settings;
+  nsresult rv;
+
+  rv = mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"),
+                                          getter_AddRefs(proxy_settings));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString proxyMode; 
+  rv = proxy_settings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  if (!proxyMode.Equals("manual")) {
+    aResult.AppendLiteral("DIRECT");
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIArray> ignoreList;
+  if (NS_SUCCEEDED(proxy_settings->GetStringList(NS_LITERAL_CSTRING("ignore-hosts"),
+                                                 getter_AddRefs(ignoreList))) && ignoreList) {
+    PRUint32 len = 0;
+    ignoreList->GetLength(&len);
+    for (PRUint32 i = 0; i < len; ++i) {
+      nsCOMPtr<nsISupportsCString> str = do_QueryElementAt(ignoreList, i);
+      if (str) {
+        nsCString s;
+        if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
+          if (HostIgnoredByProxy(s, aHost)) {
+            aResult.AppendLiteral("DIRECT");
+            return NS_OK;
+          }
+        }
+      }
+    }
+  }
+
+  if (aScheme.LowerCaseEqualsLiteral("http")) {
+    rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult);
+  } else if (aScheme.LowerCaseEqualsLiteral("https")) {
+    rv = SetProxyResultFromGSettings("org.gnome.system.proxy.https", "PROXY", aResult);
+    /* Try to use HTTP proxy when HTTPS proxy is not explicitly defined */
+    if (rv != NS_OK) 
+      rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult);
+  } else if (aScheme.LowerCaseEqualsLiteral("ftp")) {
+    rv = SetProxyResultFromGSettings("org.gnome.system.proxy.ftp", "PROXY", aResult);
+  } else {
+    rv = NS_ERROR_FAILURE;
+  }
+  if (rv != NS_OK) {
+     /* If proxy for scheme is not specified, use SOCKS proxy for all schemes */
+     rv = SetProxyResultFromGSettings("org.gnome.system.proxy.socks", "SOCKS", aResult);
+  }
+  
+  if (NS_FAILED(rv)) {
+    aResult.AppendLiteral("DIRECT");
+  }
+  
+  return NS_OK;
+}
+
+nsresult
 nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult)
 {
   nsCAutoString scheme;
   nsresult rv = aURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCAutoString host;
   rv = aURI->GetHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRInt32 port;
   rv = aURI->GetPort(&port);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!mGConf)
-    return GetProxyFromEnvironment(scheme, host, port, aResult);
+  if (mGSettings) {
+    rv = GetProxyFromGSettings(scheme, host, port, aResult);
+    if (rv == NS_OK)
+      return rv;
+  }
+  if (mGConf)
+    return GetProxyFromGConf(scheme, host, port, aResult);
 
-  return GetProxyFromGConf(scheme, host, port, aResult);
+  return GetProxyFromEnvironment(scheme, host, port, aResult);
 }
 
 #define NS_UNIXSYSTEMPROXYSERVICE_CID  /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\
      { 0x0fa3158c, 0xd5a7, 0x43de, \
        {0x91, 0x81, 0xa2, 0x85, 0xe7, 0x4c, 0xf1, 0xd4 } }
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUnixSystemProxySettings, Init)
 NS_DEFINE_NAMED_CID(NS_UNIXSYSTEMPROXYSERVICE_CID);
--- a/xpcom/system/nsIGSettingsService.idl
+++ b/xpcom/system/nsIGSettingsService.idl
@@ -34,25 +34,26 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIArray.idl"
 
-[scriptable, uuid(09637d3c-3c07-40b4-aff9-1d2a0f046f3c)]
+[scriptable, uuid(16d5b0ed-e756-4f1b-a8ce-9132e869acd8)]
 interface nsIGSettingsCollection : nsISupports
 {
   void          setString(in AUTF8String key, in AUTF8String value);
   void          setBoolean(in AUTF8String key, in boolean value);
   void          setInt(in AUTF8String key, in long value);
   AUTF8String   getString(in AUTF8String key);
   boolean       getBoolean(in AUTF8String key);
   long          getInt(in AUTF8String key);
+  nsIArray      getStringList(in AUTF8String key);
 };
 
 [scriptable, uuid(849c088b-57d1-4f24-b7b2-3dc4acb04c0a)]
 interface nsIGSettingsService : nsISupports
 {
   nsIGSettingsCollection    getCollectionForSchema(in AUTF8String schema);
 };