| Index: chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc
 | 
| ===================================================================
 | 
| --- chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc	(revision 0)
 | 
| +++ chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc	(revision 0)
 | 
| @@ -0,0 +1,475 @@
 | 
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "chrome/browser/password_manager/keyring_proxy/keyring_proxy.h"
 | 
| +
 | 
| +// This file is part of a very small helper binary that runs without any
 | 
| +// dependencies on base. Thus we can't use normal logging; we use assert.
 | 
| +#include <assert.h>
 | 
| +#include <errno.h>
 | 
| +#include <glib.h>
 | 
| +#include <glib-object.h>
 | 
| +#include <gnome-keyring.h>
 | 
| +#include <inttypes.h>
 | 
| +#include <stdio.h>
 | 
| +#include <stdlib.h>
 | 
| +#include <string.h>
 | 
| +
 | 
| +#include <map>
 | 
| +#include <string>
 | 
| +#include <vector>
 | 
| +
 | 
| +#include "chrome/browser/password_manager/keyring_proxy/gnome_keyring_loader.h"
 | 
| +#include "chrome/browser/password_manager/keyring_proxy/message_reader.h"
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// This is the approximation of PasswordForm that we store in
 | 
| +// GNOME Keyring, using the schema in kGnomeSchema below.
 | 
| +struct BasicPasswordForm {
 | 
| +  std::string origin;
 | 
| +  std::string action;
 | 
| +  std::string username_element;
 | 
| +  std::string username_value;
 | 
| +  std::string password_element;
 | 
| +  std::string password_value;
 | 
| +  std::string submit_element;
 | 
| +  std::string signon_realm;
 | 
| +  uint32_t ssl_valid;
 | 
| +  uint32_t preferred;
 | 
| +  std::string date_created;
 | 
| +  uint32_t blacklisted_by_user;
 | 
| +  uint32_t scheme;
 | 
| +};
 | 
| +
 | 
| +// Convert the attributes of a given keyring entry into a BasicPasswordForm.
 | 
| +// Note: does *not* get the actual password, as that is not a key attribute!
 | 
| +void FormFromAttributes(GnomeKeyringAttributeList* attrs,
 | 
| +                        BasicPasswordForm* form) {
 | 
| +  // Read the string and int attributes into the appropriate map.
 | 
| +  std::map<std::string, std::string> string_attr_map;
 | 
| +  std::map<std::string, uint32_t> uint_attr_map;
 | 
| +  for (guint i = 0; i < attrs->len; ++i) {
 | 
| +    GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
 | 
| +    if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
 | 
| +      string_attr_map[attr.name] = attr.value.string;
 | 
| +    else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
 | 
| +      uint_attr_map[attr.name] = attr.value.integer;
 | 
| +  }
 | 
| +
 | 
| +  form->origin = string_attr_map["origin_url"];
 | 
| +  form->action = string_attr_map["action_url"];
 | 
| +  form->username_element = string_attr_map["username_element"];
 | 
| +  form->username_value = string_attr_map["username_value"];
 | 
| +  form->password_element = string_attr_map["password_element"];
 | 
| +  form->submit_element = string_attr_map["submit_element"];
 | 
| +  form->signon_realm = string_attr_map["signon_realm"];
 | 
| +  form->ssl_valid = uint_attr_map["ssl_valid"];
 | 
| +  form->preferred = uint_attr_map["preferred"];
 | 
| +  form->date_created = string_attr_map["date_created"];
 | 
| +  form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
 | 
| +  form->scheme = uint_attr_map["scheme"];
 | 
| +}
 | 
| +
 | 
| +// Parse all the results from the given GList into a list of BasicPasswordForms.
 | 
| +void ConvertFormList(GList* found, std::vector<BasicPasswordForm>* forms) {
 | 
| +  GList* element = g_list_first(found);
 | 
| +  while (element != NULL) {
 | 
| +    GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
 | 
| +    GnomeKeyringAttributeList* attrs = data->attributes;
 | 
| +
 | 
| +    forms->resize(forms->size() + 1);
 | 
| +    FormFromAttributes(attrs, &forms->back());
 | 
| +    if (data->secret)
 | 
| +      forms->back().password_value = data->secret;
 | 
| +
 | 
| +    element = g_list_next(element);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// Schema is analagous to the fields in BasicPasswordForm.
 | 
| +const GnomeKeyringPasswordSchema kGnomeSchema = {
 | 
| +  GNOME_KEYRING_ITEM_GENERIC_SECRET, {
 | 
| +    { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
 | 
| +    { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
 | 
| +    { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
 | 
| +    { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
 | 
| +    // This field is used to disambiguate passwords for different profiles.
 | 
| +    { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
 | 
| +    { NULL }
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +// A simple class that encapsulates the boilerplate needed to tell
 | 
| +// GLib how to watch a file descriptor for reading.
 | 
| +class FDReadSource : public GSource {
 | 
| + public:
 | 
| +  static GSource* Allocate(int fd) {
 | 
| +    GSource* source = g_source_new(&functions, sizeof(FDReadSource));
 | 
| +    FDReadSource* fdsource = static_cast<FDReadSource*>(source);
 | 
| +    fdsource->pollfd.fd = fd;
 | 
| +    fdsource->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
 | 
| +    g_source_add_poll(source, &fdsource->pollfd);
 | 
| +    return source;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  // We don't allocate or free FDReadSource ourselves. Instead, g_source_new()
 | 
| +  // will do that for us. So, make all the constructors/destructors private.
 | 
| +  FDReadSource();
 | 
| +  FDReadSource(const FDReadSource&);
 | 
| +  void operator=(const FDReadSource&);
 | 
| +  ~FDReadSource();
 | 
| +
 | 
| +  static gboolean Prepare(GSource* source, gint* timeout) {
 | 
| +    *timeout = -1;
 | 
| +    return FALSE;
 | 
| +  }
 | 
| +
 | 
| +  static gboolean Check(GSource* source) {
 | 
| +    FDReadSource* fdsource = static_cast<FDReadSource*>(source);
 | 
| +    return !!(fdsource->pollfd.revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
 | 
| +  }
 | 
| +
 | 
| +  static gboolean Dispatch(GSource* source,
 | 
| +                           GSourceFunc callback,
 | 
| +                           gpointer user_data) {
 | 
| +    if (!callback)
 | 
| +      return FALSE;  // No callback? Remove the source so it doesn't spin.
 | 
| +    return callback(user_data);
 | 
| +  }
 | 
| +
 | 
| +  static GSourceFuncs functions;
 | 
| +
 | 
| +  GPollFD pollfd;
 | 
| +};
 | 
| +
 | 
| +GSourceFuncs FDReadSource::functions = {
 | 
| +  Prepare, Check, Dispatch, NULL  // .finalize
 | 
| +};
 | 
| +
 | 
| +}  // anonymous namespace
 | 
| +
 | 
| +namespace keyring_proxy {
 | 
| +
 | 
| +class MessageProcessor : public GnomeKeyringLoader {
 | 
| + public:
 | 
| +  explicit MessageProcessor(FILE* output) : output_(output) {}
 | 
| +
 | 
| +  bool Process(const std::vector<std::string>& lines) {
 | 
| +    if (lines.size() < 1) {
 | 
| +      // Although an empty message is technically an error, we choose to be
 | 
| +      // lenient and ignore it. It makes manual testing easier.
 | 
| +      return true;
 | 
| +    }
 | 
| +    if (lines[0].length() < 2) {
 | 
| +      // The first line must have a command character followed by an id.
 | 
| +      return false;
 | 
| +    }
 | 
| +    int id = atoi(&lines[0].c_str()[1]);
 | 
| +    if (id <= 0) {
 | 
| +      // The id must be at least 1.
 | 
| +      return false;
 | 
| +    }
 | 
| +    if (lines[0][0] == KeyringProxy::kAddLoginCommand) {
 | 
| +      // Store a password. (AddLogin)
 | 
| +      if (lines.size() != 16)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_store_password(
 | 
| +          &kGnomeSchema,
 | 
| +          NULL,  // Default keyring.
 | 
| +          &lines[1].c_str()[1],  // Display name.
 | 
| +          &lines[2].c_str()[1],  // Password.
 | 
| +          OnOperationDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "origin_url", &lines[3].c_str()[1],
 | 
| +          "action_url", &lines[4].c_str()[1],
 | 
| +          "username_element", &lines[5].c_str()[1],
 | 
| +          "username_value", &lines[6].c_str()[1],
 | 
| +          "password_element", &lines[7].c_str()[1],
 | 
| +          "submit_element", &lines[8].c_str()[1],
 | 
| +          "signon_realm", &lines[9].c_str()[1],
 | 
| +          "ssl_valid", lines[10] != "0",
 | 
| +          "preferred", lines[11] != "0",
 | 
| +          "date_created", &lines[12].c_str()[1],
 | 
| +          "blacklisted_by_user", lines[13] != "0",
 | 
| +          "scheme", atoi(lines[14].c_str()),
 | 
| +          "application", lines[15].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kRemoveLoginCommand) {
 | 
| +      // Delete a password. (RemoveLogin)
 | 
| +      if (lines.size() != 9)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_delete_password(
 | 
| +          &kGnomeSchema,
 | 
| +          OnOperationDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "origin_url", &lines[1].c_str()[1],
 | 
| +          "action_url", &lines[2].c_str()[1],
 | 
| +          "username_element", &lines[3].c_str()[1],
 | 
| +          "username_value", &lines[4].c_str()[1],
 | 
| +          "password_element", &lines[5].c_str()[1],
 | 
| +          "submit_element", &lines[6].c_str()[1],
 | 
| +          "signon_realm", &lines[7].c_str()[1],
 | 
| +          "application", lines[8].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kAddLoginSearchCommand) {
 | 
| +      // Find passwords before adding a new one. (AddLoginSearch)
 | 
| +      if (lines.size() != 8)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_find_itemsv(
 | 
| +          GNOME_KEYRING_ITEM_GENERIC_SECRET,
 | 
| +          OnFindDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[1].c_str()[1],
 | 
| +          "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[2].c_str()[1],
 | 
| +          "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[3].c_str()[1],
 | 
| +          "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[4].c_str()[1],
 | 
| +          "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[5].c_str()[1],
 | 
| +          "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[6].c_str()[1],
 | 
| +          "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          lines[7].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kUpdateLoginSearchCommand) {
 | 
| +      // Find passwords to update. (UpdateLoginSearch)
 | 
| +      if (lines.size() != 7)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_find_itemsv(
 | 
| +          GNOME_KEYRING_ITEM_GENERIC_SECRET,
 | 
| +          OnFindDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[1].c_str()[1],
 | 
| +          "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[2].c_str()[1],
 | 
| +          "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[3].c_str()[1],
 | 
| +          "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[4].c_str()[1],
 | 
| +          "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[5].c_str()[1],
 | 
| +          "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          lines[6].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kGetLoginsCommand) {
 | 
| +      // Find passwords to autofill. (GetLogins)
 | 
| +      if (lines.size() != 3)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_find_itemsv(
 | 
| +          GNOME_KEYRING_ITEM_GENERIC_SECRET,
 | 
| +          OnFindDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          &lines[1].c_str()[1],
 | 
| +          "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          lines[2].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kGetLoginsListCommand) {
 | 
| +      // Find passwords to show in the password manager. (GetLoginsList)
 | 
| +      if (lines.size() != 3)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      gnome_keyring_find_itemsv(
 | 
| +          GNOME_KEYRING_ITEM_GENERIC_SECRET,
 | 
| +          OnFindDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
 | 
| +          lines[1] != "0",
 | 
| +          "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          lines[2].c_str(),
 | 
| +          NULL);
 | 
| +    } else if (lines[0][0] == KeyringProxy::kGetAllLoginsCommand) {
 | 
| +      // Find all passwords. (GetAllLogins)
 | 
| +      if (lines.size() != 2)
 | 
| +        return false;
 | 
| +      Request* request = AllocateRequest(id);
 | 
| +      if (!request)
 | 
| +        return false;
 | 
| +      // We need to search for something, otherwise we get no results - so
 | 
| +      // we search for the fixed application string.
 | 
| +      gnome_keyring_find_itemsv(
 | 
| +          GNOME_KEYRING_ITEM_GENERIC_SECRET,
 | 
| +          OnFindDone,
 | 
| +          request,  // data
 | 
| +          NULL,     // destroy_data
 | 
| +          "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
 | 
| +          lines[1].c_str(),
 | 
| +          NULL);
 | 
| +    } else {
 | 
| +      // Unexpected command line.
 | 
| +      return false;
 | 
| +    }
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  struct Request {
 | 
| +    int id;
 | 
| +    MessageProcessor* processor;
 | 
| +
 | 
| +    Request(int id, MessageProcessor* processor)
 | 
| +        : id(id), processor(processor) {
 | 
| +    }
 | 
| +  };
 | 
| +
 | 
| +  Request* AllocateRequest(int id) {
 | 
| +    std::pair<std::map<int, Request>::iterator, bool> result =
 | 
| +        requests_.insert(std::make_pair(id, Request(id, this)));
 | 
| +    return result.second ? &result.first->second : NULL;
 | 
| +  }
 | 
| +
 | 
| +  void ReplyAndFreeRequest(Request* request,
 | 
| +                           const std::vector<std::string>& reply) {
 | 
| +    for (size_t i = 0; i < reply.size(); ++i)
 | 
| +      fprintf(output_, "%s\n", reply[i].c_str());
 | 
| +    fprintf(output_, "\n");
 | 
| +    fflush(output_);
 | 
| +    requests_.erase(request->id);
 | 
| +  }
 | 
| +
 | 
| +  static void OnOperationDone(GnomeKeyringResult result, gpointer data) {
 | 
| +    Request* request = static_cast<Request*>(data);
 | 
| +    std::vector<std::string> reply;
 | 
| +    char line[32];
 | 
| +    snprintf(line, sizeof(line), "%d %d", request->id, result);
 | 
| +    reply.push_back(line);
 | 
| +    request->processor->ReplyAndFreeRequest(request, reply);
 | 
| +  }
 | 
| +
 | 
| +  static void OnFindDone(GnomeKeyringResult result,
 | 
| +                         GList* list, gpointer data) {
 | 
| +    Request* request = static_cast<Request*>(data);
 | 
| +    std::vector<std::string> reply;
 | 
| +    char line[32];
 | 
| +    snprintf(line, sizeof(line), "%d %d", request->id, result);
 | 
| +    reply.push_back(line);
 | 
| +    // |list| will be freed after this callback returns, so convert it now.
 | 
| +    std::vector<BasicPasswordForm> forms;
 | 
| +    ConvertFormList(list, &forms);
 | 
| +    for (size_t i = 0; i < forms.size(); ++i) {
 | 
| +      reply.push_back("+" + forms[i].origin);
 | 
| +      reply.push_back("+" + forms[i].action);
 | 
| +      reply.push_back("+" + forms[i].username_element);
 | 
| +      reply.push_back("+" + forms[i].username_value);
 | 
| +      reply.push_back("+" + forms[i].password_element);
 | 
| +      reply.push_back("+" + forms[i].password_value);
 | 
| +      reply.push_back("+" + forms[i].submit_element);
 | 
| +      reply.push_back("+" + forms[i].signon_realm);
 | 
| +      reply.push_back(forms[i].ssl_valid ? "1" : "0");
 | 
| +      reply.push_back(forms[i].preferred ? "1" : "0");
 | 
| +      reply.push_back("+" + forms[i].date_created);
 | 
| +      reply.push_back(forms[i].blacklisted_by_user ? "1" : "0");
 | 
| +      snprintf(line, sizeof(line), "%u", forms[i].scheme);
 | 
| +      reply.push_back(line);
 | 
| +    }
 | 
| +    request->processor->ReplyAndFreeRequest(request, reply);
 | 
| +  }
 | 
| +
 | 
| +  FILE* output_;
 | 
| +  std::map<int, Request> requests_;
 | 
| +};
 | 
| +
 | 
| +KeyringProxy::KeyringProxy(int fd, FILE* output)
 | 
| +    : fd_(fd), processor_(new MessageProcessor(output)) {
 | 
| +  main_loop_ = g_main_loop_new(NULL, FALSE);  // is_running
 | 
| +  input_ = FDReadSource::Allocate(fd_);
 | 
| +  g_source_set_callback(input_, DataReady, this, NULL);
 | 
| +  g_source_attach(input_, NULL);
 | 
| +}
 | 
| +
 | 
| +KeyringProxy::~KeyringProxy() {
 | 
| +  g_source_remove_by_user_data(this);
 | 
| +  g_source_unref(input_);
 | 
| +  g_main_loop_unref(main_loop_);
 | 
| +}
 | 
| +
 | 
| +bool KeyringProxy::Init() {
 | 
| +  return LoadGnomeKeyring() && gnome_keyring_is_available();
 | 
| +}
 | 
| +
 | 
| +void KeyringProxy::Run() {
 | 
| +  g_main_loop_run(main_loop_);
 | 
| +}
 | 
| +
 | 
| +void KeyringProxy::Stop() {
 | 
| +  if (!g_main_loop_is_running(main_loop_))
 | 
| +    fprintf(stderr, "%s:%d: main loop not running!\n", __FILE__, __LINE__);
 | 
| +  g_main_loop_quit(main_loop_);
 | 
| +}
 | 
| +
 | 
| +void KeyringProxy::ReadData() {
 | 
| +  // We don't need to read all the available data. If there is still data left
 | 
| +  // after we return, then we'll get called again because the descriptor will
 | 
| +  // still be ready to read. So just read a fixed amount, for simplicity.
 | 
| +  char buffer[256];
 | 
| +  ssize_t available = read(fd_, buffer, sizeof(buffer));
 | 
| +  if (available > 0) {
 | 
| +    // Got some data. Handle it. Note that this may not be a complete message.
 | 
| +    ssize_t used = 0;
 | 
| +    while (used < available) {
 | 
| +      ssize_t handled = reader_.HandleData(&buffer[used], available);
 | 
| +      assert(handled > 0);
 | 
| +      used += handled;
 | 
| +      available -= handled;
 | 
| +      if (reader_.is_complete()) {
 | 
| +        if (!processor_->Process(reader_.lines())) {
 | 
| +          // A fatal processing error occurred. Shut down the proxy.
 | 
| +          Stop();
 | 
| +        }
 | 
| +        reader_.Reset();
 | 
| +      } else {
 | 
| +        assert(available == 0);
 | 
| +      }
 | 
| +    }
 | 
| +  } else if (available == 0 || errno != EINTR) {
 | 
| +    // EOF, or an unexpected error. Shut down the proxy.
 | 
| +    Stop();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +gboolean KeyringProxy::DataReady(gpointer data) {
 | 
| +  static_cast<KeyringProxy*>(data)->ReadData();
 | 
| +  // We always keep this source. If we run out of input, we should quit.
 | 
| +  return TRUE;
 | 
| +}
 | 
| +
 | 
| +}  // namespace keyring_proxy
 | 
| 
 | 
| Property changes on: chrome/browser/password_manager/keyring_proxy/keyring_proxy.cc
 | 
| ___________________________________________________________________
 | 
| Added: svn:eol-style
 | 
|    + LF
 | 
| 
 | 
| 
 |