aboutsummaryrefslogtreecommitdiffstats
path: root/fetchmail-FAQ.html
Commit message (Expand)AuthorAgeFilesLines
...
* Fix a markup error.Eric S. Raymond1997-09-111-3/+3
* Added warning about Microsoft Exchange.Eric S. Raymond1997-09-111-3/+28
* Added O4.Eric S. Raymond1997-09-071-2/+23
* Update.Eric S. Raymond1997-09-031-3/+7
* Typo fix.Eric S. Raymond1997-09-031-3/+3
* Fix some mailto URLs.Eric S. Raymond1997-09-031-4/+4
* Added O3.Eric S. Raymond1997-09-031-3/+10
* Better exim support.Eric S. Raymond1997-08-111-4/+5
* Simplified bug report.Eric S. Raymond1997-08-101-5/+4
* Updated to the new format.Eric S. Raymond1997-08-051-4/+14
* Ready for release.Eric S. Raymond1997-08-021-28/+21
* Simplify the computation of truename.Eric S. Raymond1997-07-311-14/+44
* Bump version.Eric S. Raymond1997-07-301-2/+2
* Ready to ship 4.0.4.Eric S. Raymond1997-07-291-2/+2
* Added Al Youngwerth as a backup maintainer.Eric S. Raymond1997-07-291-5/+6
* Paragraph fix.Eric S. Raymond1997-07-251-2/+2
* Added the via option.Eric S. Raymond1997-07-241-3/+3
* Added a note about smail.Eric S. Raymond1997-07-241-2/+14
* Ready to release 4.0.1.Eric S. Raymond1997-07-161-2/+2
* Added Michael's recipe.Eric S. Raymond1997-07-081-4/+57
* More good stuff.Eric S. Raymond1997-07-081-2/+15
* Documentation upgeade.Eric S. Raymond1997-07-021-4/+7
* Fair warning about multidrop.Eric S. Raymond1997-07-021-6/+12
* version bump.Eric S. Raymond1997-07-011-2/+2
* Renumber O section.Eric S. Raymond1997-06-251-5/+5
* Whitespace fix.Eric S. Raymond1997-06-251-2/+2
* Added Charlie Brady's SSH recipe.Eric S. Raymond1997-06-251-3/+57
* Queue restoration advice.Eric S. Raymond1997-06-241-2/+21
* Nail biff.Eric S. Raymond1997-06-181-1/+15
* Added O5.Eric S. Raymond1997-06-181-32/+56
* Handle multiple To: headers.Eric S. Raymond1997-06-161-15/+3
* Small fix.Eric S. Raymond1997-06-141-2/+2
* Don't rely on the size being in POP3's fetch response any more.Eric S. Raymond1997-06-141-25/+3
* Added T5.Eric S. Raymond1997-06-141-2/+14
* Handle slavishly RFC1725-conformant servers.Eric S. Raymond1997-06-131-3/+27
* The core-dump bug is back.Eric S. Raymond1997-06-111-1/+16
* Normal build is now with POP2 disabled and optimization on.Eric S. Raymond1997-06-111-2/+9
* First step towards netrc fix.Eric S. Raymond1997-06-101-54/+33
* Designate Rob Funk as backup maintainer.Eric S. Raymond1997-06-091-1/+6
* Scott reports the R5 bug in 3.9.6.Eric S. Raymond1997-06-051-4/+5
* Added T4.Eric S. Raymond1997-06-041-2/+10
* Correct a bad link.Eric S. Raymond1997-05-291-2/+2
* More explicit info about the reflector.Eric S. Raymond1997-05-291-5/+7
* Cosmetic fix.Eric S. Raymond1997-05-281-2/+2
* Fix broken anchor.Eric S. Raymond1997-05-281-2/+2
* Better stuff on failed SMTP connections.Eric S. Raymond1997-05-281-23/+35
* Added F5.Eric S. Raymond1997-05-281-2/+19
* Add info on tokens with a `no' prefix.Eric S. Raymond1997-05-271-8/+22
* Remove section on built-time problems.Eric S. Raymond1997-05-171-48/+4
* Nailed.Eric S. Raymond1997-05-141-2/+2
.cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// Self
#include "pulse.h"

// C
#include <err.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

// C++
#include <string>
#include <algorithm>

// External
#include <pulse/pulseaudio.h>

namespace {
static void connect_state_cb(pa_context* context, void* raw) {
  enum pa_context_state *state = (enum pa_context_state*)raw;
  *state = pa_context_get_state(context);
}

static void success_cb(pa_context* context, int success, void* raw) {
  int *r = (int*)raw;
  *r = success;
  if (!success) {
    fprintf(stderr,
            "operation failed: %s\n",
            pa_strerror(pa_context_errno(context)));
  }
}

static void card_info_cb(pa_context* context,
                         const pa_card_info* info,
                         int eol,
                         void* raw) {
  if (eol < 0) {
    fprintf(stderr, "%s error in %s: \n", __func__,
        pa_strerror(pa_context_errno(context)));
    return;
  }

  if (!eol) {
    vector<Card>* cards = (vector<Card>*)raw;
    cards->push_back(info);
  }
}

template<typename T>
static void device_info_cb(pa_context* context,
                           const T* info,
                           int eol,
                           void* raw) {
  if (eol < 0) {
    fprintf(stderr, "%s error in %s: \n", __func__,
        pa_strerror(pa_context_errno(context)));
    return;
  }

  if (!eol) {
    vector<Device>* devices_ = (vector<Device>*)raw;
    devices_->push_back(info);
  }
}

static void server_info_cb(pa_context* context __attribute__((unused)),
                           const pa_server_info* i,
                           void* raw) {
  ServerInfo* defaults = (ServerInfo*)raw;
  defaults->sink = i->default_sink_name;
  defaults->source = i->default_source_name;
}

static pa_cvolume* value_to_cvol(long value, pa_cvolume *cvol) {
  return pa_cvolume_set(cvol, cvol->channels,
      fmax((double)(value + .5) * PA_VOLUME_NORM / 100, 0));
}

static int volume_as_percent(const pa_cvolume* cvol) {
  return pa_cvolume_avg(cvol) * 100.0 / PA_VOLUME_NORM;
}

static int xstrtol(const char *str, long *out) {
  char *end = NULL;

  if (str == NULL || *str == '\0') return -1;
  errno = 0;

  *out = strtol(str, &end, 10);
  if (errno || str == end || (end && *end)) return -1;

  return 0;
}


}  // anonymous namespace

Pulse::Pulse(string client_name) :
    client_name_(client_name),
    volume_range_(0, 150),
    balance_range_(-100, 100) {
  enum pa_context_state state = PA_CONTEXT_CONNECTING;

  pa_proplist* proplist = pa_proplist_new();
  pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, client_name.c_str());
  pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.falconindy.ponymix");
  pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "0.1");
  pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card");

  mainloop_ = pa_mainloop_new();
  context_ = pa_context_new_with_proplist(pa_mainloop_get_api(mainloop_),
                                          nullptr, proplist);

  pa_proplist_free(proplist);

  pa_context_set_state_callback(context_, connect_state_cb, &state);
  pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr);
  while (state != PA_CONTEXT_READY && state != PA_CONTEXT_FAILED) {
    pa_mainloop_iterate(mainloop_, 1, nullptr);
  }

  if (state != PA_CONTEXT_READY) {
    fprintf(stderr, "failed to connect to pulse daemon: %s\n",
        pa_strerror(pa_context_errno(context_)));
    exit(EXIT_FAILURE);
  }
}

//
// Pulse Client
//
Pulse::~Pulse() {
  pa_context_unref(context_);
  pa_mainloop_free(mainloop_);
}

void Pulse::Populate() {
  populate_server_info();
  populate_sinks();
  populate_sources();
  populate_cards();
}

Card* Pulse::GetCard(const uint32_t& index) {
  for (Card& card : cards_) {
    if (card.index_ == index) return &card;
  }
  return nullptr;
}

Card* Pulse::GetCard(const string& name) {
  for (Card& card : cards_) {
    if (card.name_ == name) return &card;
  }
  return nullptr;
}

Device* Pulse::get_device(vector<Device>& devices, const uint32_t& index) {
  for (Device& device : devices) {
    if (device.index_ == index) return &device;
  }
  return nullptr;
}

Device* Pulse::get_device(vector<Device>& devices, const string& name) {
  long val;
  if (xstrtol(name.c_str(), &val) == 0) return get_device(devices, val);

  for (Device& device : devices) {
    if (device.name_ == name) return &device;
  }
  return nullptr;
}

Device* Pulse::GetSink(const uint32_t& index) {
  return get_device(sinks_, index);
}

Device* Pulse::GetSink(const string& name) {
  return get_device(sinks_, name);
}

Device* Pulse::GetSource(const uint32_t& index) {
  return get_device(sources_, index);
}

Device* Pulse::GetSource(const string& name) {
  return get_device(sources_, name);
}

Device* Pulse::GetSinkInput(const uint32_t& index) {
  return get_device(sink_inputs_, index);
}

Device* Pulse::GetSinkInput(const string& name) {
  return get_device(sink_inputs_, name);
}

Device* Pulse::GetSourceOutput(const uint32_t& index) {
  return get_device(source_outputs_, index);
}

Device* Pulse::GetSourceOutput(const string& name) {
  return get_device(source_outputs_, name);
}

void Pulse::mainloop_iterate(pa_operation* op) {
  int r;
  while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
    pa_mainloop_iterate(mainloop_, 1, &r);
  }
}

void Pulse::populate_cards() {
  cards_.clear();
  pa_operation* op = pa_context_get_card_info_list(context_,
                                                   card_info_cb,
                                                   (void*)&cards_);
  mainloop_iterate(op);
  pa_operation_unref(op);
}

void Pulse::populate_server_info() {
  pa_operation* op = pa_context_get_server_info(context_,
                                                server_info_cb,
                                                &defaults_);
  mainloop_iterate(op);
  pa_operation_unref(op);
}

void Pulse::populate_sinks() {
  sinks_.clear();
  pa_operation* op = pa_context_get_sink_info_list(context_,
                                                   device_info_cb,
                                                   (void*)&sinks_);
  mainloop_iterate(op);
  pa_operation_unref(op);

  sink_inputs_.clear();
  op = pa_context_get_sink_input_info_list(context_,
                                           device_info_cb,
                                           (void*)&sink_inputs_);
  mainloop_iterate(op);
  pa_operation_unref(op);
}

void Pulse::populate_sources() {
  sources_.clear();
  pa_operation* op = pa_context_get_source_info_list(context_,
                                                     device_info_cb,
                                                     (void*)&sources_);
  mainloop_iterate(op);
  pa_operation_unref(op);

  source_outputs_.clear();
  op = pa_context_get_source_output_info_list(context_,
                                              device_info_cb,
                                              (void*)&source_outputs_);
  mainloop_iterate(op);
  pa_operation_unref(op);
}

bool Pulse::SetMute(Device& device, bool mute) {
  int success;

  if (device.ops_.Mute == nullptr) {
    warnx("device does not support muting.");
    return false;
  }

  pa_operation* op = device.ops_.Mute(context_,
                                      device.index_,
                                      mute,
                                      success_cb,
                                      &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) device.mute_ = mute;

  return success;
}

bool Pulse::SetVolume(Device& device, long volume) {
  int success;

  if (device.ops_.SetVolume == nullptr) {
    warnx("device does not support setting balance.");
    return false;
  }

  const pa_cvolume *cvol = value_to_cvol(volume, &device.volume_);
  pa_operation* op = device.ops_.SetVolume(context_,
                                           device.index_,
                                           cvol,
                                           success_cb,
                                           &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) device.update_volume(*cvol);

  return success;
}

bool Pulse::IncreaseVolume(Device& device, long increment) {
  return SetVolume(device,
                   volume_range_.clamp(device.volume_percent_ + increment));
}

bool Pulse::DecreaseVolume(Device& device, long increment) {
  return SetVolume(device,
                   volume_range_.clamp(device.volume_percent_ - increment));
}

bool Pulse::SetBalance(Device& device, long balance) {
  int success;

  if (device.ops_.SetVolume == nullptr) {
    warnx("device does not support setting balance.");
    return false;
  }

  pa_cvolume *cvol = pa_cvolume_set_balance(&device.volume_,
                                            &device.channels_,
                                            balance / 100.0);
  pa_operation* op = device.ops_.SetVolume(context_,
                                           device.index_,
                                           cvol,
                                           success_cb,
                                           &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) device.update_volume(*cvol);

  return success;
}

bool Pulse::IncreaseBalance(Device& device, long increment) {
  return SetBalance(device,
                    balance_range_.clamp(device.balance_ + increment));
}

bool Pulse::DecreaseBalance(Device& device, long increment) {
  return SetBalance(device,
                    balance_range_.clamp(device.balance_ - increment));
}

int Pulse::GetVolume(const Device& device) const {
  return device.Volume();
}

int Pulse::GetBalance(const Device& device) const {
  return device.Balance();
}

bool Pulse::SetProfile(Card& card, const string& profile) {
  int success;
  pa_operation* op =
    pa_context_set_card_profile_by_index(context_,
                                         card.index_,
                                         profile.c_str(),
                                         success_cb,
                                         &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) {
    // Update the profile
    for (const Profile p : card.profiles_) {
      if (p.name == profile) {
        card.active_profile_ = p;
        break;
      }
    }
  }

  return success;
}

bool Pulse::Move(Device& source, Device& dest) {
  int success;

  if (source.ops_.Move == nullptr) {
    warnx("source device does not support moving.");
    return false;
  }

  pa_operation* op = source.ops_.Move(context_,
                                      source.index_,
                                      dest.index_,
                                      success_cb,
                                      &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  return success;
}

bool Pulse::Kill(Device& device) {
  int success;

  if (device.ops_.Kill == nullptr) {
    warnx("source device does not support being killed.");
    return false;
  }

  pa_operation* op = device.ops_.Kill(context_,
                                      device.index_,
                                      success_cb,
                                      &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) remove_device(device);

  return success;
}

bool Pulse::SetDefault(Device& device) {
  int success;

  if (device.ops_.SetDefault == nullptr) {
    warnx("device does not support defaults");
    return false;
  }

  pa_operation* op = device.ops_.SetDefault(context_,
                                            device.name_.c_str(),
                                            success_cb,
                                            &success);
  mainloop_iterate(op);
  pa_operation_unref(op);

  if (success) {
    switch (device.type_) {
    case DEVTYPE_SINK:
      defaults_.sink = device.name_;
      break;
    case DEVTYPE_SOURCE:
      defaults_.source = device.name_;
      break;
    default:
      errx(1, "impossible to set a default for device type %d",
           device.type_);
    }
  }

  return success;
}

void Pulse::remove_device(Device& device) {
  vector<Device>* devlist;

  switch (device.type_) {
  case DEVTYPE_SINK:
    devlist = &sinks_;
  case DEVTYPE_SINK_INPUT:
    devlist = &sink_inputs_;
    break;
  case DEVTYPE_SOURCE:
    devlist = &sources_;
    break;
  case DEVTYPE_SOURCE_OUTPUT:
    devlist = &source_outputs_;
    break;
  }
  devlist->erase(
      std::remove_if(
        devlist->begin(), devlist->end(),
        [=](Device& d) { return d.index_ == device.index_; }),
      devlist->end());
}

//
// Cards
//
Card::Card(const pa_card_info* info) :
    index_(info->index),
    name_(info->name),
    owner_module_(info->owner_module),
    driver_(info->driver),
    active_profile_(*info->active_profile) {
  for (int i = 0; info->profiles[i].name != nullptr; i++) {
    profiles_.push_back(info->profiles[i]);
  }
}

//
// Devices
//
Device::Device(const pa_sink_info* info) :
    type_(DEVTYPE_SINK),
    index_(info->index),
    name_(info->name ? info->name : ""),
    desc_(info->description),
    mute_(info->mute) {
  update_volume(info->volume);
  memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map));
  balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0;

  ops_.SetVolume = pa_context_set_sink_volume_by_index;
  ops_.Mute = pa_context_set_sink_mute_by_index;
  ops_.SetDefault = pa_context_set_default_sink;
}

Device::Device(const pa_source_info* info) :
    type_(DEVTYPE_SOURCE),
    index_(info->index),
    name_(info->name ? info->name : ""),
    desc_(info->description),
    mute_(info->mute) {
  update_volume(info->volume);
  memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map));
  balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0;

  ops_.SetVolume = pa_context_set_source_volume_by_index;
  ops_.Mute = pa_context_set_source_mute_by_index;
  ops_.SetDefault = pa_context_set_default_source;
}

Device::Device(const pa_sink_input_info* info) :
    type_(DEVTYPE_SINK_INPUT),
    index_(info->index),
    name_(info->name ? info->name : ""),
    mute_(info->mute) {
  update_volume(info->volume);
  memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map));
  balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0;

  const char *desc = pa_proplist_gets(info->proplist,
                                      PA_PROP_APPLICATION_NAME);
  if (desc) desc_ = desc;

  ops_.SetVolume = pa_context_set_sink_input_volume;
  ops_.Mute = pa_context_set_sink_input_mute;
  ops_.Kill = pa_context_kill_sink_input;
  ops_.Move = pa_context_move_sink_input_by_index;
}

Device::Device(const pa_source_output_info* info) :
    type_(DEVTYPE_SOURCE_OUTPUT),
    index_(info->index),
    name_(info->name ? info->name : ""),
    mute_(info->mute) {
  update_volume(info->volume);
  volume_percent_ = volume_as_percent(&volume_);
  balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0;

  const char *desc = pa_proplist_gets(info->proplist,
                                      PA_PROP_APPLICATION_NAME);
  if (desc) desc_ = desc;

  ops_.SetVolume = pa_context_set_source_output_volume;
  ops_.Mute = pa_context_set_source_output_mute;
  ops_.Kill = pa_context_kill_source_output;
  ops_.Move = pa_context_move_source_output_by_index;
}

void Device::update_volume(const pa_cvolume& newvol) {
  memcpy(&volume_, &newvol, sizeof(pa_cvolume));
  volume_percent_ = volume_as_percent(&volume_);
  balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0;
}

// vim: set et ts=2 sw=2: