1 /* -*- mode:C; indent-tabs-mode:t; tab-width:8; c-basic-offset:8; -*- */
2 /* gnome-netinfo - A GUI Interface for network utilities
3 * Copyright (C) 2002, 2003 by German Poo-Caaman~o
4 *
5 * This program 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 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <gtk/gtk.h>
21 #include <glib/gi18n.h>
22 #include <glib/gprintf.h>
23 #include <sys/types.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #ifdef HAVE_SYS_SOCKIO_H
33 # include <sys/sockio.h>
34 #endif
35
36
37 #include <netinet/in.h>
38 #include <sys/socket.h> /* basic socket definitions */
39 #include <arpa/inet.h> /* inet(3) functions */
40 #include <sys/un.h> /* for Unix domain sockets */
41 #include <sys/ioctl.h>
42 #include <stdlib.h>
43 #include <net/if.h>
44 #ifdef __FreeBSD__
45 #include <net/if_media.h>
46 #endif
47
48 #include <glibtop.h>
49 #include <glibtop/netlist.h>
50 #include <glibtop/netload.h>
51
52 #include "info.h"
53 #include "utils.h"
54 #include "util-mii.h"
55
56 #ifndef IN6_IS_ADDR_GLOBAL
57 #define IN6_IS_ADDR_GLOBAL(a) \
58 (((((__const uint8_t *) (a))[0] & 0xff) == 0x3f \
59 || (((__const uint8_t *) (a))[0] & 0xff) == 0x20))
60 #endif
61
62 static gboolean info_nic_update_stats (gpointer data);
63 static GList *info_get_interfaces (Netinfo *info);
64
65 static InfoInterfaceDescription info_iface_desc [] = {
66 /* Interface Name Interface Type icon Device prefix Pixbuf */
67 { N_("Other type"), INFO_INTERFACE_OTHER, "network.png", "other_type", NULL },
68 { N_("Ethernet Interface"), INFO_INTERFACE_ETH, "16_ethernet.xpm", "eth", NULL },
69 { N_("Wireless Interface"), INFO_INTERFACE_WLAN, "wavelan-16.png", "wlan", NULL },
70 { N_("Modem Interface"), INFO_INTERFACE_PPP, "16_ppp.xpm", "ppp", NULL },
71 { N_("Modem Interface"), INFO_INTERFACE_PPP, "16_ppp.xpm", "tun", NULL },
72 { N_("Parallel Line Interface"), INFO_INTERFACE_PLIP, "16_plip.xpm", "plip", NULL },
73 { N_("Infrared Interface"), INFO_INTERFACE_IRLAN, "irda-16.png", "irlan", NULL },
74 { N_("Loopback Interface"), INFO_INTERFACE_LO, "16_loopback.xpm", "lo", NULL },
75 { N_("Unknown Interface"), INFO_INTERFACE_UNKNOWN, "network.png", "", NULL },
76 { NULL, INFO_INTERFACE_UNKNOWN, NULL, NULL, NULL }
77 };
78
79 void
info_do(const gchar * nic,Netinfo * info)80 info_do (const gchar * nic, Netinfo * info)
81 {
82
83 }
84
85 void
info_set_nic(Netinfo * netinfo,const gchar * nic)86 info_set_nic (Netinfo * netinfo, const gchar *nic)
87 {
88 GtkTreeModel *model;
89 GtkTreeIter iter;
90
91 g_return_if_fail (netinfo != NULL);
92
93 if (nic == NULL)
94 return;
95
96 model = gtk_combo_box_get_model (GTK_COMBO_BOX (netinfo->combo));
97 if (!gtk_tree_model_get_iter_first (model, &iter)) {
98 g_warning ("No network devices found.");
99 return;
100 }
101
102 do {
103 char *text = NULL;
104
105 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 2, &text, -1);
106 if (!text)
107 continue;
108
109 if (strcmp (text, nic) == 0) {
110 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (netinfo->combo), &iter);
111 return;
112 }
113 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter));
114 }
115
116 gchar *
info_get_nic(Netinfo * netinfo)117 info_get_nic (Netinfo * netinfo)
118 {
119 GtkTreeModel *model;
120 GtkTreeIter iter;
121 gchar *nic = NULL;
122
123 g_return_val_if_fail (netinfo != NULL, NULL);
124
125 model = gtk_combo_box_get_model (GTK_COMBO_BOX (netinfo->combo));
126
127 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (netinfo->combo), &iter))
128 gtk_tree_model_get (model, &iter, 2, &nic, -1);
129 else {
130 g_warning ("No network devices found.");
131 return NULL;
132 }
133
134 return nic;
135 }
136
137 static void
info_get_interface_from_dev_name(const gchar * dev_name,gchar ** iface,GdkPixbuf ** pixbuf)138 info_get_interface_from_dev_name (const gchar *dev_name, gchar **iface, GdkPixbuf **pixbuf)
139 {
140 gint i;
141 gchar *path;
142 gchar *dev_type = NULL;
143 #if defined(__FreeBSD__)
144 int s;
145 struct ifmediareq ifmr;
146
147 if ((s = socket (AF_INET, SOCK_DGRAM, 0)) > -1) {
148
149 (void) memset (&ifmr, 0, sizeof (ifmr));
150 (void) strncpy (ifmr.ifm_name, dev_name, sizeof (ifmr.ifm_name));
151
152 if (ioctl (s, SIOCGIFMEDIA, (caddr_t) &ifmr) > -1) {
153 switch (IFM_TYPE (ifmr.ifm_active)) {
154 case IFM_ETHER:
155 dev_type = "eth";
156 break;
157 case IFM_IEEE80211:
158 dev_type = "wlan";
159 break;
160 }
161 }
162 close (s);
163 }
164 #endif /* defined(__FreeBSD__) */
165
166 if (!dev_type)
167 dev_type = (gchar *) dev_name;
168
169 for (i = 0; info_iface_desc[i].name; i++)
170 if (strstr (dev_type, info_iface_desc[i].prefix) == dev_type) {
171 (*iface) = g_strdup_printf ("%s (%s)", _(info_iface_desc[i].name), dev_name);
172 if (info_iface_desc[i].pixbuf == NULL) {
173 path = g_build_filename (PIXMAPS_DIR, info_iface_desc[i].icon, NULL);
174 info_iface_desc[i].pixbuf = gdk_pixbuf_new_from_file (path, NULL);
175 g_free (path);
176 }
177 (*pixbuf) = info_iface_desc[i].pixbuf;
178 return;
179 }
180 }
181
182 void
info_load_iface(Netinfo * info)183 info_load_iface (Netinfo *info)
184 {
185 GtkTreeModel *model;
186 GtkTreeIter iter;
187 GtkCellRenderer *renderer;
188 GList *items = NULL;
189 GList *p;
190 GdkPixbuf *pixbuf = NULL;
191 gchar *iface = NULL;
192 gchar *text;
193
194 items = info_get_interfaces (info);
195 p = items;
196 model = gtk_combo_box_get_model (GTK_COMBO_BOX (info->combo));
197
198 if (!items) {
199 iface = g_strdup_printf ("<i>%s</i>", _("Network Devices Not Found"));
200
201 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
202 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
203 0, NULL,
204 1, iface,
205 2, (gpointer) NULL,
206 -1);
207
208 g_free (iface);
209 } else {
210 while (p) {
211 text = g_strdup (p->data);
212
213 info_get_interface_from_dev_name (text, &iface, &pixbuf);
214
215 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
216 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
217 0, pixbuf,
218 1, iface,
219 2, (gpointer) text,
220 -1);
221
222 g_free (iface);
223 g_object_unref (pixbuf);
224
225 p = g_list_next (p);
226 }
227
228 g_list_free (items);
229 }
230
231 gtk_cell_layout_clear (GTK_CELL_LAYOUT (info->combo));
232
233 renderer = gtk_cell_renderer_pixbuf_new ();
234 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (info->combo), renderer, TRUE);
235 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (info->combo), renderer,
236 "pixbuf", 0, NULL);
237
238 renderer = gtk_cell_renderer_text_new ();
239 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (info->combo), renderer, TRUE);
240 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (info->combo), renderer,
241 "markup", 1, NULL);
242
243 gtk_combo_box_set_active (GTK_COMBO_BOX (info->combo), 0);
244 }
245
246 static gboolean
info_nic_update_stats(gpointer data)247 info_nic_update_stats (gpointer data)
248 {
249 Netinfo *info = data;
250 gchar rx_pkt[10], rx_error[10];
251 gchar tx_pkt[10], tx_error[10];
252 gchar collisions[10];
253 const gchar *nic;
254 gchar *text_tx_bytes, *text_rx_bytes;
255
256 glibtop_netload netload;
257
258 g_return_val_if_fail (info != NULL, FALSE);
259
260 nic = info_get_nic (info);
261 if (!nic)
262 return FALSE;
263
264 glibtop_get_netload (&netload, nic);
265
266 text_rx_bytes = util_legible_bytes (netload.bytes_in);
267 text_tx_bytes = util_legible_bytes (netload.bytes_out);
268
269 g_sprintf (rx_pkt, "%" G_GUINT64_FORMAT, netload.packets_in);
270 g_sprintf (tx_pkt, "%" G_GUINT64_FORMAT, netload.packets_out);
271
272 g_sprintf (rx_error, "%" G_GUINT64_FORMAT, netload.errors_in);
273 g_sprintf (tx_error, "%" G_GUINT64_FORMAT, netload.errors_out);
274
275 g_sprintf (collisions, "%" G_GUINT64_FORMAT, netload.collisions);
276
277 gtk_label_set_text (GTK_LABEL (info->tx_bytes), text_tx_bytes);
278 gtk_label_set_text (GTK_LABEL (info->tx), tx_pkt);
279 gtk_label_set_text (GTK_LABEL (info->tx_errors), tx_error);
280 gtk_label_set_text (GTK_LABEL (info->rx_bytes), text_rx_bytes);
281 gtk_label_set_text (GTK_LABEL (info->rx), rx_pkt);
282 gtk_label_set_text (GTK_LABEL (info->rx_errors), rx_error);
283 gtk_label_set_text (GTK_LABEL (info->collisions), collisions);
284
285 g_free (text_tx_bytes);
286 g_free (text_rx_bytes);
287
288 return TRUE;
289 }
290
291 void
info_nic_changed(GtkWidget * combo,gpointer data)292 info_nic_changed (GtkWidget *combo, gpointer data)
293 {
294 gchar *text = NULL;
295 Netinfo *info = data;
296 GtkTreeModel *model;
297
298 static gint timeout_source = 0;
299
300 g_return_if_fail (info != NULL);
301
302 model = gtk_tree_view_get_model (GTK_TREE_VIEW (info->list_ip_addr));
303 if (model)
304 gtk_list_store_clear (GTK_LIST_STORE (model));
305
306 text = info_get_nic (info);
307 if (!text)
308 return;
309
310 /* Fill the NIC configuration data */
311 info_get_nic_information (text, info);
312 info_nic_update_stats (info);
313
314 if (timeout_source > 0) {
315 g_source_remove (timeout_source);
316 }
317
318 timeout_source = g_timeout_add (DELAY_STATS, info_nic_update_stats, info);
319 }
320
321 static gint
info_ip6_masklen(guint8 * netmask)322 info_ip6_masklen (guint8 *netmask)
323 {
324 gint len = 0;
325 guchar val;
326 guchar *pnt;
327
328 pnt = (guchar *) netmask;
329
330 while ((*pnt == 0xff) && len < 128) {
331 len += 8;
332 pnt ++;
333 }
334
335 if (len < 128) {
336 val = *pnt;
337 while (val) {
338 len++;
339 val <<= 1;
340 }
341 }
342
343 return len;
344 }
345
346 typedef struct {
347 gchar *ip_addr;
348 gchar *ip_prefix;
349 gchar *ip_bcast;
350 gchar *ip_scope;
351 } InfoIpAddr;
352
353 static void
info_ip_addr_free(InfoIpAddr * ip)354 info_ip_addr_free (InfoIpAddr *ip)
355 {
356 g_free (ip->ip_addr);
357 g_free (ip->ip_prefix);
358 g_free (ip->ip_bcast);
359 g_free (ip->ip_scope);
360 g_free (ip);
361 }
362
363 static void
info_setup_configure_button(Netinfo * info,gboolean enable)364 info_setup_configure_button (Netinfo *info, gboolean enable)
365 {
366 gchar *network_tool_path;
367
368 network_tool_path = util_find_program_in_path ("nm-connection-editor", NULL);
369 if (!network_tool_path)
370 network_tool_path = util_find_program_in_path ("network-admin", NULL);
371
372 if (!network_tool_path)
373 gtk_widget_hide (info->configure_button);
374 else {
375 gtk_widget_show (info->configure_button);
376 gtk_widget_set_sensitive (info->configure_button, enable);
377
378 g_free (network_tool_path);
379 }
380 }
381
382 void
info_get_nic_information(const gchar * nic,Netinfo * info)383 info_get_nic_information (const gchar *nic, Netinfo *info)
384 {
385 GtkTreeModel *model;
386 GtkTreeIter iter;
387 gchar *dst;
388 InfoIpAddr *ip;
389 gint prefix;
390 struct in_addr addr, subnet;
391 gchar *address_string, *subnet_string;
392 gchar address6_string[INET6_ADDRSTRLEN];
393 glibtop_netload netload;
394 #ifdef __linux__
395 mii_data_result data;
396 #endif
397
398 gtk_label_set_text (GTK_LABEL (info->hw_address), NOT_AVAILABLE);
399 gtk_label_set_text (GTK_LABEL (info->mtu), NOT_AVAILABLE);
400 gtk_label_set_text (GTK_LABEL (info->state), NOT_AVAILABLE);
401 gtk_label_set_text (GTK_LABEL (info->multicast), NOT_AVAILABLE);
402 gtk_label_set_text (GTK_LABEL (info->link_speed), NOT_AVAILABLE);
403
404 glibtop_get_netload (&netload, nic);
405
406 /* IPv6 */
407 /* FIXME: It shows only one IPv6 address. Bug #563768 */
408 inet_ntop (AF_INET6, netload.address6, address6_string, INET6_ADDRSTRLEN);
409 prefix = info_ip6_masklen (netload.prefix6);
410
411 ip = g_new0 (InfoIpAddr, 1);
412 ip->ip_addr = g_strdup (address6_string);
413 ip->ip_prefix = g_strdup_printf ("%d", prefix);
414 ip->ip_bcast = g_strdup ("");
415
416 switch (netload.scope6) {
417 case GLIBTOP_IF_IN6_SCOPE_LINK:
418 ip->ip_scope = g_strdup ("Link");
419 break;
420 case GLIBTOP_IF_IN6_SCOPE_SITE:
421 ip->ip_scope = g_strdup ("Site");
422 break;
423 case GLIBTOP_IF_IN6_SCOPE_GLOBAL:
424 ip->ip_scope = g_strdup ("Global");
425 break;
426 case GLIBTOP_IF_IN6_SCOPE_HOST:
427 ip->ip_scope = g_strdup ("Host");
428 break;
429 case GLIBTOP_IF_IN6_SCOPE_UNKNOWN:
430 ip->ip_scope = g_strdup (_("Unknown"));
431 break;
432 default:
433 ip->ip_scope = g_strdup (_("Unknown"));
434 break;
435 }
436
437 model = gtk_tree_view_get_model (GTK_TREE_VIEW (info->list_ip_addr));
438
439 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
440 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
441 0, "IPv6",
442 1, ip->ip_addr,
443 2, ip->ip_prefix,
444 3, ip->ip_bcast,
445 4, ip->ip_scope,
446 -1);
447 info_ip_addr_free (ip);
448
449 /* IPv4 */
450 addr.s_addr = netload.address;
451 subnet.s_addr = netload.subnet;
452
453 address_string = g_strdup (inet_ntoa (addr));
454 subnet_string = g_strdup (inet_ntoa (subnet));
455
456 ip = g_new0 (InfoIpAddr, 1);
457 ip->ip_addr = g_strdup (address_string);
458 ip->ip_prefix = g_strdup (subnet_string);
459 /* FIXME: Get the broadcast address: Bug #563765 */
460 ip->ip_bcast = g_strdup ("");
461
462 model = gtk_tree_view_get_model (GTK_TREE_VIEW (info->list_ip_addr));
463
464 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
465 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
466 0, "IPv4",
467 1, ip->ip_addr,
468 2, ip->ip_prefix,
469 3, ip->ip_bcast,
470 4, "",
471 -1);
472
473 g_free (address_string);
474 g_free (subnet_string);
475 info_ip_addr_free (ip);
476
477
478 /* Get general information about the interface */
479
480 /* Get the Hardware Address */
481 if (netload.flags & (1L << GLIBTOP_NETLOAD_HWADDRESS)) {
482 dst = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x",
483 (int) ((guchar *) &netload.hwaddress)[0],
484 (int) ((guchar *) &netload.hwaddress)[1],
485 (int) ((guchar *) &netload.hwaddress)[2],
486 (int) ((guchar *) &netload.hwaddress)[3],
487 (int) ((guchar *) &netload.hwaddress)[4],
488 (int) ((guchar *) &netload.hwaddress)[5]);
489 } else {
490 dst = g_strdup_printf ("%s", NOT_AVAILABLE);
491 }
492 gtk_label_set_text (GTK_LABEL (info->hw_address), dst);
493 g_free (dst);
494
495 /* Get the interface's Maximum Transfer Unit */
496 dst = g_strdup_printf ("%d", netload.mtu);
497 gtk_label_set_text (GTK_LABEL (info->mtu), dst);
498 g_free (dst);
499
500
501 /* Get Flags to determine other properties */
502
503 /* Is the interface up? */
504 if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP)) {
505 gtk_label_set_text (GTK_LABEL (info->state), _("Active"));
506 } else {
507 gtk_label_set_text (GTK_LABEL (info->state), _("Inactive"));
508 }
509
510 /* Is this a loopback device? */
511 if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) {
512 dst = g_strdup_printf ("%s", _("Loopback"));
513 gtk_label_set_text (GTK_LABEL (info->hw_address), dst);
514 g_free (dst);
515 ip->ip_bcast = g_strdup ("");
516 info_setup_configure_button (info, FALSE);
517 } else {
518 info_setup_configure_button (info, TRUE);
519 }
520
521 /* Does this interface supports multicast? */
522 if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_MULTICAST)) {
523 gtk_label_set_text (GTK_LABEL (info->multicast), _("Enabled"));
524 } else {
525 gtk_label_set_text (GTK_LABEL (info->multicast), _("Disabled"));
526 }
527
528 /* Get the Point-To-Point address if any */
529 /* FIXME: Bug #563767 */
530
531 /* Get the link negotiation speed. Only available on Linux
532 * systems, and lately only for with super user privileges
533 * See Bug #387198 */
534 #ifdef __linux__
535 data = mii_get_basic (nic);
536 if (data.has_data) {
537 gtk_label_set_text (GTK_LABEL (info->link_speed), data.media);
538 }
539 #else
540 gtk_label_set_text (GTK_LABEL (info->link_speed), NOT_AVAILABLE);
541 #endif
542 }
543
544 static gint *
compare(gconstpointer a,gconstpointer b)545 compare (gconstpointer a, gconstpointer b)
546 {
547 return (GINT_TO_POINTER (strcmp (a, b)));
548 }
549
550 static GList *
info_get_interfaces(Netinfo * info)551 info_get_interfaces (Netinfo *info)
552 {
553 GList *items = NULL;
554 glibtop_netlist netlist;
555 gchar **devices;
556 gchar *iface;
557 guint i;
558
559 devices = glibtop_get_netlist(&netlist);
560
561 for(i = 0; i < netlist.number; ++i) {
562 iface = g_strdup (devices[i]);
563 if (g_list_find_custom (items, iface,
564 (GCompareFunc) compare) == NULL) {
565 items = g_list_append (items, iface);
566 }
567 }
568
569 g_strfreev(devices);
570
571 return items;
572 }
573
574 /* Copy on clipboard */
575 void
info_copy_to_clipboard(Netinfo * netinfo,gpointer user_data)576 info_copy_to_clipboard (Netinfo * netinfo, gpointer user_data)
577 {
578 GString *result;
579 const gchar *nic;
580 const gchar *hw_address;
581 const gchar *multicast;
582 const gchar *link_speed;
583 const gchar *state;
584 const gchar *mtu;
585 const gchar *tx;
586 const gchar *tx_errors;
587 const gchar *rx;
588 const gchar *rx_errors;
589 const gchar *collisions;
590
591 g_return_if_fail (netinfo != NULL);
592
593 /* The info output in text format:
594 Bytes received, Address Source, Number of Sequence,
595 Round Trip Time (Time), Units of Time.
596 It's a tabular output, and these belongs to the column titles */
597 result = g_string_new ("");
598
599 nic = info_get_nic (netinfo);
600
601 hw_address = gtk_label_get_text (GTK_LABEL (netinfo->hw_address));
602 multicast = gtk_label_get_text (GTK_LABEL (netinfo->multicast));
603 mtu = gtk_label_get_text (GTK_LABEL (netinfo->mtu));
604 link_speed = gtk_label_get_text (GTK_LABEL (netinfo->link_speed));
605 state = gtk_label_get_text (GTK_LABEL (netinfo->state));
606
607 tx = gtk_label_get_text (GTK_LABEL (netinfo->tx));
608 rx = gtk_label_get_text (GTK_LABEL (netinfo->rx));
609 tx_errors = gtk_label_get_text (GTK_LABEL (netinfo->tx_errors));
610 rx_errors = gtk_label_get_text (GTK_LABEL (netinfo->rx_errors));
611 collisions = gtk_label_get_text (GTK_LABEL (netinfo->collisions));
612
613 /* The info output in a text format (to copy on clipboard) */
614 g_string_append_printf (result, _("Network device:\t%s\n"), nic);
615 g_string_append_printf (result, _("Hardware address:\t%s\n"), hw_address);
616 g_string_append_printf (result, _("Multicast:\t%s\n"), multicast);
617 g_string_append_printf (result, _("MTU:\t%s\n"), mtu);
618 g_string_append_printf (result, _("Link speed:\t%s\n"), link_speed);
619 g_string_append_printf (result, _("State:\t%s\n"), state);
620
621 g_string_append_printf (result, _("Transmitted packets:\t%s\n"), tx);
622 g_string_append_printf (result, _("Transmission errors:\t%s\n"), tx_errors);
623 g_string_append_printf (result, _("Received packets:\t%s\n"), rx);
624 g_string_append_printf (result, _("Reception errors:\t%s\n"), rx_errors);
625 g_string_append_printf (result, _("Collisions:\t%s\n"), collisions);
626
627
628 gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), result->str,
629 result->len);
630
631 g_string_free (result, TRUE);
632 }
633