gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r5822 - in GNUnet: contrib doc/man src/include src/setup sr


From: gnunet
Subject: [GNUnet-SVN] r5822 - in GNUnet: contrib doc/man src/include src/setup src/setup/gtk src/setup/ncurses src/transports
Date: Sat, 8 Dec 2007 00:06:31 -0700 (MST)

Author: grothoff
Date: 2007-12-08 00:06:30 -0700 (Sat, 08 Dec 2007)
New Revision: 5822

Modified:
   GNUnet/contrib/config-daemon.scm
   GNUnet/doc/man/gnunet-setup.1
   GNUnet/src/include/gnunet_util_config.h
   GNUnet/src/setup/gnunet-setup.c
   GNUnet/src/setup/gtk/wizard_gtk.c
   GNUnet/src/setup/ncurses/wizard_curs.c
   GNUnet/src/transports/nat.c
Log:
fixing #1249

Modified: GNUnet/contrib/config-daemon.scm
===================================================================
--- GNUnet/contrib/config-daemon.scm    2007-12-07 15:37:45 UTC (rev 5821)
+++ GNUnet/contrib/config-daemon.scm    2007-12-08 07:06:30 UTC (rev 5822)
@@ -798,11 +798,11 @@
  "NAT"
  "LIMITED"
  (_ "Is this machine unreachable behind a NAT?")
- (_ "Set to YES if this machine is behind a NAT that limits connections from 
the outside to the GNUnet port and that cannot be traversed using UPnP.  Note 
that if you have configured your NAT box to allow direct connections from other 
machines to the GNUnet ports or if GNUnet can open ports using UPnP, you should 
set the option to NO.  Set this only to YES if other peers cannot contact you 
directly.  You can use 'make check' in src/transports/upnp/ to find out if your 
NAT supports UPnP.  You can also use gnunet-transport-check with the '-p' 
option in order to determine which setting results in more connections.  Use 
YES only if you get no connections otherwise.")
+ (_ "Set to YES if this machine is behind a NAT that limits connections from 
the outside to the GNUnet port and that cannot be traversed using UPnP.  Note 
that if you have configured your NAT box to allow direct connections from other 
machines to the GNUnet ports or if GNUnet can open ports using UPnP, you should 
set the option to NO.  Set this only to YES if other peers cannot contact you 
directly.  You can use 'make check' in src/transports/upnp/ to find out if your 
NAT supports UPnP.  You can also use gnunet-transport-check with the '-p' 
option in order to determine which setting results in more connections.  Use 
YES only if you get no connections otherwise.  Set to AUTO to use YES if the 
local IP is belongs to a private IP network and NO otherwise.")
  '()
  #t
- #f
- #f
+ "AUTO"
+ (list "SC" "YES" "AUTO" "NO")
  'nat-loaded) )
 
 (define (tcp-port builder)
@@ -815,7 +815,7 @@
  #t
  2086
  (cons 0 65535)
- 'nat-unlimited))
+ 'advanced))
 
 (define (tcp-upnp builder)
  (builder
@@ -881,7 +881,7 @@
  #t
  1080
  (cons 0 65535)
- 'nat-unlimited))
+ 'advanced))
 
 (define (http-upnp builder)
  (builder
@@ -905,7 +905,7 @@
  #t
  80
  (cons 0 65535)
- 'nat-unlimited))
+ 'advanced))
 
 (define (http builder)
  (builder
@@ -1097,7 +1097,7 @@
  #t
  2088
  (cons 0 65535)
- 'nat-unlimited))
+ 'advanced))
 
 (define (tcp6-blacklist builder)
  (builder
@@ -1216,7 +1216,7 @@
  #t
  "eth0"
  '()
- 'nat-unlimited) )
+ 'advanced) )
 
 (define (network-ip builder)
  (builder
@@ -1228,7 +1228,7 @@
  #t
  ""
  '()
- 'nat-unlimited) )
+ 'advanced) )
 
 (define (network-ip6 builder)
  (builder
@@ -1459,8 +1459,6 @@
      (mysql (string= (get-option ctx "MODULES" "sqstore") "sqstore_mysql") )
      (fs-loaded (list? (member "fs" (string-split (get-option ctx "GNUNETD" 
"APPLICATIONS") #\  ) ) ) )
      (nat-loaded (list? (member "nat" (string-split (get-option ctx "GNUNETD" 
"TRANSPORTS") #\  ) ) ) )
-     (nat-limited (get-option ctx "NAT" "LIMITED"))
-     (nat-unlimited (not (get-option ctx "NAT" "LIMITED")))
      (tcp-loaded (list? (member "tcp" (string-split (get-option ctx "GNUNETD" 
"TRANSPORTS") #\  ) ) ) )
      (udp-loaded (list? (member "udp" (string-split (get-option ctx "GNUNETD" 
"TRANSPORTS") #\  ) ) ) )
      (tcp6-loaded (list? (member "tcp6" (string-split (get-option ctx 
"GNUNETD" "TRANSPORTS") #\  ) ) ) )
@@ -1469,15 +1467,6 @@
      (smtp-loaded (list? (member "smtp" (string-split (get-option ctx 
"GNUNETD" "TRANSPORTS") #\  ) ) ) )
    )
   (begin 
-    (if (and nat-loaded nat-limited tcp-loaded)
-        (set-option ctx "TCP" "PORT" "0")
-        'nothing)
-    (if (and nat-loaded nat-limited tcp6-loaded)
-        (set-option ctx "TCP6" "PORT" "0")
-        'nothing)
-    (if (and nat-loaded nat-limited http-loaded)
-        (set-option ctx "HTTP" "PORT" "0")
-        'nothing) 
     (main
      (lambda (a b c d e f g h i) 
         (begin 

Modified: GNUnet/doc/man/gnunet-setup.1
===================================================================
--- GNUnet/doc/man/gnunet-setup.1       2007-12-07 15:37:45 UTC (rev 5821)
+++ GNUnet/doc/man/gnunet-setup.1       2007-12-08 07:06:30 UTC (rev 5822)
@@ -1,4 +1,4 @@
-.TH GNUNET-SETUP "1" "11 Nov 2006" "GNUnet"
+.TH GNUNET-SETUP "1" "8 Dec 2007" "GNUnet"
 .SH NAME
 gnunet\-setup \- configuring GNUnet
 .SH SYNOPSIS
@@ -17,8 +17,15 @@
 \fB\-d\fR, \fB\-\-daemon\fR
 generate configuration for gnunetd (gnunetd.conf).  If run without this 
option, gnunet\-setup will generate a configuration for GNUnet clients.
 .TP
+\fB\-g \fISECTION:OPTION\fR, \fB\-\-get=\fISECTION:OPTION\fR
+display the value of the given OPTION from the given SECTION in the 
configuration file.
+.TP
 \fB\-L \fILOGLEVEL\fR, \fB\-\-loglevel=LOGLEVEL\fR
-change the loglevel. Possible values for LOGLEVEL are NOTHING, FATAL, ERROR, 
FAILURE, WARNING, MESSAGE, INFO, DEBUG, CRON and EVERYTHING..TP
+change the loglevel. Possible values for LOGLEVEL are NOTHING, FATAL, ERROR, 
FAILURE, WARNING, MESSAGE, INFO, DEBUG, CRON and EVERYTHING.
+.TP
+\fB\-s \fISECTION:OPTION=VALUE\fR, \fB\-\-set=\fISECTION:OPTION=VALUE\fR
+set the value of the given OPTION from the given SECTION to the specified 
VALUE.  If this change implies other options also changing, perform those 
changes as well.
+.TP
 \fB\-v\fR, \fB\-\-version\fR
 print the version number
 .TP

Modified: GNUnet/src/include/gnunet_util_config.h
===================================================================
--- GNUnet/src/include/gnunet_util_config.h     2007-12-07 15:37:45 UTC (rev 
5821)
+++ GNUnet/src/include/gnunet_util_config.h     2007-12-08 07:06:30 UTC (rev 
5822)
@@ -136,6 +136,7 @@
 /**
  * Get a configuration value that should be in a set of
  * predefined strings
+ *
  * @param choices NULL-terminated list of legal values
  * @param default default value (use indicated by return value;
  *        will NOT be aliased, maybe NULL), must be reference

Modified: GNUnet/src/setup/gnunet-setup.c
===================================================================
--- GNUnet/src/setup/gnunet-setup.c     2007-12-07 15:37:45 UTC (rev 5821)
+++ GNUnet/src/setup/gnunet-setup.c     2007-12-08 07:06:30 UTC (rev 5822)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2001, 2002, 2005, 2006 Christian Grothoff (and other contributing 
authors)
+     (C) 2001, 2002, 2005, 2006, 2007 Christian Grothoff (and other 
contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -48,6 +48,60 @@
 
 static char *cfgFilename;
 
+static int option_processing;
+
+static char * get_option;
+
+static char * set_option;
+
+static int
+set_option_helper (GNUNET_CommandLineProcessorContext * ctx,
+                  void * unused,
+                  const char *cmdLineOption,
+                  const char *value)
+{
+  option_processing = GNUNET_YES;
+  if (set_option != NULL) 
+    {
+      fprintf (stderr,
+              _("Can only set one option per invocation.\n"));
+      return GNUNET_SYSERR;
+    }
+  if ( (NULL == strstr(value, ":")) ||
+       (NULL == strstr(strstr(value, ":"), "=")) ) 
+    {
+      fprintf (stderr,
+              _("Invalid synatx, argument to 'set' must have the format 
SECTION:OPTION=VALUE.\n"));
+      return GNUNET_SYSERR;
+    }
+  set_option = GNUNET_strdup(value);
+  return GNUNET_OK;
+}
+
+static int
+get_option_helper (GNUNET_CommandLineProcessorContext * ctx,
+           void * unused,
+           const char *cmdLineOption,
+           const char *value)
+{
+  option_processing = GNUNET_YES;
+  if (get_option != NULL) 
+    {
+      fprintf (stderr,
+              _("Can only display one option per invocation.\n"));
+      return GNUNET_SYSERR;
+    }
+  if (NULL == strstr(value, ":")) 
+    {
+      fprintf (stderr,
+              _("Invalid synatx, argument to 'get' must have the format 
SECTION:OPTION.\n"));
+      return GNUNET_SYSERR;
+    }
+  get_option = GNUNET_strdup(value);
+  return GNUNET_OK;
+}
+
+
 /**
  * All gnunet-setup command line options
  */
@@ -56,7 +110,13 @@
   {'d', "daemon", NULL,
    gettext_noop ("generate configuration for gnunetd, the GNUnet daemon"),
    0, &GNUNET_getopt_configure_set_one, &config_daemon},
+  {'g', "get", "SECTION:ENTRY",
+   gettext_noop ("print a value from the configuration file to stdout"),
+   1, &get_option_helper, NULL },
   GNUNET_COMMAND_LINE_OPTION_HELP (gettext_noop ("Tool to setup GNUnet.")),    
 /* -h */
+  {'s', "set", "SECTION:ENTRY=VALUE",
+   gettext_noop ("update a value in the configuration file"),
+   1, &set_option_helper, NULL },
   GNUNET_COMMAND_LINE_OPTION_VERSION (PACKAGE_VERSION), /* -v */
   GNUNET_COMMAND_LINE_OPTION_VERBOSE,
   GNUNET_COMMAND_LINE_OPTION_END,
@@ -149,6 +209,8 @@
   int done;
   char *dirname;
   char *specname;
+  char * value;
+  char * option;
   int i;
 
   ectx = GNUNET_GE_create_context_stderr (GNUNET_NO,
@@ -172,28 +234,35 @@
       GNUNET_GE_free_context (ectx);
       return -1;
     }
-  if (i != argc - 1)
+  if (option_processing) 
     {
-      if (i < argc - 1)
-        {
-          fprintf (stderr, _("Too many arguments.\n"));
-          return -1;
-        }
-      GNUNET_GE_LOG (ectx,
-                     GNUNET_GE_WARNING | GNUNET_GE_REQUEST | GNUNET_GE_USER,
-                     _("No interface specified, using default\n"));
-      operation = "config";
+      operation = "options";
+    }
+  else 
+    {
+      if (i != argc - 1)
+       {
+         if (i < argc - 1)
+           {
+             fprintf (stderr, _("Too many arguments.\n"));
+             return -1;
+           }
+         GNUNET_GE_LOG (ectx,
+                        GNUNET_GE_WARNING | GNUNET_GE_REQUEST | GNUNET_GE_USER,
+                        _("No interface specified, using default\n"));
+         operation = "config";
 #if HAVE_DIALOG
-      operation = "menuconfig";
+         operation = "menuconfig";
 #endif
 #if HAVE_GTK
-      operation = "gconfig";
+         operation = "gconfig";
 #endif
+       }
+      else
+       {
+         operation = argv[i];
+       }
     }
-  else
-    {
-      operation = argv[i];
-    }
   if (NULL != strstr (operation, "wizard"))
     config_daemon = GNUNET_YES; /* wizard implies daemon! */
   if (cfgFilename == NULL)
@@ -245,34 +314,86 @@
       return -1;
     }
   gns2cfg (GNUNET_GNS_get_tree_root (gns));
-
-  done = GNUNET_NO;
-  i = 0;
-  while ((done == GNUNET_NO) && (modules[i] != NULL))
+  if (option_processing) 
     {
-      if (strcmp (operation, modules[i]) == 0)
-        {
-          if (dyn_config (modules[i + 1],
-                          modules[i + 2], argc, argv,
-                          cfgFilename) != GNUNET_YES)
-            {
-              GNUNET_GE_LOG (ectx,
-                             GNUNET_GE_FATAL | GNUNET_GE_USER |
-                             GNUNET_GE_ADMIN | GNUNET_GE_IMMEDIATE,
-                             _("`%s' is not available."), operation);
-              GNUNET_GNS_free_specification (gns);
-              GNUNET_GC_free (cfg);
-              GNUNET_GE_free_context (ectx);
-              GNUNET_free (cfgFilename);
-              return -1;
-            }
-          else
-            {
-              done = GNUNET_YES;
-            }
-        }
-      i += 3;
+      if (get_option != NULL) 
+       {
+         option = strstr(get_option, ":");
+         option[0] = '\0';
+         option++;
+         done = 0;
+         if (GNUNET_NO == GNUNET_GC_have_configuration_value(cfg,
+                                                             get_option,
+                                                             option)) 
+           {
+             fprintf(stderr,
+                     _("Undefined option.\n"));
+             done = 1;
+           }
+         else 
+           {
+             GNUNET_GC_get_configuration_value_string(cfg,
+                                                      get_option,
+                                                      option,
+                                                      NULL,
+                                                      &value);
+             fprintf(stdout, "%s\n", value);
+             GNUNET_free(value);
+           }
+         GNUNET_free(get_option);
+       }
+      if (set_option != NULL) 
+       {
+         option = strstr(set_option, ":");
+         option[0] = '\0';
+         option++;
+         value = strstr(option, "=");
+         value[0] = '\0';
+         value++;
+         if ( (GNUNET_OK != GNUNET_GC_set_configuration_value_string(cfg, ectx,
+                                                                   set_option,
+                                                                   option,
+                                                                     value)) ||
+              (GNUNET_OK != GNUNET_GC_write_configuration (cfg, cfgFilename)) )
+           done = 1;
+         GNUNET_free(set_option);
+       }
+      GNUNET_GNS_free_specification (gns);
+      GNUNET_GC_free (cfg);
+      GNUNET_GE_free_context (ectx);
+      GNUNET_free (cfgFilename);
+      return done;
     }
+  else 
+    {
+      done = GNUNET_NO;
+      i = 0;
+      while ((done == GNUNET_NO) && (modules[i] != NULL))
+       {
+         if (strcmp (operation, modules[i]) == 0)
+           {
+             if (dyn_config (modules[i + 1],
+                             modules[i + 2], argc, argv,
+                             cfgFilename) != GNUNET_YES)
+               {
+                 GNUNET_GE_LOG (ectx,
+                                GNUNET_GE_FATAL | GNUNET_GE_USER |
+                                GNUNET_GE_ADMIN | GNUNET_GE_IMMEDIATE,
+                                _("`%s' is not available."), operation);
+                 GNUNET_GNS_free_specification (gns);
+                 GNUNET_GC_free (cfg);
+                 GNUNET_GE_free_context (ectx);
+                 GNUNET_free (cfgFilename);
+                 return -1;
+               }
+             else
+               {
+                 done = GNUNET_YES;
+               }
+           }
+         i += 3;
+       }
+    }
   GNUNET_free (cfgFilename);
   if (done == GNUNET_NO)
     {

Modified: GNUnet/src/setup/gtk/wizard_gtk.c
===================================================================
--- GNUnet/src/setup/gtk/wizard_gtk.c   2007-12-07 15:37:45 UTC (rev 5821)
+++ GNUnet/src/setup/gtk/wizard_gtk.c   2007-12-08 07:06:30 UTC (rev 5822)
@@ -192,10 +192,14 @@
   GNUNET_free (val);
 
   chkFW = lookup_widget ("chkFW");
+#if 0
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chkFW),
                                 GNUNET_GC_get_configuration_value_yesno
                                 (editCfg, "NAT", "LIMITED",
                                  GNUNET_NO) == GNUNET_YES);
+#else
+  gtk_widget_hide (chkFW);
+#endif
 
   gtk_widget_show (curwnd);
 }
@@ -517,10 +521,12 @@
 void
 on_chkFW_toggledsetup_gtk (GtkToggleButton * togglebutton, gpointer user_data)
 {
+#if 0
   GNUNET_GC_set_configuration_value_choice (editCfg, err_ctx, "NAT",
                                             "LIMITED",
                                             gtk_toggle_button_get_active
                                             (togglebutton) ? "YES" : "NO");
+#endif
 }
 
 void

Modified: GNUnet/src/setup/ncurses/wizard_curs.c
===================================================================
--- GNUnet/src/setup/ncurses/wizard_curs.c      2007-12-07 15:37:45 UTC (rev 
5821)
+++ GNUnet/src/setup/ncurses/wizard_curs.c      2007-12-08 07:06:30 UTC (rev 
5822)
@@ -315,20 +315,6 @@
 }
 
 static int
-nat_limited ()
-{
-  /* TODO: try autodetect! */
-  return query_yesno (_("Network configuration: NAT"),
-                      _("Is this machine behind "
-                        "NAT?\n\nIf you are connected to the internet through 
another computer "
-                        "doing SNAT, a router or a \"hardware firewall\" and 
other computers "
-                        "on the internet cannot connect to this computer, say 
\"yes\" here. "
-                        "Answer \"no\" on direct connections through modems, 
ISDN cards and "
-                        "DNAT (also known as \"port forwarding\")."),
-                      NULL, "NAT", "LIMITED");
-}
-
-static int
 ip_address ()
 {
   /* TODO: try autodetect! */
@@ -558,33 +544,30 @@
           dir = network_interface ();
           break;
         case 2:
-          dir = nat_limited ();
-          break;
-        case 3:
           dir = ip_address ();
           break;
-        case 4:
+        case 3:
           dir = network_load_up ();
           break;
-        case 5:
+        case 4:
           dir = network_load_down ();
           break;
-        case 6:
+        case 5:
           dir = disk_quota ();
           break;
-        case 7:
+        case 6:
           dir = user ();
           break;
-        case 8:
+        case 7:
           dir = group ();
           break;
-        case 9:
+        case 8:
           dir = autostart ();
           break;
-        case 10:
+        case 9:
           dir = save_config ();
           break;
-        case 11:
+        case 10:
           dir = 0;
           ret = GNUNET_OK;
           break;

Modified: GNUnet/src/transports/nat.c
===================================================================
--- GNUnet/src/transports/nat.c 2007-12-07 15:37:45 UTC (rev 5821)
+++ GNUnet/src/transports/nat.c 2007-12-08 07:06:30 UTC (rev 5822)
@@ -32,15 +32,6 @@
 
 #define DEBUG_NAT GNUNET_NO
 
-/**
- * Host-Address in a NAT network.  Since the idea behind
- * NAT is that it can not be contacted from the outside,
- * the address is empty.
- */
-typedef struct
-{
-} HostAddress;
-
 /* *********** globals ************* */
 
 /* apis (our advertised API and the core api ) */
@@ -48,9 +39,34 @@
 
 static GNUNET_CoreAPIForTransport *coreAPI;
 
+static const char * nat_limited_choices[] = { "YES", "NO", "AUTO", NULL };
 
+
 /* *************** API implementation *************** */
 
+static int
+lan_ip_detected () {
+  GNUNET_IPv4Address addr;
+  unsigned int anum;
+  
+  if (GNUNET_SYSERR == getPublicIPAddress (coreAPI->cfg,
+                                          coreAPI->ectx,
+                                          &addr) )
+    return GNUNET_YES; /* kind-of */
+  anum = ntohl(addr.addr);
+  if ( ( (anum >= 0x0a000000) &&
+        (anum <= 0x0affffff) ) || /* 10.x.x.x */
+       ( (anum >= 0xac100000) &&
+        (anum <= 0xac10ffff) ) || /* 172.16.0.0-172.31.0.0 */
+       ( (anum >= 0xc0a80000) &&
+        (anum <= 0xc0a8ffff) ) || /* 192.168.x.x */
+       ( (anum >= 0x7f000000) &&
+        (anum <= 0x7fffffff) ) /* 127.x.x.x */
+       )
+    return GNUNET_YES;
+  return GNUNET_NO;
+}
+
 /**
  * Verify that a hello-Message is correct (a node is reachable at that
  * address).
@@ -62,20 +78,28 @@
 static int
 verifyHello (const GNUNET_MessageHello * hello)
 {
-  if ((ntohs (hello->senderAddressSize) != sizeof (HostAddress)) ||
+  const char * choice;
+
+  if ((ntohs (hello->senderAddressSize) != 0) ||
       (ntohs (hello->header.size) != GNUNET_sizeof_hello (hello)) ||
       (ntohs (hello->header.type) != GNUNET_P2P_PROTO_HELLO))
     return GNUNET_SYSERR;       /* obviously invalid */
-  if (GNUNET_YES == GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
-                                                             "NAT", "LIMITED",
-                                                             GNUNET_NO))
+
+  choice = "AUTO";
+  GNUNET_GC_get_configuration_value_choice (coreAPI->cfg,
+                                           "NAT", "LIMITED",
+                                           nat_limited_choices,
+                                           "AUTO",
+                                           &choice);
+  if ( ( (0 == strcmp(choice, "YES")) ||
+        ( (0 == strcmp(choice, "AUTO")) &&
+          (lan_ip_detected()) ) ) &&
+       (0 != memcmp (&coreAPI->myIdentity->hashPubKey,
+                    &hello->senderIdentity.hashPubKey,
+                    sizeof (GNUNET_HashCode))) )
     {
       /* if WE are a NAT and this is not our hello,
          it is invalid since NAT-to-NAT is not possible! */
-      if (0 == memcmp (&coreAPI->myIdentity->hashPubKey,
-                       &hello->senderIdentity.hashPubKey,
-                       sizeof (GNUNET_HashCode)))
-        return GNUNET_OK;
       return GNUNET_SYSERR;
     }
   return GNUNET_OK;
@@ -91,14 +115,21 @@
 static GNUNET_MessageHello *
 createhello ()
 {
+  const char * choice;
   GNUNET_MessageHello *msg;
 
-  if (GNUNET_NO == GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
-                                                            "NAT", "LIMITED",
-                                                            GNUNET_NO))
+  choice = "AUTO";
+  GNUNET_GC_get_configuration_value_choice (coreAPI->cfg,
+                                           "NAT", "LIMITED",
+                                           nat_limited_choices,
+                                           "AUTO",
+                                           &choice);
+  if ( ( (0 == strcmp(choice, "YES")) ||
+        ( (0 == strcmp(choice, "AUTO")) &&
+          (! lan_ip_detected()) ) ) )
     return NULL;
-  msg = GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (HostAddress));
-  msg->senderAddressSize = htons (sizeof (HostAddress));
+  msg = GNUNET_malloc (sizeof (GNUNET_MessageHello));
+  msg->senderAddressSize = htons (0);
   msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_NAT);
   msg->MTU = htonl (0);
   return msg;





reply via email to

[Prev in Thread] Current Thread [Next in Thread]