clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator,
                                            gpointer                native,
                                            ClutterEvent           *event)
{
  ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator);
  ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE;
  ClutterBackendX11 *backend_x11;
  ClutterStageX11 *stage_x11 = NULL;
  ClutterStage *stage = NULL;
  ClutterInputDevice *device, *source_device;
  XGenericEventCookie *cookie;
  XIEvent *xi_event;
  XEvent *xevent;

  backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());

  xevent = native;

  cookie = &xevent->xcookie;

  if (cookie->type != GenericEvent ||
      cookie->extension != manager_xi2->opcode)
    return CLUTTER_TRANSLATE_CONTINUE;

  xi_event = (XIEvent *) cookie->data;

  if (!xi_event)
    return CLUTTER_TRANSLATE_REMOVE;

  if (!(xi_event->evtype == XI_HierarchyChanged ||
        xi_event->evtype == XI_DeviceChanged))
    {
      stage = get_event_stage (translator, xi_event);
      if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
        return CLUTTER_TRANSLATE_CONTINUE;
      else
        stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
    }

  event->any.stage = stage;

  switch (xi_event->evtype)
    {
    case XI_HierarchyChanged:
      {
        XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;

        translate_hierarchy_event (backend_x11, manager_xi2, xev);
      }
      retval = CLUTTER_TRANSLATE_REMOVE;
      break;

    case XI_DeviceChanged:
      {
        XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;

        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));
        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));
        if (device)
          {
            _clutter_input_device_reset_axes (device);
            translate_device_classes (backend_x11->xdpy,
                                      device,
                                      xev->classes,
                                      xev->num_classes);
          }

        if (source_device)
          _clutter_input_device_reset_scroll_info (source_device);
      }
      retval = CLUTTER_TRANSLATE_REMOVE;
      break;

    case XI_KeyPress:
    case XI_KeyRelease:
      {
        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
        ClutterEventX11 *event_x11;
        char buffer[7] = { 0, };
        gunichar n;

        event->key.type = event->type = (xev->evtype == XI_KeyPress)
                                      ? CLUTTER_KEY_PRESS
                                      : CLUTTER_KEY_RELEASE;

        event->key.time = xev->time;
        event->key.stage = stage;
        event->key.modifier_state =
          _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
        event->key.hardware_keycode = xev->detail;

          /* keyval is the key ignoring all modifiers ('1' vs. '!') */
        event->key.keyval =
          _clutter_keymap_x11_translate_key_state (backend_x11->keymap,
                                                   event->key.hardware_keycode,
                                                   &event->key.modifier_state,
                                                   NULL);

        /* KeyEvents have platform specific data associated to them */
        event_x11 = _clutter_event_x11_new ();
        _clutter_event_set_platform_data (event, event_x11);

        event_x11->key_group =
          _clutter_keymap_x11_get_key_group (backend_x11->keymap,
                                             event->key.modifier_state);
        event_x11->key_is_modifier =
          _clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
                                               event->key.hardware_keycode);
        event_x11->num_lock_set =
          _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
        event_x11->caps_lock_set =
          _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));
        clutter_event_set_source_device (event, source_device);

        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));
        clutter_event_set_device (event, device);

        /* XXX keep this in sync with the evdev device manager */
        n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
        if (n == 0)
          {
            /* not printable */
            event->key.unicode_value = (gunichar) '\0';
          }
        else
          {
            event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
            if (event->key.unicode_value == -1 ||
                event->key.unicode_value == -2)
              event->key.unicode_value = (gunichar) '\0';
          }

        CLUTTER_NOTE (EVENT,
                      "%s: win:0x%x device:%d source:%d, key: %12s (%d)",
                      event->any.type == CLUTTER_KEY_PRESS
                        ? "key press  "
                        : "key release",
                      (unsigned int) stage_x11->xwin,
                      xev->deviceid,
                      xev->sourceid,
                      event->key.keyval ? buffer : "(none)",
                      event->key.keyval);

        if (xi_event->evtype == XI_KeyPress)
          _clutter_stage_x11_set_user_time (stage_x11, event->key.time);

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;

    case XI_ButtonPress:
    case XI_ButtonRelease:
      {
        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));
        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));

        /* Set the stage for core events coming out of nowhere (see bug #684509) */
        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
            clutter_input_device_get_pointer_stage (device) == NULL &&
            stage != NULL)
          _clutter_input_device_set_stage (device, stage);

        switch (xev->detail)
          {
          case 4:
          case 5:
          case 6:
          case 7:
            /* we only generate Scroll events on ButtonPress */
            if (xi_event->evtype == XI_ButtonRelease)
              return CLUTTER_TRANSLATE_REMOVE;

            event->scroll.type = event->type = CLUTTER_SCROLL;

            if (xev->detail == 4)
              event->scroll.direction = CLUTTER_SCROLL_UP;
            else if (xev->detail == 5)
              event->scroll.direction = CLUTTER_SCROLL_DOWN;
            else if (xev->detail == 6)
              event->scroll.direction = CLUTTER_SCROLL_LEFT;
            else
              event->scroll.direction = CLUTTER_SCROLL_RIGHT;

            event->scroll.stage = stage;

            event->scroll.time = xev->time;
            event->scroll.x = xev->event_x;
            event->scroll.y = xev->event_y;
            event->scroll.modifier_state =
              _clutter_input_device_xi2_translate_state (&xev->mods,
                                                         &xev->buttons,
                                                         &xev->group);

            clutter_event_set_source_device (event, source_device);
            clutter_event_set_device (event, device);

            event->scroll.axes = translate_axes (event->scroll.device,
                                                 event->scroll.x,
                                                 event->scroll.y,
                                                 stage_x11,
                                                 &xev->valuators);

            CLUTTER_NOTE (EVENT,
                          "scroll: win:0x%x, device:%d '%s', time:%d "
                          "(direction:%s, "
                          "x:%.2f, y:%.2f, "
                          "emulated:%s)",
                          (unsigned int) stage_x11->xwin,
                          device->id,
                          device->device_name,
                          event->any.time,
                          event->scroll.direction == CLUTTER_SCROLL_UP ? "up" :
                          event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" :
                          event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" :
                          event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" :
                          "invalid",
                          event->scroll.x,
                          event->scroll.y,
#ifdef HAVE_XINPUT_2_2
                          (xev->flags & XIPointerEmulated) ? "yes" : "no"
#else
                          "no"
#endif
                          );
            break;

          default:
            event->button.type = event->type =
              (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
                                                   : CLUTTER_BUTTON_RELEASE;

            event->button.stage = stage;

            event->button.time = xev->time;
            event->button.x = xev->event_x;
            event->button.y = xev->event_y;
            event->button.button = xev->detail;
            event->button.modifier_state =
              _clutter_input_device_xi2_translate_state (&xev->mods,
                                                         &xev->buttons,
                                                         &xev->group);

            clutter_event_set_source_device (event, source_device);
            clutter_event_set_device (event, device);

            event->button.axes = translate_axes (event->button.device,
                                                 event->button.x,
                                                 event->button.y,
                                                 stage_x11,
                                                 &xev->valuators);

            CLUTTER_NOTE (EVENT,
                          "%s: win:0x%x, device:%d '%s', time:%d "
                          "(button:%d, "
                          "x:%.2f, y:%.2f, "
                          "axes:%s, "
                          "emulated:%s)",
                          event->any.type == CLUTTER_BUTTON_PRESS
                            ? "button press  "
                            : "button release",
                          (unsigned int) stage_x11->xwin,
                          device->id,
                          device->device_name,
                          event->any.time,
                          event->button.button,
                          event->button.x,
                          event->button.y,
                          event->button.axes != NULL ? "yes" : "no",
#ifdef HAVE_XINPUT_2_2
                          (xev->flags & XIPointerEmulated) ? "yes" : "no"
#else
                          "no"
#endif
                          );
            break;
          }

        if (source_device != NULL && device->stage != NULL)
          _clutter_input_device_set_stage (source_device, device->stage);

#ifdef HAVE_XINPUT_2_2
        if (xev->flags & XIPointerEmulated)
          _clutter_event_set_pointer_emulated (event, TRUE);
#endif /* HAVE_XINPUT_2_2 */

        if (xi_event->evtype == XI_ButtonPress)
          _clutter_stage_x11_set_user_time (stage_x11, event->button.time);

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;

    case XI_Motion:
      {
        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
        gdouble delta_x, delta_y;

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));
        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));

        /* Set the stage for core events coming out of nowhere (see bug #684509) */
        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
            clutter_input_device_get_pointer_stage (device) == NULL &&
            stage != NULL)
          _clutter_input_device_set_stage (device, stage);

        if (scroll_valuators_changed (source_device,
                                      &xev->valuators,
                                      &delta_x, &delta_y))
          {
            event->scroll.type = event->type = CLUTTER_SCROLL;
            event->scroll.direction = CLUTTER_SCROLL_SMOOTH;

            event->scroll.stage = stage;
            event->scroll.time = xev->time;
            event->scroll.x = xev->event_x;
            event->scroll.y = xev->event_y;
            event->scroll.modifier_state =
              _clutter_input_device_xi2_translate_state (&xev->mods,
                                                         &xev->buttons,
                                                         &xev->group);

            clutter_event_set_scroll_delta (event, delta_x, delta_y);
            clutter_event_set_source_device (event, source_device);
            clutter_event_set_device (event, device);

            CLUTTER_NOTE (EVENT,
                          "smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
                          (unsigned int) stage_x11->xwin,
                          event->scroll.device->id,
                          event->scroll.device->device_name,
                          event->scroll.x,
                          event->scroll.y,
                          delta_x, delta_y);

            retval = CLUTTER_TRANSLATE_QUEUE;
            break;
          }

        event->motion.type = event->type = CLUTTER_MOTION;

        event->motion.stage = stage;

        event->motion.time = xev->time;
        event->motion.x = xev->event_x;
        event->motion.y = xev->event_y;
        event->motion.modifier_state =
          _clutter_input_device_xi2_translate_state (&xev->mods,
                                                     &xev->buttons,
                                                     &xev->group);

        clutter_event_set_source_device (event, source_device);
        clutter_event_set_device (event, device);

        event->motion.axes = translate_axes (event->motion.device,
                                             event->motion.x,
                                             event->motion.y,
                                             stage_x11,
                                             &xev->valuators);

        if (source_device != NULL && device->stage != NULL)
          _clutter_input_device_set_stage (source_device, device->stage);

#ifdef HAVE_XINPUT_2_2
        if (xev->flags & XIPointerEmulated)
          _clutter_event_set_pointer_emulated (event, TRUE);
#endif /* HAVE_XINPUT_2_2 */

        CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
                      (unsigned int) stage_x11->xwin,
                      event->motion.device->id,
                      event->motion.device->device_name,
                      event->motion.x,
                      event->motion.y,
                      event->motion.axes != NULL ? "yes" : "no");

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;

#ifdef HAVE_XINPUT_2_2
    case XI_TouchBegin:
    case XI_TouchEnd:
      {
        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));

        if (xi_event->evtype == XI_TouchBegin)
          event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
        else
          event->touch.type = event->type = CLUTTER_TOUCH_END;

        event->touch.stage = stage;
        event->touch.time = xev->time;
        event->touch.x = xev->event_x;
        event->touch.y = xev->event_y;
        event->touch.modifier_state =
          _clutter_input_device_xi2_translate_state (&xev->mods,
                                                     &xev->buttons,
                                                     &xev->group);

        clutter_event_set_source_device (event, source_device);

        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));
        clutter_event_set_device (event, device);

        event->touch.axes = translate_axes (event->touch.device,
                                            event->motion.x,
                                            event->motion.y,
                                            stage_x11,
                                            &xev->valuators);

        if (xi_event->evtype == XI_TouchBegin)
          {
            event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;

            _clutter_stage_x11_set_user_time (stage_x11, event->touch.time);
          }

        event->touch.sequence = GUINT_TO_POINTER (xev->detail);

        if (xev->flags & XITouchEmulatingPointer)
          _clutter_event_set_pointer_emulated (event, TRUE);

        CLUTTER_NOTE (EVENT, "touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
                      event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
                      (unsigned int) stage_x11->xwin,
                      event->touch.device->id,
                      event->touch.device->device_name,
                      GPOINTER_TO_UINT (event->touch.sequence),
                      event->touch.x,
                      event->touch.y,
                      event->touch.axes != NULL ? "yes" : "no");

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;

    case XI_TouchUpdate:
      {
        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));

        event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
        event->touch.stage = stage;
        event->touch.time = xev->time;
        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
        event->touch.x = xev->event_x;
        event->touch.y = xev->event_y;

        clutter_event_set_source_device (event, source_device);

        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));
        clutter_event_set_device (event, device);

        event->touch.axes = translate_axes (event->touch.device,
                                            event->motion.x,
                                            event->motion.y,
                                            stage_x11,
                                            &xev->valuators);

        event->touch.modifier_state =
          _clutter_input_device_xi2_translate_state (&xev->mods,
                                                     &xev->buttons,
                                                     &xev->group);
        event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;

        if (xev->flags & XITouchEmulatingPointer)
          _clutter_event_set_pointer_emulated (event, TRUE);

        CLUTTER_NOTE (EVENT, "touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
                      (unsigned int) stage_x11->xwin,
                      event->touch.device->id,
                      event->touch.device->device_name,
                      GPOINTER_TO_UINT (event->touch.sequence),
                      event->touch.x,
                      event->touch.y,
                      event->touch.axes != NULL ? "yes" : "no");

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;
#endif /* HAVE_XINPUT_2_2 */

    case XI_Enter:
    case XI_Leave:
      {
        XIEnterEvent *xev = (XIEnterEvent *) xi_event;

        device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                      GINT_TO_POINTER (xev->deviceid));

        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                             GINT_TO_POINTER (xev->sourceid));

        if (xi_event->evtype == XI_Enter)
          {
            event->crossing.type = event->type = CLUTTER_ENTER;

            event->crossing.stage = stage;
            event->crossing.source = CLUTTER_ACTOR (stage);
            event->crossing.related = NULL;

            event->crossing.time = xev->time;
            event->crossing.x = xev->event_x;
            event->crossing.y = xev->event_y;

            _clutter_input_device_set_stage (device, stage);
          }
        else
          {
            if (device->stage == NULL)
              {
                CLUTTER_NOTE (EVENT,
                              "Discarding Leave for ButtonRelease "
                              "event off-stage");

                retval = CLUTTER_TRANSLATE_REMOVE;
                break;
              }

            event->crossing.type = event->type = CLUTTER_LEAVE;

            event->crossing.stage = stage;
            event->crossing.source = CLUTTER_ACTOR (stage);
            event->crossing.related = NULL;

            event->crossing.time = xev->time;
            event->crossing.x = xev->event_x;
            event->crossing.y = xev->event_y;

            _clutter_input_device_set_stage (device, NULL);
          }

        _clutter_input_device_reset_scroll_info (source_device);

        clutter_event_set_device (event, device);
        clutter_event_set_source_device (event, source_device);

        retval = CLUTTER_TRANSLATE_QUEUE;
      }
      break;

    case XI_FocusIn:
    case XI_FocusOut:
      retval = CLUTTER_TRANSLATE_CONTINUE;
      break;
    }

  return retval;
}


x11: trap errors when calling XIQueryDevice

Devices can disappear at any time, causing XIQueryDevice
to throw an error. At the same time, plug a memory leak.

https://bugzilla.gnome.org/show_bug.cgi?id=701974

translate_device_classes (Display             *xdisplay,
                          ClutterInputDevice  *device,
                          XIAnyClassInfo     **classes,
                          guint                n_classes)
{
  gint i;

  for (i = 0; i < n_classes; i++)
    {
      XIAnyClassInfo *class_info = classes[i];

      switch (class_info->type)
        {
        case XIKeyClass:
          {
            XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
            gint j;

            _clutter_input_device_set_n_keys (device,
                                              key_info->num_keycodes);

            for (j = 0; j < key_info->num_keycodes; j++)
              {
                clutter_input_device_set_key (device, j,
                                              key_info->keycodes[i],
                                              0);
              }
          }
          break;

        case XIValuatorClass:
          translate_valuator_class (xdisplay, device,
                                    (XIValuatorClassInfo *) class_info);
          break;

#ifdef HAVE_XINPUT_2_2
        case XIScrollClass:
          {
            XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
            ClutterScrollDirection direction;

            if (scroll_info->scroll_type == XIScrollTypeVertical)
              direction = CLUTTER_SCROLL_DOWN;
            else
              direction = CLUTTER_SCROLL_RIGHT;

            CLUTTER_NOTE (BACKEND, "Scroll valuator %d: %s, increment: %f",
                          scroll_info->number,
                          scroll_info->scroll_type == XIScrollTypeVertical
                            ? "vertical"
                            : "horizontal",
                          scroll_info->increment);

            _clutter_input_device_add_scroll_info (device,
                                                   scroll_info->number,
                                                   direction,
                                                   scroll_info->increment);
          }
          break;
#endif /* HAVE_XINPUT_2_2 */

        default:
          break;
        }
    }
}

The translate_hierarchy_event function in x11/clutter-device-manager-xi2.c in Clutter, when resuming the system, does not properly handle XIQueryDevice errors when a device has "disappeared," which causes the gnome-shell to crash and allows physically proximate attackers to access the previous gnome-shell session via unspecified vectors.


remove_device (ClutterDeviceManagerXI2 *manager_xi2,
               gint                     device_id)
{
  ClutterInputDevice *device;

  device = g_hash_table_lookup (manager_xi2->devices_by_id,
                                GINT_TO_POINTER (device_id));

  if (device != NULL)
    {
      manager_xi2->master_devices =
        g_list_remove (manager_xi2->master_devices, device);
      manager_xi2->slave_devices =
        g_list_remove (manager_xi2->slave_devices, device);

      /* blow the cache */
      g_slist_free (manager_xi2->all_devices);
      manager_xi2->all_devices = NULL;

      g_signal_emit_by_name (manager_xi2, "device-removed", device);

      g_object_run_dispose (G_OBJECT (device));

      g_hash_table_remove (manager_xi2->devices_by_id,
                           GINT_TO_POINTER (device_id));
    }
}


ms_escher_read_DgContainer (MSEscherState *state, MSEscherHeader *h)
{
	return ms_escher_read_container (state, h, 0, FALSE);
}

Heap-based buffer overflow in the ms_escher_get_data function in plugins/excel/ms-escher.c in GNOME Office Gnumeric before 1.12.9 allows remote attackers to cause a denial of service (crash) via a crafted xls file with a crafted length value.


ms_escher_header_release (MSEscherHeader *h)
{
	if (h->attrs != NULL) {
		if (h->release_attrs)
			ms_obj_attr_bag_destroy (h->attrs);
		h->attrs = NULL;
	}
}


ms_escher_header_init (MSEscherHeader *h)
{
	h->ver = h->instance = h->fbt = h->len = 0;
	h->attrs = NULL;
	h->release_attrs = TRUE;
}


ms_escher_spcontainer_start (GString *buf)
{
	gsize res = buf->len;
	guint8 tmp[8] = { 0xf, 0, 4, 0xf0, 0xde, 0xad, 0xbe, 0xef };
	g_string_append_len (buf, tmp, sizeof tmp);
	return res;
}


ms_escher_read_SolverContainer (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_header_add_attr (MSEscherHeader *h, MSObjAttr *attr)
{
	if (h->attrs == NULL)
		h->attrs = ms_obj_attr_bag_new ();
	ms_obj_attr_bag_insert (h->attrs, attr);
}


ms_escher_read_ChildAnchor (MSEscherState *state, MSEscherHeader *h)
{
	gboolean needs_free;
	int len = h->len - COMMON_HEADER_LEN;
	guint8 const *data = ms_escher_get_data (state,
		h->offset + COMMON_HEADER_LEN, len, &needs_free);

	/* Stored as absolute pixels in fixed point for pre-grouped position
	 * What we do not know is where the parent group stores the offset from
	 * original to current */
	d (1, {
		g_print ("ChildAnchor");
		gsf_mem_dump (data, len);});

	if (needs_free)
		g_free ((guint8 *)data);
	return FALSE;
}


ms_escher_read_SplitMenuColors (MSEscherState *state, MSEscherHeader *h)
{
	gboolean needs_free;
	guint8 const * data;

	g_return_val_if_fail (h->instance == 4, TRUE);
	g_return_val_if_fail (h->len == 24, TRUE); /*header + 4*4 */

	if ((data = ms_escher_get_data (state, h->offset + COMMON_HEADER_LEN,
					16, &needs_free))) {
		guint32 const top_level_fill = GSF_LE_GET_GUINT32(data + 0);
		guint32 const line	= GSF_LE_GET_GUINT32(data + 4);
		guint32 const shadow	= GSF_LE_GET_GUINT32(data + 8);
		guint32 const threeD	= GSF_LE_GET_GUINT32(data + 12);

		d (0, g_printerr ("top_level_fill = 0x%x;\nline = 0x%x;\nshadow = 0x%x;\nthreeD = 0x%x;\n",
			      top_level_fill, line, shadow, threeD););
	} else
		return TRUE;
	return FALSE;
}


ms_escher_opt_add_color (GString *buf, gsize marker,
			 guint16 pid, GOColor c)
{
	guint8 r = GO_COLOR_UINT_R (c);
	guint8 g = GO_COLOR_UINT_G (c);
	guint8 b = GO_COLOR_UINT_B (c);
	guint32 val = r | (g << 8) | (b << 16);
	ms_escher_opt_add_simple (buf, marker, pid, val);
}


ms_escher_read_ColorScheme (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_Dg (MSEscherState *state, MSEscherHeader *h)
{
#if 0
	guint8 const *data = h->data + COMMON_HEADER_LEN;
	guint32 num_shapes = GSF_LE_GET_GUINT32(data);
	/* spid_cur = last SPID given to an SP in this DG :-)  */
	guint32 spid_cur   = GSF_LE_GET_GUINT32(data+4);
	guint32 drawing_id = h->instance;

	/* This drawing has these num_shapes shapes, with a pointer to the last
	 * SPID given to it */
#endif
	return FALSE;
}


ms_escher_read_Spgr (MSEscherState *state, MSEscherHeader *h)
{
	gboolean needs_free;
	int len = h->len - COMMON_HEADER_LEN;
	guint8 const *data = ms_escher_get_data (state,
		h->offset + COMMON_HEADER_LEN, len, &needs_free);

	/* Stored as absolute pixels in fixed point for pre-grouped position
	 * What we do not know is where the parent group stores the offset from
	 * original to current */
	d (1, {
		g_print ("SPGR\t");
		gsf_mem_dump (data, len);});

	if (needs_free)
		g_free ((guint8 *)data);
	return FALSE;
}


ms_escher_read_OPT (MSEscherState *state, MSEscherHeader *h)
{
	int const num_properties = h->instance;
	gboolean needs_free;
	guint8 const *data = ms_escher_get_data (state,
		h->offset + COMMON_HEADER_LEN, h->len - COMMON_HEADER_LEN, &needs_free);
	guint8 const *fopte = data;
	guint8 const *extra = fopte + 6*num_properties;
	guint prev_pid = 0; /* A debug tool */
	char const *name;
	int i;

	/* let's be really careful */
	g_return_val_if_fail (6*num_properties + COMMON_HEADER_LEN <= h->len, TRUE);
	g_return_val_if_fail (data != NULL, TRUE);

	for (i = 0; i < num_properties; ++i, fopte += 6) {
		guint16 const tmp = GSF_LE_GET_GUINT32(fopte);
		guint const pid = tmp & 0x3fff;
		gboolean const is_blip = (tmp & 0x4000) != 0;
		gboolean const is_complex = (tmp & 0x8000) != 0;
		guint32 val = GSF_LE_GET_GUINT32 (fopte+2);
		MSObjAttrID id = MS_OBJ_ATTR_NONE;

		/* container is sorted by pid. Use this as sanity test */
		if (prev_pid >= pid) {
			g_printerr ("Pids not monotonic %d >= %d;\n", prev_pid, pid);
			if (needs_free)
				g_free ((guint8 *)data);
			return TRUE;
		}
		prev_pid = pid;

		name = NULL;
		switch (pid) {
	/* Transofrmation */
		/* 0 : fixed point: 16.16 degrees */
		case 4 : name = "long rotation"; break;

	/* Protection */
		case 119 : case 120 : case 121 : case 122 : case 123 :
		case 124 : case 125 : case 126 : case 127 :
			ms_escher_read_OPT_bools (h,
				protection_bools, G_N_ELEMENTS (protection_bools),
				pid, val);
			break;

	/* Text */
		/* 0 : id for the text, value determined by the host */
		case 128 : name = "long Txid"; break;
		/* 1/10" : margins relative to shape's inscribed text rectangle (in EMUs) */
		case 129 : name = "long dxTextLeft"; break;
		/* 1/20" :  */
		case 130 : name = "long dyTextTop"; break;
		/* 1/10" :  */
		case 131 : name = "long dxTextRight"; break;
		/* 1/20" :  */
		case 132 : name = "long dyTextBottom"; break;
		/* Square */
		case 133 : name = "WrapMode wrap_text_at_margin"; break;
		/* 0 : Text zoom/scale (used if fFitTextToShape) */
		case 134 : name = "long scaleText"; break;
		/* anchor_Top : How to anchor the text */
		case 135 : name = "AnchorType anchorText"; break;
		/* HorzN : Text flow */
		case 136 : name = "TextFlow txflTextFlow"; break;
		/* msocdir0 : Font rotation */
		case 137 : name = "RotationType cdirFont"; break;
		/* NULL : ID of the next shape (used by Word for linked textboxes) */
		case 138 : name = "MSOHSP hspNext"; break;
		/* LTR : Bi-Di Text direction */
		case 139 : name = "TextDirection txdir"; break;

		case 187 : case 188 : case 189 : case 190 : case 191 :
			ms_escher_read_OPT_bools (h,
				text_bools, G_N_ELEMENTS (text_bools),
				pid, val);
			break;

	/* GeoText */
		/* NULL : UNICODE text string */
		case 192 : name = "wchar* gtextUNICODE"; break;
		/* NULL : RTF text string */
		case 193 : name = "char* gtextRTF"; break;
		/* Center : alignment on curve */
		case 194 : name = "Alignment gtextAlign"; break;
		/* 36<<16 : default point size */
		case 195 : name = "long gtextSize"; break;
		/* 1<<16 : fixed point 16.16 */
		case 196 : name = "long gtextSpacing"; break;
		/* NULL : font family name */
		case 197 : name = "wchar* gtextFont"; break;

		case 240 : case 241 : case 242 : case 243 : case 244 :
		case 245 : case 246 : case 247 : case 248 : case 249 :
		case 250 : case 251 : case 252 : case 253 : case 254 :
		case 255 :
			ms_escher_read_OPT_bools (h,
				geotext_bools, G_N_ELEMENTS (geotext_bools),
				pid, val);
			break;

	/* Blip */
		/* 0 : 16.16 fraction times total image width or height, as appropriate. */
		case 256 : name = "fixed16_16 cropFromTop";
			   id = MS_OBJ_ATTR_BLIP_CROP_TOP;
			   break;
		case 257 : name = "fixed16_16 cropFromBottom";
			   id = MS_OBJ_ATTR_BLIP_CROP_BOTTOM;
			   break;
		case 258 : name = "fixed16_16 cropFromLeft";
			   id = MS_OBJ_ATTR_BLIP_CROP_LEFT;
			   break;
		case 259 : name = "fixed16_16 cropFromRight";
			   id = MS_OBJ_ATTR_BLIP_CROP_RIGHT;
			   break;

		/* NULL : Blip to display */
		case 260 : id = MS_OBJ_ATTR_BLIP_ID;
			   name = "Blip * pib";
			   break;

		/* NULL : Blip file name */
		case 261 : name = "wchar * pibName"; break;
		/* What are BlipFlags ? */
		/* Comment Blip flags */
		case 262 : name = "BlipType pibFlags"; break;
		/* ~0 : transparent color (none if ~0UL)  */
		case 263 : name = "long pictureTransparent"; break;
		/* 1<<16 : contrast setting */
		case 264 : name = "long pictureContrast"; break;
		/* 0 : brightness setting */
		case 265 : name = "long pictureBrightness"; break;
		/* 0 : 16.16 gamma */
		case 266 : name = "fixed16_16 pictureGamma"; break;
		/* 0 : Host-defined ID for OLE objects (usually a pointer) */
		case 267 : name = "Long pictureId"; break;
		/* undefined : Double shadow Colour */
		case 268 : name = "Colour pictureDblCrMod"; break;
		/* undefined : */
		case 269 : name = "Colour pictureFillCrMod"; break;
		/* undefined : */
		case 270 : name = "Colour pictureLineCrMod"; break;
		/* NULL : Blip to display when printing */
		case 271 : name = "Blip * pibPrint"; break;
		/* NULL : Blip file name */
		case 272 : name = "wchar * pibPrintName"; break;
		/* Comment Blip flags */
		case 273 : name = "BlipType pibPrintFlags"; break;

		case 316 : case 317 : case 318 : case 319 :
			ms_escher_read_OPT_bools (h,
				picture_bools, G_N_ELEMENTS (picture_bools),
				pid, val);
			break;

	/* Geometry */
		/* 0 : Defines the G (geometry) coordinate space. */
		case 320 : name = "long geoLeft"; break;
		/* 0 :  */
		case 321 : name = "long geoTop"; break;
		/* 21600 :  */
		case 322 : name = "long geoRight"; break;
		/* 21600 :  */
		case 323 : name = "long geoBottom"; break;
		/* msoshapeLinesClosed :  */
		case 324 : name = "ShapePath shapePath"; break;
		/* NULL : An array of points, in G units. */
		case 325 : name = "IMsoArray pVertices"; break;
		/* NULL :  */
		case 326 : name = "IMsoArray pSegmentInfo"; break;
		/* 0 : Adjustment values corresponding to the positions of the
		 * adjust handles of the shape. The number of values used and
		 * their allowable ranges vary from shape type to shape type.
		 */
		case 327 : name = "long adjustValue"; break;
		/* 0 :  */
		case 328 : name = "long adjust2Value"; break;
		/* 0 :  */
		case 329 : name = "long adjust3Value"; break;
		/* 0 :  */
		case 330 : name = "long adjust4Value"; break;
		/* 0 :  */
		case 331 : name = "long adjust5Value"; break;
		/* 0 :  */
		case 332 : name = "long adjust6Value"; break;
		/* 0 :  */
		case 333 : name = "long adjust7Value"; break;
		/* 0 :  */
		case 334 : name = "long adjust8Value"; break;
		/* 0 :  */
		case 335 : name = "long adjust9Value"; break;
		/* 0 :  */
		case 336 : name = "long adjust10Value"; break;

		case 378 : case 379 : case 380 : case 381 : case 382 :
		case 383 :
			ms_escher_read_OPT_bools (h,
				geometry_bools, G_N_ELEMENTS (geometry_bools),
				pid, val);
			break;

	/* FillStyle */
		/* Solid : Type of fill */
		case 384 : id = MS_OBJ_ATTR_FILL_TYPE;
			   name = "FillType fillType";
			   break;

		/* white : Foreground color */
		case 385 : id = MS_OBJ_ATTR_FILL_COLOR;
			   name = "Colour fillColor";
			   break;

		/* 1<<16 : Fixed 16.16 */
		case 386 : id = MS_OBJ_ATTR_FILL_ALPHA;
			   name = "long fillOpacity";
			   break;
		/* white : Background color */
		case 387 : id = MS_OBJ_ATTR_FILL_BACKGROUND;
			   name = "Colour fillBackColor";
			   break;
		/* 1<<16 : Shades only */
		case 388 : id = MS_OBJ_ATTR_FILL_BACKGROUND_ALPHA;
			   name = "long fillBackOpacity";
			   break;
		/* undefined : Modification for BW views */
		case 389 : name = "Colour fillCrMod"; break;
		/* NULL : Pattern/texture */
		case 390 : name = "IMsoBlip* fillBlip"; break;
		/* NULL : Blip file name */
		case 391 : name = "wchar* fillBlipName"; break;
		/* Comment : Blip flags */
		case 392 : name = "BlipFlags fillBlipFlags"; break;
		/* 0 : How big (A units) to make a metafile texture. */
		case 393 : name = "long fillWidth"; break;
		/* 0 :  */
		case 394 : name = "long fillHeight"; break;
		/* 0 : Fade angle - degrees in 16.16 */
		case 395 : id = MS_OBJ_ATTR_FILL_ANGLE;
			   name = "long fillAngle";
			   break;
		/* 0 : Linear shaded fill focus percent */
		case 396 : id = MS_OBJ_ATTR_FILL_FOCUS;
			   name = "long fillFocus";
			   break;
		/* 0 : Fraction 16.16 */
		case 397 : name = "long fillToLeft"; break;
		/* 0 : Fraction 16.16 */
		case 398 : name = "long fillToTop"; break;
		/* 0 : Fraction 16.16 */
		case 399 : name = "long fillToRight"; break;
		/* 0 : Fraction 16.16 */
		case 400 : name = "long fillToBottom"; break;
		/* 0 : For shaded fills, use the specified rectangle instead of
		 * the shape's bounding rect to define how large the fade is
		 * going to be. */
		case 401 : name = "long fillRectLeft"; break;
		/* 0 :  */
		case 402 : name = "long fillRectTop"; break;
		/* 0 :  */
		case 403 : name = "long fillRectRight"; break;
		/* 0 :  */
		case 404 : name = "long fillRectBottom"; break;
		/* measure_Default :  */
		case 405 : name = "LengthMeasure fillDztype"; break;
		/* 0 : Special shades */
		case 406 : id = MS_OBJ_ATTR_FILL_PRESET;
			   name = "long fillShadePreset";
			   break;
		/* NULL : a preset array of colors */
		case 407 : name = "IMsoArray fillShadeColors"; break;
		/* 0 :  */
		case 408 : name = "long fillOriginX"; break;
		/* 0 :  */
		case 409 : name = "long fillOriginY"; break;
		/* 0 :  */
		case 410 : name = "long fillShapeOriginX"; break;
		/* 0 :  */
		case 411 : name = "long fillShapeOriginY"; break;
		/* shade_Default : Type of shading, if a shaded (gradient) fill. */
		case 412 : id = MS_OBJ_ATTR_FILL_SHADE_TYPE;
			   name = "ShadeType fillShadeType";
			   break;

		case 443 : case 444 : case 445 : case 446 : case 447 :
			ms_escher_read_OPT_bools (h,
				fill_bools, G_N_ELEMENTS (fill_bools),
				pid, val);
			break;

	/* LineStyle */
		/* black : Color of line */
		case 448 : id = MS_OBJ_ATTR_OUTLINE_COLOR;
			   name = "Colour lineColor";
			   break;
		/* 1<<16 : Not implemented */
		case 449 : name = "long lineOpacity"; break;
		/* white : Background color */
		case 450 : name = "Colour lineBackColor"; break;
		/* undefined : Modification for BW views */
		case 451 : name = "Colour lineCrMod"; break;
		/* Solid : Type of line */
		case 452 : name = "LineFill lineType"; break;
		/* NULL : Pattern/texture */
		case 453 : name = "IMsoBlip* lineFillBlip"; break;
		/* NULL : Blip file name */
		case 454 : name = "wchar* lineFillBlipName"; break;
		/* Comment : Blip flags */
		case 455 : name = "BlipFlags lineFillBlipFlags"; break;
		/* 0 : How big (A units) to make a metafile texture. */
		case 456 : name = "long lineFillWidth"; break;
		/* 0 :  */
		case 457 : name = "long lineFillHeight"; break;
		/* measure_Default : How to interpret fillWidth/Height numbers. */
		case 458 : name = "LengthMeasure lineFillDztype"; break;
		/* 9525 : A units; 1pt == 12700 EMUs */
		case 459 : id = MS_OBJ_ATTR_OUTLINE_WIDTH;
			   name = "long lineWidth";
			   val = val * 256. / 12700.; /* convert to 256th of pt */
			   break;
		/* 8<<16 : ratio (16.16) of width */
		case 460 : name = "long lineMiterLimit"; break;
		/* Simple : Draw parallel lines? */
		case 461 : name = "LineStyle lineStyle"; break;
		/* Solid : Can be overridden by: */
		case 462 : id = MS_OBJ_ATTR_OUTLINE_STYLE;
			   name = "DashedLineStyle lineDashing";
			   break;
		/* NULL : As Win32 ExtCreatePen */
		case 463 : name = "IMsoArray lineDashStyle"; break;
		/* NoEnd : Arrow at start */
		case 464 : id = MS_OBJ_ATTR_ARROW_START;
			   name = "LineEndStyle lineStartArrowhead";
			   break;
		/* NoEnd : Arrow at end */
		case 465 : id = MS_OBJ_ATTR_ARROW_END;
			   name = "LineEndStyle lineEndArrowhead";
			   break;
		/* MediumWidthArrow : Arrow at start */
		case 466 : id = MS_OBJ_ATTR_ARROW_START_WIDTH;
			   name = "ArrowWidth lineStartArrowWidth"; break;
		/* MediumLenArrow : Arrow at end */
		case 467 : id = MS_OBJ_ATTR_ARROW_START_LENGTH;
			   name = "ArrowLength lineStartArrowLength"; break;
		/* MediumWidthArrow : Arrow at start */
		case 468 : id = MS_OBJ_ATTR_ARROW_END_WIDTH;
			   name = "ArrowWidth lineEndArrowWidth"; break;
		/* MediumLenArrow : Arrow at end */
		case 469 : id = MS_OBJ_ATTR_ARROW_END_LENGTH;
			   name = "ArrowLength lineEndArrowLength"; break;
		/* JoinRound : How to join lines */
		case 470 : name = "LineJoin lineJoinStyle"; break;
		/* EndCapFlat : How to end lines */
		case 471 : name = "LineCap lineEndCapStyle"; break;

		case 507 : case 508 : case 509 : case 510 : case 511 :
			ms_escher_read_OPT_bools (h,
				line_bools, G_N_ELEMENTS (line_bools),
				pid, val);
			break;

	/* ShadowStyle */
		/* Offset : Type of effect */
		case 512 : name = "Shadow shadowType"; break;
		/* 0x808080 : Foreground color */
		case 513 : name = "Colour shadowColor"; break;
		/* 0xCBCBCB : Embossed color */
		case 514 : name = "Colour shadowHighlight"; break;
		/* undefined : Modification for BW views */
		case 515 : name = "Colour shadowCrMod"; break;
		/* 1<<16 : Fixed 16.16 */
		case 516 : name = "long shadowOpacity"; break;
		/* 25400 : Offset shadow */
		case 517 : name = "long shadowOffsetX"; break;
		/* 25400 : Offset shadow */
		case 518 : name = "long shadowOffsetY"; break;
		/* 0 : Double offset shadow */
		case 519 : name = "long shadowSecondOffsetX"; break;
		/* 0 : Double offset shadow */
		case 520 : name = "long shadowSecondOffsetY"; break;
		/* 1<<16 : 16.16 */
		case 521 : name = "long shadowScaleXToX"; break;
		/* 0 : 16.16 */
		case 522 : name = "long shadowScaleYToX"; break;
		/* 0 : 16.16 */
		case 523 : name = "long shadowScaleXToY"; break;
		/* 1<<16 : 16.16 */
		case 524 : name = "long shadowScaleYToY"; break;
		/* 0 : 16.16 / weight */
		case 525 : name = "long shadowPerspectiveX"; break;
		/* 0 : 16.16 / weight */
		case 526 : name = "long shadowPerspectiveY"; break;
		/* 1<<8 : scaling factor */
		case 527 : name = "long shadowWeight"; break;
		/* 0 :  */
		case 528 : name = "long shadowOriginX"; break;
		/* 0 :  */
		case 529 : name = "long shadowOriginY"; break;
		case 574 : case 575 :
			ms_escher_read_OPT_bools (h,
				shadow_bools, G_N_ELEMENTS (shadow_bools),
				pid, val);
			break;

	/* PerspectiveStyle */
		/* Shape : Where transform applies */
		case 576 : name = "Transform perspectiveType"; break;
		/* 0 : The long values define a transformation matrix,
		 * effectively, each value is scaled by the perspectiveWeight
		 * parameter. */
		case 577 : name = "long perspectiveOffsetX"; break;
		/* 0 :  */
		case 578 : name = "long perspectiveOffsetY"; break;
		/* 1<<16 :  */
		case 579 : name = "long perspectiveScaleXToX"; break;
		/* 0 :  */
		case 580 : name = "long perspectiveScaleYToX"; break;
		/* 0 :  */
		case 581 : name = "long perspectiveScaleXToY"; break;
		/* 1<<16 :  */
		case 582 : name = "long perspectiveScaleYToY"; break;
		/* 0 :  */
		case 583 : name = "long perspectivePerspectiveX"; break;
		/* 0 :  */
		case 584 : name = "long perspectivePerspectiveY"; break;
		/* 1<<8 : Scaling factor */
		case 585 : name = "long perspectiveWeight"; break;
		/* 1<<15 :  */
		case 586 : name = "long perspectiveOriginX"; break;
		/* 1<<15 :  */
		case 587 : name = "long perspectiveOriginY"; break;
		/* FALSE : On/off */
		case 639 : name = "bool fPerspective"; break;

	/* 3D Object */
		/* 0 : Fixed-point 16.16 */
		case 640 : name = "long DSpecularAmt"; break;
		/* 65536 : Fixed-point 16.16 */
		case 641 : name = "long c3DDiffuseAmt"; break;
		/* 5 : Default gives OK results */
		case 642 : name = "long c3DShininess"; break;
		/* 12700 : Specular edge thickness */
		case 643 : name = "long c3DEdgeThickness"; break;
		/* 0 : Distance of extrusion in EMUs */
		case 644 : name = "long c3DExtrudeForward"; break;
		/* 457200 :  */
		case 645 : name = "long c3DExtrudeBackward"; break;
		/* 0 : Extrusion direction */
		case 646 : name = "long c3DExtrudePlane"; break;
		/* FillThenLine : Basic color of extruded part of shape; the
		 * lighting model used will determine the exact shades used
		 * when rendering.  */
		case 647 : name = "Colour c3DExtrusionColor"; break;
		/* undefined : Modification for BW views */
		case 648 : name = "Colour c3DCrMod"; break;
		case 700 : case 701 : case 702 : case 703 :
			ms_escher_read_OPT_bools (h,
				three_d_obj_bools, G_N_ELEMENTS (three_d_obj_bools),
				pid, val);
			break;

	/* 3D Style */
		/* 0 : degrees (16.16) about y axis */
		case 704 : name = "long c3DYRotationAngle"; break;
		/* 0 : degrees (16.16) about x axis */
		case 705 : name = "long c3DXRotationAngle"; break;
		/* 100 : These specify the rotation axis; only their relative magnitudes matter. */
		case 706 : name = "long c3DRotationAxisX"; break;
		/* 0 :  */
		case 707 : name = "long c3DRotationAxisY"; break;
		/* 0 :  */
		case 708 : name = "long c3DRotationAxisZ"; break;
		/* 0 : degrees (16.16) about axis */
		case 709 : name = "long c3DRotationAngle"; break;
		/* 0 : rotation center x (16.16 or g-units) */
		case 710 : name = "long c3DRotationCenterX"; break;
		/* 0 : rotation center y (16.16 or g-units) */
		case 711 : name = "long c3DRotationCenterY"; break;
		/* 0 : rotation center z (absolute (emus)) */
		case 712 : name = "long c3DRotationCenterZ"; break;
		/* FullRender : Full,wireframe, or bcube */
		case 713 : name = "RenderMode c3DRenderMode"; break;
		/* 30000 : pixels (16.16) */
		case 714 : name = "long c3DTolerance"; break;
		/* 1250000 : X view point (emus) */
		case 715 : name = "long c3DXViewpoint"; break;
		/* -1250000 : Y view point (emus) */
		case 716 : name = "long c3DYViewpoint"; break;
		/* 9000000 : Z view distance (emus) */
		case 717 : name = "long c3DZViewpoint"; break;
		/* 32768 :  */
		case 718 : name = "long c3DOriginX"; break;
		/* -32768 :  */
		case 719 : name = "long c3DOriginY"; break;
		/* -8847360 : degree (16.16) skew angle */
		case 720 : name = "long c3DSkewAngle"; break;
		/* 50 : Percentage skew amount */
		case 721 : name = "long c3DSkewAmount"; break;
		/* 20000 : Fixed point intensity */
		case 722 : name = "long c3DAmbientIntensity"; break;
		/* 50000 : Key light source direc- */
		case 723 : name = "long c3DKeyX"; break;
		/* 0 : tion; only their relative */
		case 724 : name = "long c3DKeyY"; break;
		/* 10000 : magnitudes matter */
		case 725 : name = "long c3DKeyZ"; break;
		/* 38000 : Fixed point intensity */
		case 726 : name = "long c3DKeyIntensity"; break;
		/* -50000 : Fill light source direc- */
		case 727 : name = "long c3DFillX"; break;
		/* 0 : tion; only their relative */
		case 728 : name = "long c3DFillY"; break;
		/* 10000 : magnitudes matter */
		case 729 : name = "long c3DFillZ"; break;
		/* 38000 : Fixed point intensity */
		case 730 : name = "long c3DFillIntensity"; break;

		case 763 : case 764 : case 765 : case 766 : case 767 :
			ms_escher_read_OPT_bools (h,
				three_d_style_bools, G_N_ELEMENTS (three_d_style_bools),
				pid, val);
			break;

	/* Shape */
		/* NULL : master shape */
		case 769 : name = "MSOHSP pMaster"; break;
		/* None : Type of connector */
		case 771 : name = "ConnectStyle"; break;
		/* Automatic : Settings for modifications to be made when in
		 * different forms of black-and-white mode. */
		case 772 : name = "BlackWhiteMode bWMode"; break;
		/* Automatic :  */
		case 773 : name = "BlackWhiteMode bWModePureBW"; break;
		/* Automatic :  */
		case 774 : name = "BlackWhiteMode bWModeBW"; break;
		case 826 : case 827 : case 828 : case 830 : case 831 :
			ms_escher_read_OPT_bools (h,
				shape_bools, G_N_ELEMENTS (shape_bools),
				pid, val);
			break;

	/* CallOut */
		/* TwoSegment : CalloutType */
		case 832 : name = "CalloutType spcot"; break;
		/* 1/12" : Distance from box to first point.(EMUs) */
		case 833 : name = "long dxyCalloutGap"; break;
		/* Any : Callout angle */
		case 834 : name = "CallOutAngle spcoa"; break;
		/* Specified : Callout drop type */
		case 835 : name = "CalloutDrop spcod"; break;
		/* 9 points : if msospcodSpecified, the actual drop distance */
		case 836 : name = "long dxyCalloutDropSpecified"; break;
		/* 0 : if fCalloutLengthSpecified, the actual distance */
		case 837 : name = "long dxyCalloutLengthSpecified"; break;

		case 889 : case 890 : case 891 : case 892 : case 893 :
		case 894 : case 895 :
			ms_escher_read_OPT_bools (h,
				callout_bools, G_N_ELEMENTS (callout_bools),
				pid, val);
			break;

	/* GroupShape */
		/* NULL : Shape Name (present only if explicitly set) */
		case 896 : id = MS_OBJ_ATTR_OBJ_NAME;
			   name = "wchar* wzName";
			   break;
		/* NULL : alternate text */
		case 897 : id = MS_OBJ_ATTR_OBJ_ALT_TEXT;
			   name = "wchar* wzDescription";
			   break;

		/* NULL : The hyperlink in the shape. */
		case 898 : name = "IHlink* pihlShape"; break;
		/* NULL : The polygon that text will be wrapped around (Word) */
		case 899 : name = "IMsoArray pWrapPolygonVertices"; break;
		/* 1/8" : Left wrapping distance from text (Word) */
		case 900 : name = "long dxWrapDistLeft"; break;
		/* 0 : Top wrapping distance from text (Word) */
		case 901 : name = "long dyWrapDistTop"; break;
		/* 1/8" : Right wrapping distance from text (Word) */
		case 902 : name = "long dxWrapDistRight"; break;
		/* 0 : Bottom wrapping distance from text (Word) */
		case 903 : name = "long dyWrapDistBottom"; break;
		/* 0 : Regroup ID  */
		case 904 : name = "long lidRegroup"; break;

		case 953 : case 954 : case 955 : case 956 : case 957 :
		case 958 : case 959 :
			ms_escher_read_OPT_bools (h,
				group_bools, G_N_ELEMENTS (group_bools),
				pid, val);
			break;

		default : name = "UnknownID";
		}

		d (0, {
			if (NULL != name)
				   g_printerr ("%s %d = 0x%08x (=%d) %s%s;\n", name, pid, val, val,
			      is_blip ? " is blip" : "",
					   is_complex ? " is complex" : "");
		});

		/* TODO : use this for something */
		if (is_complex) {
			/* check for over run */
			g_return_val_if_fail (extra + val - data + COMMON_HEADER_LEN <= h->len, TRUE);

			d (5, gsf_mem_dump (extra, val););
			d (11, {
				static int count = 0;
				char *name = g_strdup_printf ("gnumeric-complex-opt-[%d]-%d", pid, count++);
				FILE *f = g_fopen (name, "w");
				if (f != NULL) {
					fwrite (extra, 1, val, f);
					fclose (f);
				}
				g_free (name);
			});
			if (id & MS_OBJ_ATTR_IS_PTR_MASK) {
				char *s = g_utf16_to_utf8 ((gunichar2*)extra, val / 2,
							   NULL, NULL, NULL);
				ms_escher_header_add_attr (h,
					ms_obj_attr_new_ptr (id, s));
				id = MS_OBJ_ATTR_NONE;
			}
			extra += val;
		}

		if (id & MS_OBJ_ATTR_IS_INT_MASK)
			ms_escher_header_add_attr (h,
				ms_obj_attr_new_uint (id, val));
		else if (id != MS_OBJ_ATTR_NONE) {
			g_warning ("We know attr[%d] with id == 0x%x, but do not store it ?", pid, id);
		}
	}
	if (needs_free)
		g_free ((guint8 *)data);

	return FALSE;
}


ms_escher_read_ConnectorRule (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_Dgg (MSEscherState *state, MSEscherHeader *h)
{
#if 0
	typedef struct {
		guint32 max_spid;
		guint32 num_id_clust;
		guint32 num_shapes_saved; /* Includes deleted shapes if undo saved */
		guint32 num_drawings_saved;
		GArray *id_clusts;
	} FDGG;
	typedef struct {
		guint32 DG_owning_spids;
		guint32 spids_used_so_far;
	} ID_CLUST;

	FDGG fd;
	guint32 lp;
	guint8 const *data = h->data + COMMON_HEADER_LEN;
	fd.id_clusts = g_array_new (1, 1, sizeof(ID_CLUST));
	fd.max_spid           = GSF_LE_GET_GUINT32(data+ 0);
	fd.num_id_clust       = GSF_LE_GET_GUINT32(data+ 4);
	fd.num_shapes_saved   = GSF_LE_GET_GUINT32(data+ 8);
	fd.num_drawings_saved = GSF_LE_GET_GUINT32(data+12);

	g_printerr ("maxspid 0x%x clusts 0x%x shapes 0x%x drawings x%x\n",
		fd.max_spid, fd.num_id_clust, fd.num_shapes_saved,
		fd.num_drawings_saved);

	data+=16;
	for (lp = 0; lp < fd.num_id_clust; lp++) {
		ID_CLUST cl;
		cl.DG_owning_spids   = GSF_LE_GET_GUINT32(data+0);
		cl.spids_used_so_far = GSF_LE_GET_GUINT32(data+4);
		g_array_append_val (fd.id_clusts, cl);
	}
#endif
	return FALSE;
}


ms_escher_read_Sp (MSEscherState *state, MSEscherHeader *h)
{
	static char const * const shape_names[] = {
		/* 0 */ "Not a primitive",
		/* 1 */ "Rectangle",
		/* 2 */ "RoundRectangle",
		/* 3 */ "Ellipse",
		/* 4 */ "Diamond",
		/* 5 */ "IsocelesTriangle",
		/* 6 */ "RightTriangle",
		/* 7 */ "Parallelogram",
		/* 8 */ "Trapezoid",
		/* 9 */ "Hexagon",
		/* 10 */ "Octagon",
		/* 11 */ "Plus",
		/* 12 */ "Star",
		/* 13 */ "Arrow",
		/* 14 */ "ThickArrow",
		/* 15 */ "HomePlate",
		/* 16 */ "Cube",
		/* 17 */ "Balloon",
		/* 18 */ "Seal",
		/* 19 */ "Arc",
		/* 20 */ "Line",
		/* 21 */ "Plaque",
		/* 22 */ "Can",
		/* 23 */ "Donut",
		/* 24-31 */	"TextSimple", "TextOctagon", "TextHexagon", "TextCurve", "TextWave",
				"TextRing", "TextOnCurve", "TextOnRing",
		/* 32 */ "StraightConnector1",
		/* 33-36 */	"BentConnector2",	"BentConnector3",
				"BentConnector4",	"BentConnector5",
		/* 37-40 */	"CurvedConnector2",	"CurvedConnector3",
				"CurvedConnector4",	"CurvedConnector5",
		/* 41-43 */ "Callout1",		"Callout2",	"Callout3",
		/* 44-46 */ "AccentCallout1", "AccentCallout2", "AccentCallout3",
		/* 47-49 */ "BorderCallout1", "BorderCallout2", "BorderCallout3",
		/* 50-52 */ "AccentBorderCallout1", "AccentBorderCallout2", "AccentBorderCallout3",
		/* 53-54 */ "Ribbon", "Ribbon2",
		/* 55 */ "Chevron",
		/* 56 */ "Pentagon",
		/* 57 */ "NoSmoking",
		/* 58 */ "Seal8",
		/* 59 */ "Seal16",
		/* 60 */ "Seal32",
		/* 61 */ "WedgeRectCallout",
		/* 62 */ "WedgeRRectCallout",
		/* 63 */ "WedgeEllipseCallout",
		/* 64 */ "Wave",
		/* 65 */ "FoldedCorner",
		/* 66 */ "LeftArrow",
		/* 67 */ "DownArrow",
		/* 68 */ "UpArrow",
		/* 69 */ "LeftRightArrow",
		/* 70 */ "UpDownArrow",
		/* 71 */ "IrregularSeal1",
		/* 72 */ "IrregularSeal2",
		/* 73 */ "LightningBolt",
		/* 74 */ "Heart",
		/* 75 */ "PictureFrame",
		/* 76 */ "QuadArrow",
		/* 77-83 */ "LeftArrowCallout", "RightArrowCallout",
			"UpArrowCallout", "DownArrowCallout", "LeftRightArrowCallout",
			"UpDownArrowCallout", "QuadArrowCallout",
		/* 84 */ "Bevel",
		/* 85 */ "LeftBracket",
		/* 86 */ "RightBracket",
		/* 87 */ "LeftBrace",
		/* 88 */ "RightBrace",
		/* 89 */ "LeftUpArrow",
		/* 90 */ "BentUpArrow",
		/* 91 */ "BentArrow",
		/* 92 */ "Seal24",
		/* 93 */ "StripedRightArrow",
		/* 94 */ "NotchedRightArrow",
		/* 95 */ "BlockArc",
		/* 96 */ "SmileyFace",
		/* 97 */ "VerticalScroll",
		/* 98 */ "HorizontalScroll",
		/* 99 */ "CircularArrow",
		/* 100 */ "NotchedCircularArrow",
		/* 101 */ "UturnArrow",
		/* 102-105 */ "CurvedRightArrow", "CurvedLeftArrow", "CurvedUpArrow", "CurvedDownArrow",
		/* 106 */ "CloudCallout",
		/* 107-108 */ "EllipseRibbon", "EllipseRibbon2",
		/* 109-135 */ "FlowChartProcess", "FlowChartDecision",
			"FlowChartInputOutput", "FlowChartPredefinedProcess",
			"FlowChartInternalStorage", "FlowChartDocument",
			"FlowChartMultidocument", "FlowChartTerminator",
			"FlowChartPreparation", "FlowChartManualInput",
			"FlowChartManualOperation", "FlowChartConnector",
			"FlowChartPunchedCard", "FlowChartPunchedTape",
			"FlowChartSummingJunction", "FlowChartOr", "FlowChartCollate",
			"FlowChartSort", "FlowChartExtract", "FlowChartMerge",
			"FlowChartOfflineStorage", "FlowChartOnlineStorage",
			"FlowChartMagneticTape", "FlowChartMagneticDisk",
			"FlowChartMagneticDrum", "FlowChartDisplay", "FlowChartDelay",
		/* 136 */ "TextPlainText",
		/* 137 */ "TextStop",
		/* 138-139 */ "TextTriangle", "TextTriangleInverted",
		/* 140-141 */ "TextChevron", "TextChevronInverted",
		/* 142-143 */ "TextRingInside", "TextRingOutside",
		/* 144-145 */ "TextArchUpCurve", "TextArchDownCurve",
		/* 146-147 */ "TextCircleCurve", "TextButtonCurve",
		/* 148-149 */ "TextArchUpPour", "TextArchDownPour",
		/* 150 */ "TextCirclePour",
		/* 151 */ "TextButtonPour",
		/* 152-153 */ "TextCurveUp",	"TextCurveDown",
		/* 154-155 */ "TextCascadeUp",	"TextCascadeDown",
		/* 156-159 */ "TextWave1",		"TextWave2", "TextWave3", "TextWave4",
		/* 160-161 */ "TextInflate",	"TextDeflate",
		/* 162-163 */ "TextInflateBottom", "TextDeflateBottom",
		/* 164-165 */ "TextInflateTop",	"TextDeflateTop",
		/* 166-167 */ "TextDeflateInflate",	"TextDeflateInflateDeflate",
		/* 168-171 */ "TextFadeRight", "TextFadeLeft", "TextFadeUp", "TextFadeDown",
		/* 172-174 */ "TextSlantUp", "TextSlantDown", "TextCanUp",
		/* 175 */ "TextCanDown",
		/* 176 */ "FlowChartAlternateProcess",
		/* 177 */ "FlowChartOffpageConnector",
		/* 178 */ "Callout90",
		/* 179 */ "AccentCallout90",
		/* 180 */ "BorderCallout90",
		/* 181 */ "AccentBorderCallout90",
		/* 182 */ "LeftRightUpArrow",
		/* 183-184 */ "Sun",	"Moon",
		/* 185 */ "BracketPair",
		/* 186 */ "BracePair",
		/* 187 */ "Seal4",
		/* 188 */ "DoubleWave",
		/* 189-200 */ "ActionButtonBlank", "ActionButtonHome",
			"ActionButtonHelp", "ActionButtonInformation",
			"ActionButtonForwardNext", "ActionButtonBackPrevious",
			"ActionButtonEnd", "ActionButtonBeginning",
			"ActionButtonReturn", "ActionButtonDocument",
			"ActionButtonSound", "ActionButtonMovie",
		/* 201 */ "HostControl",
		/* 202 */ "TextBox"
	};

	gboolean needs_free;
	guint32 spid, flags;
	guint8 const *data;

	g_return_val_if_fail (h->instance <= 202, TRUE);

	d (0, g_printerr ("%s (0x%x);\n", shape_names[h->instance],
		      h->instance););

	data = ms_escher_get_data (state,
		h->offset + COMMON_HEADER_LEN, 8, &needs_free);
	if (data == NULL)
		return TRUE;

	spid  = GSF_LE_GET_GUINT32 (data+0);
	flags = GSF_LE_GET_GUINT32 (data+4);
	d (0, g_printerr ("SPID %d, Type %d,%s%s%s%s%s%s%s%s%s%s%s%s;\n",
		      spid, h->instance,
			(flags&0x01) ? " Group": "",
			(flags&0x02) ? " Child": "",
			(flags&0x04) ? " Patriarch": "",
			(flags&0x08) ? " Deleted": "",
			(flags&0x10) ? " OleShape": "",
			(flags&0x20) ? " HaveMaster": "",
			(flags&0x40) ? " FlipH": "",
			(flags&0x80) ? " FlipV": "",
			(flags&0x100) ? " Connector":"",
			(flags&0x200) ? " HasAnchor": "",
			(flags&0x400) ? " HasBackground": "",
			(flags&0x800) ? " HasSpt": ""
		       ););

	if (flags & 0x40)
		ms_escher_header_add_attr (h,
			ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_H));
	if (flags & 0x80)
		ms_escher_header_add_attr (h,
			ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_V));
	if (needs_free)
		g_free ((guint8*)data);

	return FALSE;
}


ms_escher_read_ClientTextbox (MSEscherState *state, MSEscherHeader *h)
{
	guint16 opcode;
	int has_next_record;
	char *text;
	PangoAttrList *markup;

	g_return_val_if_fail (h->len == COMMON_HEADER_LEN, TRUE);
	g_return_val_if_fail (h->offset + h->len == state->end_offset, TRUE);

	/* Read the TXO, be VERY careful until we are sure of the state */
	has_next_record = ms_biff_query_peek_next (state->q, &opcode);
	g_return_val_if_fail (has_next_record, TRUE);
	g_return_val_if_fail (opcode == BIFF_TXO, TRUE);
	has_next_record = ms_biff_query_next (state->q);
	g_return_val_if_fail (has_next_record, TRUE);

	text = ms_read_TXO (state->q, state->container, &markup);
	ms_escher_header_add_attr (h,
		ms_obj_attr_new_ptr (MS_OBJ_ATTR_TEXT, text));
	if (markup != NULL) {
		ms_escher_header_add_attr (h,
			ms_obj_attr_new_markup (MS_OBJ_ATTR_MARKUP, markup));
		pango_attr_list_unref (markup);
	}
	d (0, g_printerr ("'%s';\n", text););
	return FALSE;
}


ms_escher_read_Blip (MSEscherState *state, MSEscherHeader *h)
{
	int offset = COMMON_HEADER_LEN + 16;
	guint32 inst = h->instance;
	gboolean needs_free, failure = FALSE;
	guint8 const *data;
	MSEscherBlip *blip = NULL;
	char const   *type = NULL;

	/*  This doesn't make alot of sense.
	 *  Which is the normative indicator of what type the blip is ?
	 *  We have the requested type for each OS, and the instance code for
	 *  this record.  The magic number for this record seems to be the right
	 *  things to use, but the comments from S59FE3.HTM
	 *
	 *  NOTE!: The primary UID is only saved to disk
	 *     if (inst ^ blip_signature == 1).
	 *
	 * suggest that sometimes the magic numers have built flags.
	 * I'll assume that what they mean to say is that the magic number
	 * in the instance code may have its low bit set to indicate the
	 * presence of a primary uid if the rest of the signature is one
	 * of the known signatures.
	 */
	if (inst & 0x1) {
		offset += 16;
		inst &= (~0x1);
	}

	/* Clients may set bit 0x800 */
	inst &= (~0x800);
	if (inst == 0x216 || inst == 0x3d4 || inst == 0x542) {
		guint8 const *tmp = ms_escher_get_data (state, h->offset + offset,
			META_FILE_HEADER_LEN, &needs_free);
		guint32 uncompressed_len = GSF_LE_GET_GUINT32 (tmp+0);
		guint32 compressed_len = GSF_LE_GET_GUINT32 (tmp+28);
		guint8  compress = tmp[32];
		guint8  filter	 = tmp[33];

		if (needs_free)
			g_free ((guint8*)tmp);
		offset += META_FILE_HEADER_LEN;

		if (inst == 0x216)
			type = "wmf";
		else if (inst == 0x3d4)
			type = "emf";
		else
			type = "pict";

		if (filter != 0xfe /* must be none */	||
		    (unsigned)(h->len - offset) < compressed_len) {
			failure = TRUE;
			g_warning ("invalid metafile header %x, %u != %u;", filter,
				   (h->len - offset), compressed_len);
		} else {
			tmp = ms_escher_get_data (state, h->offset + offset,
				compressed_len, &needs_free);
			if (compress == 0) {		/* Yes 0 == deflate */
				uLongf len = uncompressed_len*4;
				guint8 *buffer = g_malloc (len);

				int res = uncompress (buffer, &len, tmp, compressed_len);
				if (res != Z_OK) {
					g_free (buffer);
					failure = TRUE;
					g_warning ("compression failure %d;", res);
				} else
					blip = ms_escher_blip_new (buffer,
						len, type, FALSE);
			} else if (compress == 0xfe) {	/* 0xfe  == none */
				blip = ms_escher_blip_new ((guint8 *)tmp, compressed_len,
					type, !needs_free);
				needs_free = FALSE;
			} else {
				failure = TRUE;
				g_warning ("Unknown compression type %hhx;", compress);
			}
			if (needs_free)
				g_free ((guint8*)tmp);
		}
	} else if (inst == 0x46a || inst == 0x6e0 || inst == 0x7a8) {
		if (inst == 0x46a)	/* JPEG data, with 1 byte header */
			type = "jpeg";
		else if (inst == 0x6e0)	/* PNG  data, with 1 byte header */
			type = "png";
		else			/* DIB  data, with 1 byte header */
			type = "dib";
		offset++;
		data = ms_escher_get_data (state, h->offset + offset,
			h->len - offset, &needs_free);
		if (data)
			blip = ms_escher_blip_new ((guint8 *)data,
						   h->len - offset,
						   type, !needs_free);
		else
			failure = TRUE;
	} else {
		failure = TRUE;
		g_warning ("Don't know what to do with this image %x;", inst);
	}

	/* Always add a blip to keep the indicies in sync, even if it's NULL */
	ms_container_add_blip (state->container, blip);
	return failure;
}


ms_escher_read_UserDefined (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_BStoreContainer (MSEscherState *state, MSEscherHeader *h)
{
	return ms_escher_read_container (state, h, 0, FALSE);
}


ms_escher_read_CLSID (MSEscherState *state, MSEscherHeader *h)
{
	/* Holds a 'Class ID Record' ID record which is only included in the
	 * 'clipboard format'.  It contains an OLE CLSID record from the source
	 * app, and can be used check where the clipboard data originated.
	 *
	 * We ignore these.  What is an 'OLE CLSID' ?  Would we ever need this ?
	 */
	return FALSE;
}


ms_escher_read_container (MSEscherState *state, MSEscherHeader *container,
			  gint prefix, gboolean return_attrs_in_container)
{
	MSEscherHeader h;

	g_return_val_if_fail (container != NULL, TRUE);

	ms_escher_header_init (&h);
	h.container = container;

	/* Skip the common header */
	h.offset = container->offset + prefix + COMMON_HEADER_LEN;

	do {
		guint16 tmp;
		char const *fbt_name = NULL;
		gboolean (*handler)(MSEscherState *state,
				    MSEscherHeader *container) = NULL;
		gboolean needs_free;

		guint8 const *data = ms_escher_get_data (state, h.offset,
			COMMON_HEADER_LEN, &needs_free);

		if (!data) {
			ms_escher_header_release (&h);
			return TRUE;
		}

		tmp	= GSF_LE_GET_GUINT16 (data + 0);
		h.fbt	= GSF_LE_GET_GUINT16 (data + 2);

		/* Include the length of this header in the record size */
		h.len	   = GSF_LE_GET_GUINT32 (data + 4) + COMMON_HEADER_LEN;
		h.ver      = tmp & 0x0f;
		h.instance = (tmp >> 4) & 0xfff;

		d (0 , g_printerr ("length 0x%x(=%d), ver 0x%x, instance 0x%x, offset = 0x%x(=%d);\n",
			       h.len, h.len, h.ver, h.instance, h.offset, h.offset););

		if (needs_free)
			g_free ((void *)data);

		/*
		 * Let's double check that the data we just read makes sense.
		 * If problems arise in the next tests it probably indicates
		 * that the PRECEDING record length was invalid.  Check that
		 * it included the header.
		 */
		if ((h.fbt & (~0x1ff)) != 0xf000) {
			g_warning ("Invalid fbt = 0x%x\n", h.fbt);
			ms_escher_header_release (&h);
			return TRUE;
		}

#define EshRecord(x) \
		x : fbt_name = #x; \
		    handler = &ms_escher_read_ ## x; \
		break

		switch (h.fbt) {
		case EshRecord(DggContainer);	case EshRecord(Dgg);
		case EshRecord(DgContainer);	case EshRecord(Dg);
		case EshRecord(SpgrContainer);	case EshRecord(Spgr);
		case EshRecord(SpContainer);	case EshRecord(Sp);
		case EshRecord(BStoreContainer);case EshRecord(BSE);
		case EshRecord(Textbox);	case EshRecord(ClientTextbox);
		case EshRecord(Anchor); case EshRecord(ChildAnchor); case EshRecord(ClientAnchor);
		case EshRecord(ClientData);
		case EshRecord(CLSID);
		case EshRecord(OPT);
		case EshRecord(ColorMRU);
		case EshRecord(SplitMenuColors);
		case EshRecord(RegroupItems);
		case EshRecord(ColorScheme);
		case EshRecord(OleObject);
		case EshRecord(DeletedPspl);
		case EshRecord(SolverContainer);
		case EshRecord(ConnectorRule);
		case EshRecord(AlignRule);
		case EshRecord(ArcRule);
		case EshRecord(ClientRule);
		case EshRecord(CalloutRule);
		case EshRecord(Selection);
		case EshRecord(UserDefined);
		default : fbt_name = NULL;
		}
#undef EshRecord

		if (Blip_START <= h.fbt && h.fbt <= Blip_END) {
			ms_escher_read_Blip (state, &h);
		} else if (fbt_name != NULL) {
			gboolean res;

			/* Not really needed */
			g_return_val_if_fail (handler != NULL, TRUE);

			d (0, g_printerr ("{ /* %s */\n", fbt_name););
			res = (*handler)(state, &h);

			d (0, g_printerr ("}; /* %s */\n", fbt_name););
			if (res) {
				ms_escher_header_release (&h);
				g_warning ("%s;", fbt_name);
				return TRUE;
			}

		} else
			g_warning ("Invalid fbt = %x;", h.fbt);

		h.offset += h.len;
	} while (h.offset < (container->offset + container->len));

	if (container->attrs == NULL && return_attrs_in_container) {
		container->attrs = h.attrs;
		h.release_attrs = FALSE;
	}
	ms_escher_header_release (&h);
	return FALSE;
}


ms_escher_read_SpgrContainer (MSEscherState *state, MSEscherHeader *h)
{
	return ms_escher_read_container (state, h, 0, FALSE);
}


ms_escher_read_ArcRule (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_opt_add_bool (GString *buf, gsize marker,
			guint16 pid, gboolean b)
{
	int N = ms_escher_get_inst (buf, marker);
	guint16 gid = pid | 0xf;
	guint8 shift = gid - pid;
	guint32 val = (b ? 0x00010001 : 0x00010000) << shift;

	if (N > 0 && GSF_LE_GET_GUINT16 (buf->str + buf->len - 6) == gid) {
		val |= GSF_LE_GET_GUINT32 (buf->str + buf->len - 4);
		GSF_LE_SET_GUINT32 (buf->str + buf->len - 4, val);
	} else
		ms_escher_opt_add_simple (buf, marker, gid, val);
}


ms_escher_read_OPT_bools (MSEscherHeader *h,
			  MSEscherBoolOptTable const *bools, unsigned n_bools,
			  guint pid, guint32 const val)
{
	unsigned i;
	guint32 mask = 0x10000 << (n_bools - 1);
	guint32 bit  = 0x00001 << (n_bools - 1);

	g_return_if_fail (n_bools > 0);
	g_return_if_fail (bools[n_bools-1].pid == pid);

	d (2, g_printerr ("Set of Bools %d-%d = 0x%08x;\n{\n",
		      bools[0].pid, bools[n_bools-1].pid, val););

	pid -= (n_bools - 1);
	for (i = 0; i < n_bools; i++, mask >>= 1, bit >>= 1, pid++) {
		gboolean set_val = ((val & bit) == bit);
		gboolean def_val = bools[i].default_val;
		const MSObjAttrID aid = bools[i].id;

		if (!(val & mask))	/* the value is set */
			continue;

		d (0, g_printerr ("bool %s(%d) = %s; /* def: %s; gnm: %d */\n",
			      bools[i].name, pid,
			      set_val ? "true" : "false",
			      def_val ? "true" : "false",
			      aid););

		if (set_val != def_val && aid != 0)
			ms_escher_header_add_attr
				(h, ms_obj_attr_new_flag (aid));
	}
	d (2, g_printerr ("};\n"););
}


ms_escher_read_SpContainer (MSEscherState *state, MSEscherHeader *h)
{
	return ms_escher_read_container (state, h, 0, FALSE);
}


ms_escher_opt_add_str_wchar (GString *buf, gsize marker, GString *extra,
			     guint16 pid, const char *str)
{
	glong ic;
	gunichar2 *str16 = g_utf8_to_utf16 (str, -1, NULL, &ic, NULL);
	guint8 tmp[6];

	GSF_LE_SET_GUINT16 (tmp, pid | 0x8000);
	GSF_LE_SET_GUINT32 (tmp + 2, ic * 2 + 2);
	g_string_append_len (buf, tmp, sizeof tmp);
	g_string_append_len (extra, (gpointer)str16, ic * 2 + 2);

	g_free (str16);

	ms_escher_set_inst (buf, marker,
			    ms_escher_get_inst (buf, marker) + 1);
}


ms_escher_read_Selection (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_line_type_to_xl (GOLineDashType ld)
{
	switch (ld) {
	default:
	case GO_LINE_NONE:		return -1;
	case GO_LINE_SOLID:		return 0;
	case GO_LINE_S_DOT:		return 2;
	case GO_LINE_S_DASH_DOT:	return 3;
	case GO_LINE_S_DASH_DOT_DOT:	return 4;
	case GO_LINE_DASH_DOT_DOT_DOT:	return 4;
	case GO_LINE_DOT:		return 5;
	case GO_LINE_S_DASH:		return 6;
	case GO_LINE_DASH:		return 7;
	case GO_LINE_LONG_DASH:		return 8;
	case GO_LINE_DASH_DOT:		return 9;
	case GO_LINE_DASH_DOT_DOT:	return 10;
	}
}


ms_escher_parse (BiffQuery *q, MSContainer *container, gboolean return_attrs)
{
	MSEscherState state;
	MSEscherHeader fake_header;
	MSObjAttrBag *res = NULL;
	char const *drawing_record_name = "Unknown";

	g_return_val_if_fail (q != NULL, NULL);

	if (q->opcode == BIFF_MS_O_DRAWING)
		drawing_record_name = "Drawing";
	else if (q->opcode == BIFF_MS_O_DRAWING_GROUP)
		drawing_record_name = "Drawing Group";
	else if (q->opcode == BIFF_MS_O_DRAWING_SELECTION)
		drawing_record_name = "Drawing Selection";
	else if (q->opcode == BIFF_CHART_gelframe)
		drawing_record_name = "Chart GelFrame";
	else {
		g_warning ("EXCEL : unexpected biff type %x;", q->opcode);
		return NULL;
	}

	state.container	   = container;
	state.q		   = q;
	state.segment_len  = q->length;
	state.start_offset = 0;
	state.end_offset   = q->length;

	ms_escher_header_init (&fake_header);
	fake_header.container = NULL;
	fake_header.offset = 0;

	d (0, g_printerr ("{  /* Escher '%s'*/\n", drawing_record_name););
	ms_escher_read_container (&state,
		&fake_header, -COMMON_HEADER_LEN, return_attrs);
	d (0, g_printerr ("}; /* Escher '%s'*/\n", drawing_record_name););

	if (return_attrs) {
		res = fake_header.attrs;
		fake_header.release_attrs = FALSE;
	}
	ms_escher_header_release (&fake_header);
	return res;
}


ms_escher_get_inst (GString *buf, gsize marker)
{
	guint16 iv = GSF_LE_GET_GUINT16 (buf->str + marker);
	return (iv >> 4);
}


ms_escher_get_data (MSEscherState *state,
		    gint offset,	/* bytes from logical start of the stream */
		    gint num_bytes,	/*how many bytes we want, NOT incl prefix */
		    gboolean * needs_free)
{
	BiffQuery *q = state->q;
	guint8    *res;

	g_return_val_if_fail (offset >= state->start_offset, NULL);

	/* find the 1st containing record */
	while (offset >= state->end_offset) {
		if (!ms_biff_query_next (q)) {
			g_warning ("unexpected end of stream;");
			return NULL;
		}

		if (q->opcode != BIFF_MS_O_DRAWING &&
		    q->opcode != BIFF_MS_O_DRAWING_GROUP &&
		    q->opcode != BIFF_MS_O_DRAWING_SELECTION &&
		    q->opcode != BIFF_CHART_gelframe &&
		    q->opcode != BIFF_CONTINUE) {
		  g_warning ("Unexpected record type 0x%x len=0x%x @ 0x%lx;", q->opcode, q->length, (long)q->streamPos);
			return NULL;
		}

		d (1, g_printerr ("Target is 0x%x bytes at 0x%x, current = 0x%x..0x%x;\n"
			      "Adding biff-0x%x of length 0x%x;\n",
			      num_bytes, offset,
			      state->start_offset,
			      state->end_offset,
			      q->opcode, q->length););

		state->start_offset = state->end_offset;
		state->end_offset += q->length;
		state->segment_len = q->length;
	}

	g_return_val_if_fail (offset >= state->start_offset, NULL);
	g_return_val_if_fail ((size_t)(offset - state->start_offset) < q->length, NULL);

	res = q->data + offset - state->start_offset;
	if ((*needs_free = ((offset + num_bytes) > state->end_offset))) {
		guint8 *buffer = g_malloc (num_bytes);
		guint8 *tmp = buffer;

		/* Setup front stub */
		int len = q->length - (res - q->data);
		int counter = 0;

		d (1, g_printerr ("MERGE needed (%d) which is >= -%d + %d;\n",
			      num_bytes, offset, state->end_offset););

		do {
			int maxlen = (buffer + num_bytes) - tmp;
			len = MIN (len, maxlen);
			d (1, g_printerr ("record %d) add %d bytes;\n", ++counter, len););

			/* copy necessary portion of current record */
			memcpy (tmp, res, len);
			tmp += len;

			/* Get next record */
			if (!ms_biff_query_next (q)) {
				g_warning ("unexpected end of stream;");
				return NULL;
			}

			/* We should only see DRAW records now */
			if (q->opcode != BIFF_MS_O_DRAWING &&
			    q->opcode != BIFF_MS_O_DRAWING_GROUP &&
			    q->opcode != BIFF_MS_O_DRAWING_SELECTION &&
			    q->opcode != BIFF_CHART_gelframe &&
			    q->opcode != BIFF_CONTINUE) {
				g_warning ("Unexpected record type 0x%x @ 0x%lx;", q->opcode, (long)q->streamPos);
				g_free (buffer);
				return NULL;
			}

			state->start_offset = state->end_offset;
			state->end_offset += q->length;
			state->segment_len = q->length;

			res = q->data;
			len = q->length;

		} while ((num_bytes - (tmp - buffer)) > len);

		/* Copy back stub */
		memcpy (tmp, res, num_bytes - (tmp-buffer));
		d (1, g_printerr ("record %d) add %d bytes;\n",
			      ++counter,
			      num_bytes - (int)(tmp-buffer)););
		return buffer;
	}

	return res;
}


ms_escher_read_AlignRule (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_ColorMRU (MSEscherState *state, MSEscherHeader *h)
{
	d (3 , {
		guint const num_Colours = h->instance;
		g_printerr ("There are %d Colours in a record with remaining length %d;\n",
			num_Colours, (h->len - COMMON_HEADER_LEN));
	});

	/* Colors in order from left to right.  */
	/* TODO : When we know how to parse a Colour record read these */
	return FALSE;
}


ms_escher_read_DeletedPspl (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_BSE (MSEscherState *state, MSEscherHeader *h)
{
	/* read the header */
	gboolean needs_free;
	guint8 const * data = ms_escher_get_data (state,
		h->offset + COMMON_HEADER_LEN, 36, &needs_free);
	guint8 const  win_type	= GSF_LE_GET_GUINT8  (data + 0);
	guint8 const  mac_type	= GSF_LE_GET_GUINT8  (data + 1);
	/*guint16 const tag	= GSF_LE_GET_GUINT16 (data + 18);*/
	guint32 const size	= GSF_LE_GET_GUINT32 (data + 20);
	guint32 const ref_count	= GSF_LE_GET_GUINT32 (data + 24);
	gint32 const del_offset	= GSF_LE_GET_GUINT32 (data + 28);
	guint8 const is_texture	= GSF_LE_GET_GUINT8  (data + 32);
	guint8 const name_len	= GSF_LE_GET_GUINT8  (data + 33);
	guint8 checksum[16]; /* RSA Data Security, Inc. MD4 Message-Digest Algorithm */
	char const *name = "unknown";
	int i;
	for (i = 16; i-- > 0;)
		checksum[i] = GSF_LE_GET_GUINT8 (data + 2 + i);

	d (0 , {
		g_printerr ("Win type = %s;\n", bliptype_name (win_type));
		g_printerr ("Mac type = %s;\n", bliptype_name (mac_type));
		g_printerr ("Size = 0x%x(=%d) RefCount = 0x%x DelayOffset = 0x%x '%s';\n",
			size, size, ref_count, del_offset, name);

		switch (is_texture) {
		case 0: g_printerr ("Default usage;\n"); break;
		case 1: g_printerr ("Is texture;\n"); break;
		default:g_printerr ("UNKNOWN USAGE : %d;\n", is_texture);
		}

		g_printerr ("Checksum = 0x");
		for (i = 0; i < 16; ++i)
			g_printerr ("%02x", checksum[i]);
		g_printerr (";\n");
	});

	/* Very red herring I think */
	if (name_len != 0) {
		g_printerr ("WARNING : Maybe a name?\n");
		/* name = biff_get_text (data+36, name_len, &txt_byte_len); */
	}

	/* Ignore empties */
	if (h->len > 36 + COMMON_HEADER_LEN)
		return ms_escher_read_container (state, h, 36, FALSE);

	/* Store a blank */
	ms_container_add_blip (state->container, NULL);
	return FALSE;
}


ms_escher_read_ClientRule (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_read_Textbox (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_xl_to_line_type (guint16 pattern)
{
	static GOLineDashType const dash_map []= {
		GO_LINE_SOLID,
		GO_LINE_DASH,
		GO_LINE_DOT,
		GO_LINE_DASH_DOT,
		GO_LINE_DASH_DOT_DOT,
		GO_LINE_DOT, /* ? */
		GO_LINE_DASH,
		GO_LINE_DASH, /* Long dash */
		GO_LINE_DASH_DOT,
		GO_LINE_DASH_DOT, /* Long dash dot */
		GO_LINE_DASH_DOT_DOT  /* Long dash dot dot */
	};

	return (pattern >= G_N_ELEMENTS (dash_map))
		? GO_LINE_SOLID
		: dash_map[pattern];
}


ms_escher_read_RegroupItems (MSEscherState *state, MSEscherHeader *h)
{
	/* Each shape in a drawing has a regroup ID (separate from the shape
	 * ID), so that the regroup command can find shapes that were once
	 * grouped. In order to handle nested grouping and ungrouping
	 * there is a table logging changes to regroup IDs. Each entry has an
	 * old ID and a new ID and records the change of all instances of the
	 * old ID to the new ID.
	 *
	 * The instance of an msofbtRegroupItems record contains the number of
	 * entries, and the record itself is just that many FRITs (File Regroup
	 * Items).
	 *
	 * typedef struct { FRID fridNew; FRID fridOld; } FRIT;
	 */

	return FALSE;
}


ms_escher_set_inst (GString *buf, gsize marker, guint inst)
{
	guint16 iv = GSF_LE_GET_GUINT16 (buf->str + marker);
	GSF_LE_SET_GUINT16 (buf->str + marker,
			    (iv & 15) | (inst << 4));
}


ms_escher_read_Anchor (MSEscherState *state, MSEscherHeader *h)
{
	/* only needed for clipboard info, like child anchor with dim 100,000 */
	return FALSE;
}


ms_escher_opt_start (GString *buf)
{
	gsize res = buf->len;
	guint8 tmp[8] = { 0x03,   0, 0xb, 0xf0, 0xde, 0xad, 0xbe, 0xef };
	g_string_append_len (buf, tmp, sizeof tmp);
	return res;
}


ms_escher_opt_end (GString *buf, gsize marker)
{
	/* Length does not include header.  */
	gsize len = buf->len - marker - 8;
	GSF_LE_SET_GUINT32 (buf->str + marker + 4, len);
}


bliptype_name (int const type)
{
	switch (type) {
	case 2:	 return "emf.gz";
	case 3:	 return "wmf.gz";
	case 4:	 return "pict.gz";
	case 5:	 return "jpg";
	case 6:	 return "png";
	case 7:	 return "dib";
	default: return "Unknown";
	}
}


ms_escher_blip_new (guint8 *data, guint32 len, char const *type, gboolean copy)
{
	MSEscherBlip *blip = g_new (MSEscherBlip, 1);

	blip->type = type;
	blip->data_len = len;
	blip->needs_free = TRUE;
	blip->data = copy ? g_memdup (data, len) : data;

	return blip;
}


ms_escher_read_CalloutRule (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_blip_free (MSEscherBlip *blip)
{
	blip->type = NULL;
	if (blip->needs_free) {
		g_free (blip->data);
		blip->needs_free = FALSE;
	}
	blip->data = NULL;
	g_free (blip);
}


ms_escher_clientdata (GString *buf)
{
	guint8 tmp[8] = { 0, 0, 0x11, 0xf0, 0xde, 0xad, 0xbe, 0xef };
	GSF_LE_SET_GUINT32 (tmp + 4, 0);
	g_string_append_len (buf, tmp, sizeof tmp);
}


ms_escher_spcontainer_end (GString *buf, gsize marker)
{
	/* Length does not include header.  */
	gsize len = buf->len - marker - 8;
	GSF_LE_SET_GUINT32 (buf->str + marker + 4, len);
}


ms_escher_read_OleObject (MSEscherState *state, MSEscherHeader *h)
{
	return FALSE;
}


ms_escher_opt_add_simple (GString *buf, gsize marker, guint16 pid, gint32 val)
{
	guint8 tmp[6];

	GSF_LE_SET_GUINT16 (tmp, pid);
	GSF_LE_SET_GINT32 (tmp + 2, val);
	g_string_append_len (buf, tmp, sizeof tmp);

	ms_escher_set_inst (buf, marker,
			    ms_escher_get_inst (buf, marker) + 1);
}


ms_escher_read_ClientData (MSEscherState *state, MSEscherHeader *h)
{
	guint16 opcode;
	int has_next_record;

	g_return_val_if_fail (h->len == COMMON_HEADER_LEN, TRUE);
	g_return_val_if_fail (h->offset + h->len == state->end_offset, TRUE);

	/* Read the OBJ, be VERY careful until we are sure of the state */
	has_next_record = ms_biff_query_peek_next (state->q, &opcode);
	g_return_val_if_fail (has_next_record, TRUE);
	g_return_val_if_fail (opcode == BIFF_OBJ, TRUE);
	has_next_record = ms_biff_query_next (state->q);
	g_return_val_if_fail (has_next_record, TRUE);

	/* The object takes responsibility for the attributes */
	h->release_attrs = FALSE;
	if (ms_read_OBJ (state->q, state->container, h->attrs)) {
		h->attrs = NULL;  /* It got deleted.  */
		return TRUE;
	}

	return FALSE;
}


ms_escher_read_ClientAnchor (MSEscherState *state, MSEscherHeader *h)
{
	gboolean needs_free;
	guint8 const *data;

	g_return_val_if_fail (state != NULL, TRUE);
	g_return_val_if_fail (state->container != NULL, TRUE);

	data = ms_escher_get_data (state, h->offset + COMMON_HEADER_LEN,
		MS_ANCHOR_SIZE, &needs_free);
#if 0
	gsf_mem_dump (data, MS_ANCHOR_SIZE);
#endif
	if (data) {
		guint8 *anchor = g_memdup (data, MS_ANCHOR_SIZE);

		ms_escher_header_add_attr (h,
			ms_obj_attr_new_ptr (MS_OBJ_ATTR_ANCHOR, anchor));

		if (needs_free)
			g_free ((guint8 *)data);
		return FALSE;
	}

	return TRUE;
}


ms_escher_read_DggContainer (MSEscherState *state, MSEscherHeader *h)
{
	return ms_escher_read_container (state, h, 0, FALSE);
}


ms_escher_clientanchor (GString *buf, SheetObjectAnchor const *anchor)
{
	guint8 tmp[26] = {
		0, 0, 0x10, 0xf0, 0xde, 0xad, 0xbe, 0xef,
		0, 0,
		0,0, 0,0,
		0,0, 0,0,
		0,0, 0,0,
		0,0, 0,0
	};
	guint8 *p = tmp + 10;
	GSF_LE_SET_GUINT32 (tmp + 4, sizeof (tmp) - 8);

	GSF_LE_SET_GUINT16 (p +  0, anchor->cell_bound.start.col);
	GSF_LE_SET_GUINT16 (p +  2, (guint16)(anchor->offset[0]*1024. + .5));
	GSF_LE_SET_GUINT16 (p +  4, anchor->cell_bound.start.row);
	GSF_LE_SET_GUINT16 (p +  6, (guint16)(anchor->offset[1]*256. + .5));
	GSF_LE_SET_GUINT16 (p +  8, anchor->cell_bound.end.col);
	GSF_LE_SET_GUINT16 (p + 10, (guint16)(anchor->offset[2]*1024. + .5));
	GSF_LE_SET_GUINT16 (p + 12, anchor->cell_bound.end.row);
	GSF_LE_SET_GUINT16 (p + 14, (guint16)(anchor->offset[3]*256. + .5));
	g_string_append_len (buf, tmp, sizeof tmp);
}


ms_escher_sp (GString *buf, guint32 spid, guint16 shape, guint32 flags)
{
	gsize marker = buf->len;
	guint8 tmp[16] = {
		0xa2,  0xc, 0xa, 0xf0, 0xde, 0xad, 0xbe, 0xef,
		0, 0, 0, 0, 0, 0, 0, 0
	};
	GSF_LE_SET_GUINT32 (tmp + 4, sizeof (tmp) - 8);
	GSF_LE_SET_GUINT32 (tmp + 8, spid);
	GSF_LE_SET_GUINT32 (tmp + 12, flags);
	g_string_append_len (buf, tmp, sizeof tmp);
	ms_escher_set_inst (buf, marker, shape);
}


security: fix for vulnerability CVE-2014-0017

When accepting a new connection, a forking server based on libssh forks
and the child process handles the request. The RAND_bytes() function of
openssl doesn't reset its state after the fork, but simply adds the
current process id (getpid) to the PRNG state, which is not guaranteed
to be unique.
This can cause several children to end up with same PRNG state which is
a security issue.

static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){
  if (cipher->key == NULL) {
    if (alloc_key(cipher) < 0) {
      return -1;
    }

    if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_BLOWFISH,
        GCRY_CIPHER_MODE_CBC, 0)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setkey(cipher->key[0], key, 16)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
  }

  return 0;
}

The RAND_bytes function in libssh before 0.6.3, when forking is enabled, does not properly reset the state of the OpenSSL pseudo-random number generator (PRNG), which causes the state to be shared between children processes and allows local users to obtain sensitive information by leveraging a pid collision.


static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}


void ssh_bind_fd_toaccept(ssh_bind sshbind) {
  sshbind->toaccept = 1;
}


static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
    unsigned long len) {
  gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}


static const EVP_MD *nid_to_evpmd(int nid)
{
    switch (nid) {
        case NID_X9_62_prime256v1:
            return EVP_sha256();
        case NID_secp384r1:
            return EVP_sha384();
        case NID_secp521r1:
            return EVP_sha512();
        default:
            return NULL;
    }

    return NULL;
}


static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
    unsigned long len) {
  AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_DECRYPT);
}


static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}


static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  DES_ede3_cbc_encrypt(in, out, len, cipher->key,
      (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
      cipher->IV, 0);
}


void hmac_update(HMACCTX ctx, const void *data, unsigned long len) {
  HMAC_Update(ctx, data, len);
}


static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
    unsigned long len) {
  AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_ENCRYPT);
}


static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Encrypt IV before", cipher->IV, 24);
#endif
  DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
  DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->IV + 8), 0);
  DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->IV + 16), 1);
#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Encrypt IV after", cipher->IV, 24);
#endif
}


void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
  sshbind->bindfd = fd;
}


EVPCTX evp_init(int nid)
{
    const EVP_MD *evp_md = nid_to_evpmd(nid);

    EVPCTX ctx = malloc(sizeof(EVP_MD_CTX));
    if (ctx == NULL) {
        return NULL;
    }

    EVP_DigestInit(ctx, evp_md);

    return ctx;
}


void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
  gcry_md_write(ctx->ctx,data,len);
}


static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
    int port) {
    char port_c[6];
    struct addrinfo *ai;
    struct addrinfo hints;
    int opt = 1;
    socket_t s;
    int rc;

    ZERO_STRUCT(hints);

    hints.ai_flags = AI_PASSIVE;
    hints.ai_socktype = SOCK_STREAM;

    snprintf(port_c, 6, "%d", port);
    rc = getaddrinfo(hostname, port_c, &hints, &ai);
    if (rc != 0) {
        ssh_set_error(sshbind,
                      SSH_FATAL,
                      "Resolving %s: %s", hostname, gai_strerror(rc));
        return -1;
    }

    s = socket (ai->ai_family,
                           ai->ai_socktype,
                           ai->ai_protocol);
    if (s == SSH_INVALID_SOCKET) {
        ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno));
        freeaddrinfo (ai);
        return -1;
    }

    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                   (char *)&opt, sizeof(opt)) < 0) {
        ssh_set_error(sshbind,
                      SSH_FATAL,
                      "Setting socket options failed: %s",
                      strerror(errno));
        freeaddrinfo (ai);
        close(s);
        return -1;
    }

    if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
        ssh_set_error(sshbind,
                      SSH_FATAL,
                      "Binding to %s:%d: %s",
                      hostname,
                      port,
                      strerror(errno));
        freeaddrinfo (ai);
        close(s);
        return -1;
    }

    freeaddrinfo (ai);
    return s;
}


static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_DECRYPT);
}


MD5CTX md5_init(void) {
  MD5CTX c = NULL;
  gcry_md_open(&c, GCRY_MD_MD5, 0);

  return c;
}


static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}


static int des3_1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
  if (cipher->key == NULL) {
    if (alloc_key(cipher) < 0) {
      return -1;
    }
    if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES,
          GCRY_CIPHER_MODE_CBC, 0)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setkey(cipher->key[0], key, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }

    if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES,
          GCRY_CIPHER_MODE_CBC, 0)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setkey(cipher->key[1], (unsigned char *)key + 8, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setiv(cipher->key[1], (unsigned char *)IV + 8, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }

    if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES,
          GCRY_CIPHER_MODE_CBC, 0)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setkey(cipher->key[2], (unsigned char *)key + 16, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
    if (gcry_cipher_setiv(cipher->key[2], (unsigned char *)IV + 16, 8)) {
      SAFE_FREE(cipher->key);
      return -1;
    }
  }

  return 0;
}


void md5_final(unsigned char *md, MD5CTX c) {
  MD5_Final(md,c);
  SAFE_FREE(c);
}


ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
  ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct));
  ctx->mac_type=type;
  switch(type){
    case SSH_MAC_SHA1:
      gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0);
      break;
    case SSH_MAC_SHA256:
      gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0);
      break;
    case SSH_MAC_SHA384:
      gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0);
      break;
    case SSH_MAC_SHA512:
      gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0);
      break;
    default:
      SAFE_FREE(ctx);
      return NULL;
  }
  return ctx;
}


static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
    unsigned long len) {
  gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}


static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  DES_ede3_cbc_encrypt(in, out, len, cipher->key,
      (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
      cipher->IV, 1);
}


static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_ENCRYPT);
}


static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
                           unsigned long len){

  DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
}


static int ssh_bind_import_keys(ssh_bind sshbind) {
  int rc;

  if (sshbind->ecdsakey == NULL &&
      sshbind->dsakey == NULL &&
      sshbind->rsakey == NULL) {
      ssh_set_error(sshbind, SSH_FATAL,
                    "ECDSA, DSA, or RSA host key file must be set");
      return SSH_ERROR;
  }

#ifdef HAVE_ECC
  if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
      rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
                                       NULL,
                                       NULL,
                                       NULL,
                                       &sshbind->ecdsa);
      if (rc == SSH_ERROR || rc == SSH_EOF) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "Failed to import private ECDSA host key");
          return SSH_ERROR;
      }

      if (ssh_key_type(sshbind->ecdsa) != SSH_KEYTYPE_ECDSA) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "The ECDSA host key has the wrong type");
          ssh_key_free(sshbind->ecdsa);
          sshbind->ecdsa = NULL;
          return SSH_ERROR;
      }
  }
#endif

  if (sshbind->dsa == NULL && sshbind->dsakey != NULL) {
      rc = ssh_pki_import_privkey_file(sshbind->dsakey,
                                       NULL,
                                       NULL,
                                       NULL,
                                       &sshbind->dsa);
      if (rc == SSH_ERROR || rc == SSH_EOF) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "Failed to import private DSA host key");
          return SSH_ERROR;
      }

      if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "The DSA host key has the wrong type: %d",
                  ssh_key_type(sshbind->dsa));
          ssh_key_free(sshbind->dsa);
          sshbind->dsa = NULL;
          return SSH_ERROR;
      }
  }

  if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
      rc = ssh_pki_import_privkey_file(sshbind->rsakey,
                                       NULL,
                                       NULL,
                                       NULL,
                                       &sshbind->rsa);
      if (rc == SSH_ERROR || rc == SSH_EOF) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "Failed to import private RSA host key");
          return SSH_ERROR;
      }

      if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA &&
          ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA1) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "The RSA host key has the wrong type");
          ssh_key_free(sshbind->rsa);
          sshbind->rsa = NULL;
          return SSH_ERROR;
      }
  }

  return SSH_OK;
}


static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}


int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
    void *userdata){
  if (sshbind == NULL) {
    return SSH_ERROR;
  }
  if (callbacks == NULL) {
    ssh_set_error_invalid(sshbind);
    return SSH_ERROR;
  }
  if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
    ssh_set_error(sshbind,SSH_FATAL,
        "Invalid callback passed in (badly initialized)");
    return SSH_ERROR;
  }
  sshbind->bind_callbacks = callbacks;
  sshbind->bind_callbacks_userdata=userdata;
  return 0;
}


int ssh_bind_listen(ssh_bind sshbind) {
  const char *host;
  socket_t fd;
  int rc;

  if (ssh_init() < 0) {
    ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed");
    return -1;
  }

  rc = ssh_bind_import_keys(sshbind);
  if (rc != SSH_OK) {
    return SSH_ERROR;
  }

  if (sshbind->bindfd == SSH_INVALID_SOCKET) {
      host = sshbind->bindaddr;
      if (host == NULL) {
          host = "0.0.0.0";
      }

      fd = bind_socket(sshbind, host, sshbind->bindport);
      if (fd == SSH_INVALID_SOCKET) {
          ssh_key_free(sshbind->dsa);
          sshbind->dsa = NULL;
          ssh_key_free(sshbind->rsa);
          sshbind->rsa = NULL;
          return -1;
      }
      sshbind->bindfd = fd;

      if (listen(fd, 10) < 0) {
          ssh_set_error(sshbind, SSH_FATAL,
                  "Listening to socket %d: %s",
                  fd, strerror(errno));
          close(fd);
          ssh_key_free(sshbind->dsa);
          sshbind->dsa = NULL;
          ssh_key_free(sshbind->rsa);
          sshbind->rsa = NULL;
          return -1;
      }
  } else {
      SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket");
  }
  return 0;
}


struct ssh_cipher_struct *ssh_get_ciphertab(void)
{
  return ssh_ciphertab;
}


MD5CTX md5_init(void) {
  MD5CTX c = malloc(sizeof(*c));
  if (c == NULL) {
    return NULL;
  }

  MD5_Init(c);

  return c;
}


static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
  gcry_cipher_decrypt(cipher->key[1], in, len, out, len);
  gcry_cipher_encrypt(cipher->key[2], out, len, in, len);
}


void sha256(unsigned char *digest, int len, unsigned char *hash){
  gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
}


SHA256CTX sha256_init(void){
  SHA256CTX c = malloc(sizeof(*c));
  if (c == NULL) {
    return NULL;
  }
  SHA256_Init(c);

  return c;
}


static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  gcry_cipher_decrypt(cipher->key[2], out, len, in, len);
  gcry_cipher_encrypt(cipher->key[1], in, len, out, len);
  gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}


socket_t ssh_bind_get_fd(ssh_bind sshbind) {
  return sshbind->bindfd;
}


void md5_final(unsigned char *md, MD5CTX c) {
  gcry_md_final(c);
  memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN);
  gcry_md_close(c);
}


ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){
  if(sshbind->poll)
    return sshbind->poll;
  sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN,
      ssh_bind_poll_callback,sshbind);
  return sshbind->poll;
}


void sha1(unsigned char *digest, int len, unsigned char *hash) {
  SHA1(digest, len, hash);
}


void hmac_update(HMACCTX c, const void *data, unsigned long len) {
  gcry_md_write(c, data, len);
}