<commit_before>#include "LouvainSimplifier.h"

#include <Graph.h>
#include <Louvain.h>
#include <iostream>

using namespace std;

bool
LouvainSimplifier::apply(Graph & target_graph, time_t start_time, time_t end_time, float start_sentiment, float end_sentiment, Graph & source_graph, RawStatistics & stats, bool is_first_level, Graph * base_graph) {
  if (target_graph.getNodeArray().hasTemporalCoverage() && !(end_time > start_time)) {
    cerr << "invalid time range for apply: " << start_time << " - " << end_time << endl;
    return false;
  }

  assert(base_graph);

  auto & sid = source_graph.getNodeArray().getTable()["source"];
  auto & soid = source_graph.getNodeArray().getTable()["id"];
  auto & user_type = source_graph.getNodeArray().getTable()["type"];
  auto & political_party = source_graph.getNodeArray().getTable()["party"];
  auto & name_column = source_graph.getNodeArray().getTable()["name"];
  auto & uname_column = source_graph.getNodeArray().getTable()["uname"];

  auto begin = source_graph.begin_edges();
  auto end = source_graph.end_edges();
  auto it = begin;

  if (current_pos == -1) {
    target_graph.clear();
    current_pos = 0;
    cerr << "restarting update, begin = " << begin.get() << ", cp = " << current_pos << ", end = " << end.get() << ", source = " << &source_graph << ", edges = " << source_graph.getEdgeCount() << endl;
  } else {
    cerr << "continuing update, begin = " << begin.get() << ", cp = " << current_pos << ", end = " << end.get() << ", source = " << &source_graph << ", edges = " << source_graph.getEdgeCount() << endl;
    for (int i = 0; i < current_pos; i++) ++it;
  }

  auto & nodes = target_graph.getNodeArray();

  unsigned int skipped_count = 0;
  bool is_changed = false;
  unsigned int num_edges_processed = 0;
  for ( ; it != end; ++it, current_pos++) {
    num_edges_processed++;

    time_t t = 0;
    float se = 0;
    short lang = 0;
    long long app_id = -1, filter_id = -1;
    bool is_first = false;

    assert(it->face != -1);
    if (it->face != -1) {
      assert(it->face >= 0 && it->face < source_graph.getFaceCount());
      auto & fd = source_graph.getFaceAttributes(it->face);
      t = fd.timestamp;
      se = fd.sentiment;
      lang = fd.lang;
      app_id = fd.app_id;
      filter_id = fd.filter_id;
      is_first = fd.first_edge == current_pos;
    }

    if (it->tail < 0 || it->head < 0 || it->tail >= nodes.size() || it->head >= nodes.size()) {
      cerr << "invalid values: tail = " << it->tail << ", head = " << it->head << ", t = " << t << ", count = " << nodes.size() << ", n = " << num_edges_processed << endl;
      assert(0);
    }

    if ((!start_time || t >= start_time) && (!end_time || t < end_time) &&
	se >= start_sentiment && se <= end_sentiment) {
      if (t < min_time || min_time == 0) min_time = t;
      if (t > max_time) max_time = t;

      pair<int, int> np(it->tail, it->head);
      short first_user_sid = sid.getInt(np.first);
      short target_user_sid = sid.getInt(np.second);
      long long first_user_soid = soid.getInt64(np.first);
      long long target_user_soid = soid.getInt64(np.second);

      auto td1 = base_graph->getNodeTertiaryData(np.first); // data is copied, since the backing array might change
      auto td2 = base_graph->getNodeTertiaryData(np.second);

      if (!is_first_level) {
	if (td1.indegree < target_graph.getMinSignificance()) {
	  skipped_count++;
	  continue;
	}
	if (td2.indegree < target_graph.getMinSignificance()) {
	  skipped_count++;
	  continue;
	}
      }

      is_changed = true;

      auto & target_nd_old = nodes.getNodeData(np.second);
      NodeType target_type = target_nd_old.type;

      if (is_first_level && is_first) {
	stats.addActivity(t, first_user_sid, first_user_soid, lang, app_id, filter_id, PoliticalParty(political_party.getInt(np.first)));
      }

      if (is_first_level && !seen_nodes.count(np.second)) {
	seen_nodes.insert(np.second);
	UserType ut = UserType(user_type.getInt(np.second));
	if (ut != UNKNOWN_TYPE) stats.addUserType(ut);
	// stats.addPoliticalParty(PoliticalParty(political_party.getInt(np.first)));
      }

      if (target_type == NODE_HASHTAG) {
	stats.addHashtag(name_column.getText(np.second));
	num_hashtags++;
      } else if (target_type == NODE_URL) {
	stats.addLink(name_column.getText(np.second), uname_column.getText(np.second));
	num_links++;
      } else {
	if (is_first_level && target_type == NODE_ANY) {
	  stats.addReceivedActivity(t, target_user_sid, target_user_soid, app_id, filter_id);
	}

	long long coverage = 0;
	if (target_graph.getNodeArray().hasTemporalCoverage()) {
	  assert(end_time > start_time);
	  int time_pos = 63LL * (t - start_time) / (end_time - start_time);
	  assert(time_pos >= 0 && time_pos < 64);
	  coverage |= 1 << time_pos;
	}

	unordered_map<int, unordered_map<int, int> >::iterator it1;
	unordered_map<int, int>::iterator it2;
	if ((it1 = seen_edges.find(np.first)) != seen_edges.end() &&
	    (it2 = it1->second.find(np.second)) != it1->second.end()) {
#if 0
	  updateOutdegree(np.first, 1.0f);
	  updateIndegree(np.second, 1.0f);
	  updateNodeSize(np.first);
	  updateNodeSize(np.second);
#endif
	  if (target_graph.getNodeArray().hasTemporalCoverage()) {
	    assert(0);
	    auto & ed = target_graph.getEdgeAttributes(it2->second);
	    ed.coverage |= coverage;
	    float new_weight = 0;
	    for (int i = 0; i < 64; i++) {
	      if (ed.coverage & (1 << i)) new_weight += 1.0f;
	    }
	    new_weight /= 64.0f;
	    target_graph.updateEdgeWeight(it2->second, new_weight - ed.weight);
	  }
	} else {
	  seen_edges[np.first][np.second] = target_graph.addEdge(np.first, np.second, -1, 1.0f / 64.0f, 0, target_graph.getNodeArray().hasTemporalCoverage() ? coverage : 1.0f);
#if 0
	  seen_edges[np.second][np.first] = target_graph.addEdge(np.second, np.first, -1, 1.0f / 64.0f, 0, target_graph.getNodeArray().hasTemporalCoverage() ? coverage : 1.0f);
#endif
	}
      }
    }
  }

  // cerr << "updated graph data, nodes = " << nodes.size() << ", edges = " << getEdgeCount() << ", min_sig = " << target_graph.getMinSignificance() << ", skipped = " << skipped_count << ", first = " << is_first_level << endl;

  if (is_first_level) {
    stats.setTimeRange(min_time, max_time);
    stats.setNumRawNodes(nodes.size());
    stats.setNumRawEdges(source_graph.getEdgeCount());
    // stats.setNumPosts(num_posts);
    // stats.setNumActiveUsers(num_active_users);
  }

  if (is_changed) {
    target_graph.removeAllChildren();

    double precision = 0.000001;

    cerr << "doing Louvain\n";

    Louvain c(&target_graph, -1, precision);
    double mod = target_graph.modularity();
    int level = 0;
    bool is_improved = true;
    bool is_first = true;
    for (int level = 1; level <= 1 && is_improved; level++) {
      is_improved = c.oneLevel();
      double new_mod = target_graph.modularity();
      level++;

      cerr << "l " << level << ": size: " << c.size() << ", modularity increase: " << mod << " to " << new_mod << endl;
      mod = new_mod;
    }

    unsigned int visible_nodes = 0, toplevel_nodes = 0;
    auto end = target_graph.end_visible_nodes();
    for (auto it = target_graph.begin_visible_nodes(); it != end; ++it) {
      visible_nodes++;
      auto & td = target_graph.getNodeTertiaryData(*it);
      if (td.parent_node == -1) {
	toplevel_nodes++;
	if (td.child_count == 1) {
	  // target_graph.removeChild(td.first_child);
	}
	int best_d = 0;
	int best_node = -1;
	for (int n = td.first_child; n != -1; ) {
	  auto & ctd = target_graph.getNodeTertiaryData(n);
	  if (best_node == -1 || ctd.indegree > best_d) {
	    best_node = n;
	    best_d = ctd.indegree;
	  }
	  target_graph.setIsGroupLeader(n, false);
	  n = ctd.next_child;
	}
	if (best_node != -1) {
	  target_graph.setIsGroupLeader(best_node, true);
	}
      }
    }
    cerr << "after louvain: visible = " << visible_nodes << ", toplevel = " << toplevel_nodes << endl;
  }

  return is_changed;
}
<commit_msg>add assertion<commit_after>#include "LouvainSimplifier.h"

#include <Graph.h>
#include <Louvain.h>
#include <iostream>

using namespace std;

bool
LouvainSimplifier::apply(Graph & target_graph, time_t start_time, time_t end_time, float start_sentiment, float end_sentiment, Graph & source_graph, RawStatistics & stats, bool is_first_level, Graph * base_graph) {
  if (target_graph.getNodeArray().hasTemporalCoverage() && !(end_time > start_time)) {
    cerr << "invalid time range for apply: " << start_time << " - " << end_time << endl;
    return false;
  }

  assert(base_graph);

  auto & sid = source_graph.getNodeArray().getTable()["source"];
  auto & soid = source_graph.getNodeArray().getTable()["id"];
  auto & user_type = source_graph.getNodeArray().getTable()["type"];
  auto & political_party = source_graph.getNodeArray().getTable()["party"];
  auto & name_column = source_graph.getNodeArray().getTable()["name"];
  auto & uname_column = source_graph.getNodeArray().getTable()["uname"];

  auto begin = source_graph.begin_edges();
  auto end = source_graph.end_edges();
  auto it = begin;

  if (current_pos == -1) {
    target_graph.clear();
    current_pos = 0;
    cerr << "restarting update, begin = " << begin.get() << ", cp = " << current_pos << ", end = " << end.get() << ", source = " << &source_graph << ", edges = " << source_graph.getEdgeCount() << endl;
  } else {
    cerr << "continuing update, begin = " << begin.get() << ", cp = " << current_pos << ", end = " << end.get() << ", source = " << &source_graph << ", edges = " << source_graph.getEdgeCount() << endl;
    for (int i = 0; i < current_pos; i++) {
      assert(it != end);
      ++it;
    }
  }

  auto & nodes = target_graph.getNodeArray();

  unsigned int skipped_count = 0;
  bool is_changed = false;
  unsigned int num_edges_processed = 0;
  for ( ; it != end; ++it, current_pos++) {
    num_edges_processed++;

    time_t t = 0;
    float se = 0;
    short lang = 0;
    long long app_id = -1, filter_id = -1;
    bool is_first = false;

    assert(it->face != -1);
    if (it->face != -1) {
      assert(it->face >= 0 && it->face < source_graph.getFaceCount());
      auto & fd = source_graph.getFaceAttributes(it->face);
      t = fd.timestamp;
      se = fd.sentiment;
      lang = fd.lang;
      app_id = fd.app_id;
      filter_id = fd.filter_id;
      is_first = fd.first_edge == current_pos;
    }

    if (it->tail < 0 || it->head < 0 || it->tail >= nodes.size() || it->head >= nodes.size()) {
      cerr << "invalid values: tail = " << it->tail << ", head = " << it->head << ", t = " << t << ", count = " << nodes.size() << ", n = " << num_edges_processed << endl;
      assert(0);
    }

    if ((!start_time || t >= start_time) && (!end_time || t < end_time) &&
	se >= start_sentiment && se <= end_sentiment) {
      if (t < min_time || min_time == 0) min_time = t;
      if (t > max_time) max_time = t;

      pair<int, int> np(it->tail, it->head);
      short first_user_sid = sid.getInt(np.first);
      short target_user_sid = sid.getInt(np.second);
      long long first_user_soid = soid.getInt64(np.first);
      long long target_user_soid = soid.getInt64(np.second);

      auto td1 = base_graph->getNodeTertiaryData(np.first); // data is copied, since the backing array might change
      auto td2 = base_graph->getNodeTertiaryData(np.second);

      if (!is_first_level) {
	if (td1.indegree < target_graph.getMinSignificance()) {
	  skipped_count++;
	  continue;
	}
	if (td2.indegree < target_graph.getMinSignificance()) {
	  skipped_count++;
	  continue;
	}
      }

      is_changed = true;

      auto & target_nd_old = nodes.getNodeData(np.second);
      NodeType target_type = target_nd_old.type;

      if (is_first_level && is_first) {
	stats.addActivity(t, first_user_sid, first_user_soid, lang, app_id, filter_id, PoliticalParty(political_party.getInt(np.first)));
      }

      if (is_first_level && !seen_nodes.count(np.second)) {
	seen_nodes.insert(np.second);
	UserType ut = UserType(user_type.getInt(np.second));
	if (ut != UNKNOWN_TYPE) stats.addUserType(ut);
	// stats.addPoliticalParty(PoliticalParty(political_party.getInt(np.first)));
      }

      if (target_type == NODE_HASHTAG) {
	stats.addHashtag(name_column.getText(np.second));
	num_hashtags++;
      } else if (target_type == NODE_URL) {
	stats.addLink(name_column.getText(np.second), uname_column.getText(np.second));
	num_links++;
      } else {
	if (is_first_level && target_type == NODE_ANY) {
	  stats.addReceivedActivity(t, target_user_sid, target_user_soid, app_id, filter_id);
	}

	long long coverage = 0;
	if (target_graph.getNodeArray().hasTemporalCoverage()) {
	  assert(end_time > start_time);
	  int time_pos = 63LL * (t - start_time) / (end_time - start_time);
	  assert(time_pos >= 0 && time_pos < 64);
	  coverage |= 1 << time_pos;
	}

	unordered_map<int, unordered_map<int, int> >::iterator it1;
	unordered_map<int, int>::iterator it2;
	if ((it1 = seen_edges.find(np.first)) != seen_edges.end() &&
	    (it2 = it1->second.find(np.second)) != it1->second.end()) {
#if 0
	  updateOutdegree(np.first, 1.0f);
	  updateIndegree(np.second, 1.0f);
	  updateNodeSize(np.first);
	  updateNodeSize(np.second);
#endif
	  if (target_graph.getNodeArray().hasTemporalCoverage()) {
	    assert(0);
	    auto & ed = target_graph.getEdgeAttributes(it2->second);
	    ed.coverage |= coverage;
	    float new_weight = 0;
	    for (int i = 0; i < 64; i++) {
	      if (ed.coverage & (1 << i)) new_weight += 1.0f;
	    }
	    new_weight /= 64.0f;
	    target_graph.updateEdgeWeight(it2->second, new_weight - ed.weight);
	  }
	} else {
	  seen_edges[np.first][np.second] = target_graph.addEdge(np.first, np.second, -1, 1.0f / 64.0f, 0, target_graph.getNodeArray().hasTemporalCoverage() ? coverage : 1.0f);
#if 0
	  seen_edges[np.second][np.first] = target_graph.addEdge(np.second, np.first, -1, 1.0f / 64.0f, 0, target_graph.getNodeArray().hasTemporalCoverage() ? coverage : 1.0f);
#endif
	}
      }
    }
  }

  // cerr << "updated graph data, nodes = " << nodes.size() << ", edges = " << getEdgeCount() << ", min_sig = " << target_graph.getMinSignificance() << ", skipped = " << skipped_count << ", first = " << is_first_level << endl;

  if (is_first_level) {
    stats.setTimeRange(min_time, max_time);
    stats.setNumRawNodes(nodes.size());
    stats.setNumRawEdges(source_graph.getEdgeCount());
    // stats.setNumPosts(num_posts);
    // stats.setNumActiveUsers(num_active_users);
  }

  if (is_changed) {
    target_graph.removeAllChildren();

    double precision = 0.000001;

    cerr << "doing Louvain\n";

    Louvain c(&target_graph, -1, precision);

    cerr << "Louvain created\n";

    double mod = target_graph.modularity();
    int level = 0;
    bool is_improved = true;
    bool is_first = true;

    cerr << "initial modularity = " << mod << endl;

    for (int level = 1; level <= 1 && is_improved; level++) {
      is_improved = c.oneLevel();
      double new_mod = target_graph.modularity();
      level++;

      cerr << "l " << level << ": size: " << c.size() << ", modularity increase: " << mod << " to " << new_mod << endl;
      mod = new_mod;
    }

    unsigned int visible_nodes = 0, toplevel_nodes = 0;
    auto end = target_graph.end_visible_nodes();
    for (auto it = target_graph.begin_visible_nodes(); it != end; ++it) {
      visible_nodes++;
      auto & td = target_graph.getNodeTertiaryData(*it);
      if (td.parent_node == -1) {
	toplevel_nodes++;
	if (td.child_count == 1) {
	  // target_graph.removeChild(td.first_child);
	}
	int best_d = 0;
	int best_node = -1;
	for (int n = td.first_child; n != -1; ) {
	  auto & ctd = target_graph.getNodeTertiaryData(n);
	  if (best_node == -1 || ctd.indegree > best_d) {
	    best_node = n;
	    best_d = ctd.indegree;
	  }
	  target_graph.setIsGroupLeader(n, false);
	  n = ctd.next_child;
	}
	if (best_node != -1) {
	  target_graph.setIsGroupLeader(best_node, true);
	}
      }
    }
    cerr << "after louvain: visible = " << visible_nodes << ", toplevel = " << toplevel_nodes << endl;
  }

  return is_changed;
}
<|endoftext|>


<commit_before>/*********************************************************************
** Author: Zach Colbert
** Date: 4 October 2017
** Description: Takes five floats as input, and returns their mean.
*********************************************************************/

#include <iostream>
using namespace std;

// The function takes five floats as input from the user, and returns their mean.
int main()
{
    // Declare variables for each of the inputs and the output
    double input1, input2, input3, input4, input5, outputMean;

    //Prompt the user for each of the necessary inputs
    cout << "Please enter five numbers." << endl;
    cin >> input1;
    cin >> input2;
    cin >> input3;
    cin >> input4;
    cin >> input5;

    // Use another function to find the mean?

    // Output the mean
    // cout << "The average of those numbers is:\n";
    // cout << outputMean;
}<commit_msg>Pretending to get some work done.<commit_after>/*********************************************************************
** Author: Zach Colbert
** Date: 4 October 2017
** Description: Takes five floats as input, and returns their mean.
*********************************************************************/

#include <iostream>
using namespace std;

// The function takes five floats as input from the user, and returns their mean.
int main()
{
    // Declare variables for each of the inputs and the output
    double input1, input2, input3, input4, input5, outputMean;

    //Prompt the user for each of the necessary inputs
    cout << "Please enter five numbers." << endl;
    cin >> input1;
    cin >> input2;
    cin >> input3;
    cin >> input4;
    cin >> input5;

    // Use another function to find the mean?
    // function avg()

    // Output the mean
    // cout << "The average of those numbers is:\n";
    // cout << outputMean;
}
<|endoftext|>


<commit_before>/*
 * tlshandler.cpp - abstract wrapper for TLS
 * Copyright (C) 2003  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "qca.h"
#include "xmpp.h"

#include <qtimer.h>
#include <qurl.h>

using namespace XMPP;
// FIXME: remove this code once qca cert host checking works ...
using namespace QCA;

// ip address string to binary (msb), adapted from jdns (adapted from qt)
// return: size 4 = ipv4, size 16 = ipv6, size 0 = error
static QByteArray ipaddr_str2bin(const QString &str)
{
    // ipv6
    if (str.contains(':')) {
        QStringList parts = str.split(':', QString::KeepEmptyParts);
        if (parts.count() < 3 || parts.count() > 8)
            return QByteArray();

        QByteArray ipv6(16, 0);
        int        at   = 16;
        int        fill = 9 - parts.count();
        for (int n = parts.count() - 1; n >= 0; --n) {
            if (at <= 0)
                return QByteArray();

            if (parts[n].isEmpty()) {
                if (n == parts.count() - 1) {
                    if (!parts[n - 1].isEmpty())
                        return QByteArray();
                    ipv6[--at] = 0;
                    ipv6[--at] = 0;
                } else if (n == 0) {
                    if (!parts[n + 1].isEmpty())
                        return QByteArray();
                    ipv6[--at] = 0;
                    ipv6[--at] = 0;
                } else {
                    for (int i = 0; i < fill; ++i) {
                        if (at <= 0)
                            return QByteArray();
                        ipv6[--at] = 0;
                        ipv6[--at] = 0;
                    }
                }
            } else {
                if (parts[n].indexOf('.') == -1) {
                    bool ok;
                    int  x = parts[n].toInt(&ok, 16);
                    if (!ok || x < 0 || x > 0xffff)
                        return QByteArray();
                    ipv6[--at] = x & 0xff;
                    ipv6[--at] = (x >> 8) & 0xff;
                } else {
                    if (n != parts.count() - 1)
                        return QByteArray();

                    QByteArray buf = ipaddr_str2bin(parts[n]);
                    if (buf.isEmpty())
                        return QByteArray();

                    ipv6[--at] = buf[3];
                    ipv6[--at] = buf[2];
                    ipv6[--at] = buf[1];
                    ipv6[--at] = buf[0];
                    --fill;
                }
            }
        }

        return ipv6;
    } else if (str.contains('.')) {
        QStringList parts = str.split('.', QString::KeepEmptyParts);
        if (parts.count() != 4)
            return QByteArray();

        QByteArray out(4, 0);
        for (int n = 0; n < 4; ++n) {
            bool ok;
            int  x = parts[n].toInt(&ok);
            if (!ok || x < 0 || x > 0xff)
                return QByteArray();
            out[n] = (unsigned char)x;
        }
        return out;
    } else
        return QByteArray();
}

// acedomain must be all lowercase, with no trailing dot or wildcards
static bool cert_match_domain(const QString &certname, const QString &acedomain)
{
    // KSSL strips start/end whitespace, even though such whitespace is
    //   probably not legal anyway. (compat)
    QString name = certname.trimmed();

    // KSSL strips trailing dot, even though the dot is probably not
    //   legal anyway. (compat)
    if (name.length() > 0 && name[name.length() - 1] == '.')
        name.truncate(name.length() - 1);

    // after our compatibility modifications, make sure the name isn't
    //   empty.
    if (name.isEmpty())
        return false;

    // lowercase, for later performing case insensitive matching
    name = name.toLower();

    // ensure the cert field contains valid characters only
    if (QRegExp("[^a-z0-9\\.\\*\\-]").indexIn(name) >= 0)
        return false;

    // hack into parts, and require at least 1 part
    QStringList parts_name = name.split('.', QString::KeepEmptyParts);
    if (parts_name.isEmpty())
        return false;

    // KSSL checks to make sure the last two parts don't contain
    //   wildcards.  I don't know where it is written that this
    //   should be done, but for compat sake we'll do it.
    if (parts_name[parts_name.count() - 1].contains('*'))
        return false;
    if (parts_name.count() >= 2 && parts_name[parts_name.count() - 2].contains('*'))
        return false;

    QStringList parts_compare = acedomain.split('.', QString::KeepEmptyParts);
    if (parts_compare.isEmpty())
        return false;

    // don't allow empty parts
    for (const QString &s : parts_name) {
        if (s.isEmpty())
            return false;
    }
    for (const QString &s : parts_compare) {
        if (s.isEmpty())
            return false;
    }

    // RFC2818: "Names may contain the wildcard character * which is
    //   considered to match any single domain name component or
    //   component fragment. E.g., *.a.com matches foo.a.com but not
    //   bar.foo.a.com. f*.com matches foo.com but not bar.com."
    //
    // This means that for the domain to match it must have the
    //   same number of components, wildcards or not.  If there are
    //   wildcards, their scope must only be within the component
    //   they reside in.
    //
    // First, make sure the number of parts is equal.
    if (parts_name.count() != parts_compare.count())
        return false;

    // Now compare each part
    for (int n = 0; n < parts_name.count(); ++n) {
        const QString &p1 = parts_name[n];
        const QString &p2 = parts_compare[n];

        if (!QRegExp(p1, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(p2))
            return false;
    }

    return true;
}

// ipaddress must be an ipv4 or ipv6 address in binary format
static bool cert_match_ipaddress(const QString &certname, const QByteArray &ipaddress)
{
    // KSSL strips start/end whitespace, even though such whitespace is
    //   probably not legal anyway. (compat)
    QString name = certname.trimmed();

    // KSSL accepts IPv6 in brackets, which is usually done for URIs, but
    //   IMO sounds very strange for a certificate.  We'll follow this
    //   behavior anyway. (compat)
    if (name.length() >= 2 && name[0] == '[' && name[name.length() - 1] == ']')
        name = name.mid(1, name.length() - 2); // chop off brackets

    // after our compatibility modifications, make sure the name isn't
    //   empty.
    if (name.isEmpty())
        return false;

    // convert to binary form
    QByteArray addr = ipaddr_str2bin(name);
    if (addr.isEmpty())
        return false;

    // not the same?
    if (addr != ipaddress)
        return false;

    return true;
}

static bool matchesHostName(const QCA::Certificate &cert, const QString &host)
{
    QByteArray ipaddr = ipaddr_str2bin(host);
    if (!ipaddr.isEmpty()) // ip address
    {
        // check iPAddress
        for (const QString &s : cert.subjectInfo().values(IPAddress)) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }

        // check dNSName
        for (const QString &s : cert.subjectInfo().values(DNS)) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }

        // check commonName
        for (const QString &s : cert.subjectInfo().values(CommonName)) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }
    } else // domain
    {
        // lowercase
        QString name = host.toLower();

        // ACE
        name = QString::fromLatin1(QUrl::toAce(name));

        // don't allow wildcards in the comparison host
        if (name.contains('*'))
            return false;

        // strip out trailing dot
        if (name.length() > 0 && name[name.length() - 1] == '.')
            name.truncate(name.length() - 1);

        // make sure the name is not empty after our modifications
        if (name.isEmpty())
            return false;

        // check dNSName
        for (const QString &s : cert.subjectInfo().values(DNS)) {
            if (cert_match_domain(s, name))
                return true;
        }

        // check commonName
        for (const QString &s : cert.subjectInfo().values(CommonName)) {
            if (cert_match_domain(s, name))
                return true;
        }
    }

    return false;
}

//----------------------------------------------------------------------------
// TLSHandler
//----------------------------------------------------------------------------
TLSHandler::TLSHandler(QObject *parent) : QObject(parent) { }

TLSHandler::~TLSHandler() { }

//----------------------------------------------------------------------------
// QCATLSHandler
//----------------------------------------------------------------------------
class QCATLSHandler::Private {
public:
    QCA::TLS *tls;
    int       state, err;
    QString   host;
    bool      internalHostMatch;
};

QCATLSHandler::QCATLSHandler(QCA::TLS *parent) : TLSHandler(parent)
{
    d      = new Private;
    d->tls = parent;
    connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
    connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
    connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing()));
    connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
    connect(d->tls, SIGNAL(error()), SLOT(tls_error()));
    d->state             = 0;
    d->err               = -1;
    d->internalHostMatch = false;
}

QCATLSHandler::~QCATLSHandler() { delete d; }

void QCATLSHandler::setXMPPCertCheck(bool enable) { d->internalHostMatch = enable; }
bool QCATLSHandler::XMPPCertCheck() { return d->internalHostMatch; }
bool QCATLSHandler::certMatchesHostname()
{
    if (!d->internalHostMatch)
        return false;
    QCA::CertificateChain peerCert = d->tls->peerCertificateChain();

    if (matchesHostName(peerCert.primary(), d->host))
        return true;

    Jid host(d->host);

    for (const QString &idOnXmppAddr : peerCert.primary().subjectInfo().values(QCA::XMPP)) {
        if (host.compare(Jid(idOnXmppAddr)))
            return true;
    }

    return false;
}

QCA::TLS *QCATLSHandler::tls() const { return d->tls; }

int QCATLSHandler::tlsError() const { return d->err; }

void QCATLSHandler::reset()
{
    d->tls->reset();
    d->state = 0;
}

void QCATLSHandler::startClient(const QString &host)
{
    d->state = 0;
    d->err   = -1;
    if (d->internalHostMatch)
        d->host = host;
    d->tls->startClient(d->internalHostMatch ? QString() : host);
}

void QCATLSHandler::write(const QByteArray &a) { d->tls->write(a); }

void QCATLSHandler::writeIncoming(const QByteArray &a) { d->tls->writeIncoming(a); }

void QCATLSHandler::continueAfterHandshake()
{
    if (d->state == 2) {
        d->tls->continueAfterStep();
        success();
        d->state = 3;
    }
}

void QCATLSHandler::tls_handshaken()
{
    d->state = 2;
    tlsHandshaken();
}

void QCATLSHandler::tls_readyRead() { readyRead(d->tls->read()); }

void QCATLSHandler::tls_readyReadOutgoing()
{
    int        plainBytes;
    QByteArray buf = d->tls->readOutgoing(&plainBytes);
    readyReadOutgoing(buf, plainBytes);
}

void QCATLSHandler::tls_closed() { closed(); }

void QCATLSHandler::tls_error()
{
    d->err   = d->tls->errorCode();
    d->state = 0;
    fail();
}
<commit_msg>A little nonsense: avoid potentially unsafe ranged loops<commit_after>/*
 * tlshandler.cpp - abstract wrapper for TLS
 * Copyright (C) 2003  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "qca.h"
#include "xmpp.h"

#include <qtimer.h>
#include <qurl.h>

using namespace XMPP;
// FIXME: remove this code once qca cert host checking works ...
using namespace QCA;

// ip address string to binary (msb), adapted from jdns (adapted from qt)
// return: size 4 = ipv4, size 16 = ipv6, size 0 = error
static QByteArray ipaddr_str2bin(const QString &str)
{
    // ipv6
    if (str.contains(':')) {
        QStringList parts = str.split(':', QString::KeepEmptyParts);
        if (parts.count() < 3 || parts.count() > 8)
            return QByteArray();

        QByteArray ipv6(16, 0);
        int        at   = 16;
        int        fill = 9 - parts.count();
        for (int n = parts.count() - 1; n >= 0; --n) {
            if (at <= 0)
                return QByteArray();

            if (parts[n].isEmpty()) {
                if (n == parts.count() - 1) {
                    if (!parts[n - 1].isEmpty())
                        return QByteArray();
                    ipv6[--at] = 0;
                    ipv6[--at] = 0;
                } else if (n == 0) {
                    if (!parts[n + 1].isEmpty())
                        return QByteArray();
                    ipv6[--at] = 0;
                    ipv6[--at] = 0;
                } else {
                    for (int i = 0; i < fill; ++i) {
                        if (at <= 0)
                            return QByteArray();
                        ipv6[--at] = 0;
                        ipv6[--at] = 0;
                    }
                }
            } else {
                if (parts[n].indexOf('.') == -1) {
                    bool ok;
                    int  x = parts[n].toInt(&ok, 16);
                    if (!ok || x < 0 || x > 0xffff)
                        return QByteArray();
                    ipv6[--at] = x & 0xff;
                    ipv6[--at] = (x >> 8) & 0xff;
                } else {
                    if (n != parts.count() - 1)
                        return QByteArray();

                    QByteArray buf = ipaddr_str2bin(parts[n]);
                    if (buf.isEmpty())
                        return QByteArray();

                    ipv6[--at] = buf[3];
                    ipv6[--at] = buf[2];
                    ipv6[--at] = buf[1];
                    ipv6[--at] = buf[0];
                    --fill;
                }
            }
        }

        return ipv6;
    } else if (str.contains('.')) {
        QStringList parts = str.split('.', QString::KeepEmptyParts);
        if (parts.count() != 4)
            return QByteArray();

        QByteArray out(4, 0);
        for (int n = 0; n < 4; ++n) {
            bool ok;
            int  x = parts[n].toInt(&ok);
            if (!ok || x < 0 || x > 0xff)
                return QByteArray();
            out[n] = (unsigned char)x;
        }
        return out;
    } else
        return QByteArray();
}

// acedomain must be all lowercase, with no trailing dot or wildcards
static bool cert_match_domain(const QString &certname, const QString &acedomain)
{
    // KSSL strips start/end whitespace, even though such whitespace is
    //   probably not legal anyway. (compat)
    QString name = certname.trimmed();

    // KSSL strips trailing dot, even though the dot is probably not
    //   legal anyway. (compat)
    if (name.length() > 0 && name[name.length() - 1] == '.')
        name.truncate(name.length() - 1);

    // after our compatibility modifications, make sure the name isn't
    //   empty.
    if (name.isEmpty())
        return false;

    // lowercase, for later performing case insensitive matching
    name = name.toLower();

    // ensure the cert field contains valid characters only
    if (QRegExp("[^a-z0-9\\.\\*\\-]").indexIn(name) >= 0)
        return false;

    // hack into parts, and require at least 1 part
    QStringList parts_name = name.split('.', QString::KeepEmptyParts);
    if (parts_name.isEmpty())
        return false;

    // KSSL checks to make sure the last two parts don't contain
    //   wildcards.  I don't know where it is written that this
    //   should be done, but for compat sake we'll do it.
    if (parts_name[parts_name.count() - 1].contains('*'))
        return false;
    if (parts_name.count() >= 2 && parts_name[parts_name.count() - 2].contains('*'))
        return false;

    QStringList parts_compare = acedomain.split('.', QString::KeepEmptyParts);
    if (parts_compare.isEmpty())
        return false;

    // don't allow empty parts
    for (const QString &s : parts_name) {
        if (s.isEmpty())
            return false;
    }
    for (const QString &s : parts_compare) {
        if (s.isEmpty())
            return false;
    }

    // RFC2818: "Names may contain the wildcard character * which is
    //   considered to match any single domain name component or
    //   component fragment. E.g., *.a.com matches foo.a.com but not
    //   bar.foo.a.com. f*.com matches foo.com but not bar.com."
    //
    // This means that for the domain to match it must have the
    //   same number of components, wildcards or not.  If there are
    //   wildcards, their scope must only be within the component
    //   they reside in.
    //
    // First, make sure the number of parts is equal.
    if (parts_name.count() != parts_compare.count())
        return false;

    // Now compare each part
    for (int n = 0; n < parts_name.count(); ++n) {
        const QString &p1 = parts_name[n];
        const QString &p2 = parts_compare[n];

        if (!QRegExp(p1, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(p2))
            return false;
    }

    return true;
}

// ipaddress must be an ipv4 or ipv6 address in binary format
static bool cert_match_ipaddress(const QString &certname, const QByteArray &ipaddress)
{
    // KSSL strips start/end whitespace, even though such whitespace is
    //   probably not legal anyway. (compat)
    QString name = certname.trimmed();

    // KSSL accepts IPv6 in brackets, which is usually done for URIs, but
    //   IMO sounds very strange for a certificate.  We'll follow this
    //   behavior anyway. (compat)
    if (name.length() >= 2 && name[0] == '[' && name[name.length() - 1] == ']')
        name = name.mid(1, name.length() - 2); // chop off brackets

    // after our compatibility modifications, make sure the name isn't
    //   empty.
    if (name.isEmpty())
        return false;

    // convert to binary form
    QByteArray addr = ipaddr_str2bin(name);
    if (addr.isEmpty())
        return false;

    // not the same?
    if (addr != ipaddress)
        return false;

    return true;
}

static bool matchesHostName(const QCA::Certificate &cert, const QString &host)
{
    QByteArray ipaddr = ipaddr_str2bin(host);
    if (!ipaddr.isEmpty()) // ip address
    {
        // check iPAddress
        const auto ipAddresses = cert.subjectInfo().values(IPAddress);
        for (const QString &s : ipAddresses) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }

        // check dNSName
        const auto dnsNames = cert.subjectInfo().values(DNS);
        for (const QString &s : dnsNames) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }

        // check commonName
        const auto commonNames = cert.subjectInfo().values(CommonName);
        for (const QString &s : commonNames) {
            if (cert_match_ipaddress(s, ipaddr))
                return true;
        }
    } else // domain
    {
        // lowercase
        QString name = host.toLower();

        // ACE
        name = QString::fromLatin1(QUrl::toAce(name));

        // don't allow wildcards in the comparison host
        if (name.contains('*'))
            return false;

        // strip out trailing dot
        if (name.length() > 0 && name[name.length() - 1] == '.')
            name.truncate(name.length() - 1);

        // make sure the name is not empty after our modifications
        if (name.isEmpty())
            return false;

        // check dNSName
        const auto dnsNames = cert.subjectInfo().values(DNS);
        for (const QString &s : dnsNames) {
            if (cert_match_domain(s, name))
                return true;
        }

        // check commonName
        const auto commonNames = cert.subjectInfo().values(CommonName);
        for (const QString &s : commonNames) {
            if (cert_match_domain(s, name))
                return true;
        }
    }

    return false;
}

//----------------------------------------------------------------------------
// TLSHandler
//----------------------------------------------------------------------------
TLSHandler::TLSHandler(QObject *parent) : QObject(parent) { }

TLSHandler::~TLSHandler() { }

//----------------------------------------------------------------------------
// QCATLSHandler
//----------------------------------------------------------------------------
class QCATLSHandler::Private {
public:
    QCA::TLS *tls;
    int       state, err;
    QString   host;
    bool      internalHostMatch;
};

QCATLSHandler::QCATLSHandler(QCA::TLS *parent) : TLSHandler(parent)
{
    d      = new Private;
    d->tls = parent;
    connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
    connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
    connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing()));
    connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
    connect(d->tls, SIGNAL(error()), SLOT(tls_error()));
    d->state             = 0;
    d->err               = -1;
    d->internalHostMatch = false;
}

QCATLSHandler::~QCATLSHandler() { delete d; }

void QCATLSHandler::setXMPPCertCheck(bool enable) { d->internalHostMatch = enable; }
bool QCATLSHandler::XMPPCertCheck() { return d->internalHostMatch; }
bool QCATLSHandler::certMatchesHostname()
{
    if (!d->internalHostMatch)
        return false;
    QCA::CertificateChain peerCert = d->tls->peerCertificateChain();

    if (matchesHostName(peerCert.primary(), d->host))
        return true;

    Jid host(d->host);

    const auto hosts = peerCert.primary().subjectInfo().values(QCA::XMPP);
    for (const QString &idOnXmppAddr : hosts) {
        if (host.compare(Jid(idOnXmppAddr)))
            return true;
    }

    return false;
}

QCA::TLS *QCATLSHandler::tls() const { return d->tls; }

int QCATLSHandler::tlsError() const { return d->err; }

void QCATLSHandler::reset()
{
    d->tls->reset();
    d->state = 0;
}

void QCATLSHandler::startClient(const QString &host)
{
    d->state = 0;
    d->err   = -1;
    if (d->internalHostMatch)
        d->host = host;
    d->tls->startClient(d->internalHostMatch ? QString() : host);
}

void QCATLSHandler::write(const QByteArray &a) { d->tls->write(a); }

void QCATLSHandler::writeIncoming(const QByteArray &a) { d->tls->writeIncoming(a); }

void QCATLSHandler::continueAfterHandshake()
{
    if (d->state == 2) {
        d->tls->continueAfterStep();
        success();
        d->state = 3;
    }
}

void QCATLSHandler::tls_handshaken()
{
    d->state = 2;
    tlsHandshaken();
}

void QCATLSHandler::tls_readyRead() { readyRead(d->tls->read()); }

void QCATLSHandler::tls_readyReadOutgoing()
{
    int        plainBytes;
    QByteArray buf = d->tls->readOutgoing(&plainBytes);
    readyReadOutgoing(buf, plainBytes);
}

void QCATLSHandler::tls_closed() { closed(); }

void QCATLSHandler::tls_error()
{
    d->err   = d->tls->errorCode();
    d->state = 0;
    fail();
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "stx/wallclock.h"
#include "stx/assets.h"
#include "stx/protobuf/msg.h"
#include "stx/io/BufferedOutputStream.h"
#include "zbase/api/EventsAPIServlet.h"

using namespace stx;

namespace zbase {

EventsAPIServlet::EventsAPIServlet(
    EventsService* service,
    ConfigDirectory* cdir,
    const String& cachedir) :
    service_(service),
    cdir_(cdir),
    cachedir_(cachedir) {}

void EventsAPIServlet::handle(
    const AnalyticsSession& session,
    RefPtr<stx::http::HTTPRequestStream> req_stream,
    RefPtr<stx::http::HTTPResponseStream> res_stream) {
  const auto& req = req_stream->request();
  URI uri(req.uri());

  http::HTTPResponse res;
  res.populateFromRequest(req);

  if (uri.path() == "/api/v1/events/scan") {
    scanTable(session, uri, req_stream.get(), res_stream.get());
    return;
  }

  if (uri.path() == "/api/v1/events/scan_partition") {
    scanTablePartition(session, uri, req_stream.get(), res_stream.get());
    return;
  }

  res.setStatus(http::kStatusNotFound);
  res.addHeader("Content-Type", "text/html; charset=utf-8");
  res.addBody(Assets::getAsset("zbase/webui/404.html"));
  res_stream->writeResponse(res);
}

void EventsAPIServlet::scanTable(
    const AnalyticsSession& session,
    const URI& uri,
    http::HTTPRequestStream* req_stream,
    http::HTTPResponseStream* res_stream) {
  req_stream->readBody();

  const auto& params = uri.queryParams();

  String table_name;
  if (!URI::getParam(params, "table", &table_name)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?table=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  EventScanParams scan_params;

  scan_params.set_end_time(WallClock::unixMicros());

  String time_str;
  if (URI::getParam(params, "time", &time_str)) {
    scan_params.set_end_time(std::stoull(time_str));
  }

  size_t limit = 1000;
  String limit_str;
  if (URI::getParam(params, "limit", &limit_str)) {
    limit = std::stoull(limit_str);
  }
  scan_params.set_limit(limit);

  String columns_str;
  if (URI::getParam(params, "columns", &columns_str)) {
    for (const auto& c : StringUtil::split(columns_str, ",")) {
      *scan_params.add_columns() = c;
    }
  }

  http::HTTPSSEStream sse_stream(req_stream, res_stream);
  sse_stream.start();

  auto send_result_row = [&sse_stream] (const msg::DynamicMessage& row) {
    Buffer buf;
    json::JSONOutputStream json(BufferOutputStream::fromBuffer(&buf));
    json.beginObject();
    json.addObjectEntry("event");
    row.toJSON(&json);
    json.endObject();

    sse_stream.sendEvent(buf, Some<String>("result"));
  };

  auto send_status_update = [&sse_stream, &scan_params] (bool done) {
    Buffer buf;
    json::JSONOutputStream json(BufferOutputStream::fromBuffer(&buf));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString(done ? "finished" : "running");
    //json.addComma();
    //json.addObjectEntry("scanned_until");
    //json.addInteger(result.scannedUntil().unixMicros());
    //json.addComma();
    //json.addObjectEntry("rows_scanned");
    //json.addInteger(result.rowScanned());
    //json.addComma();
    //json.addObjectEntry("results");
    //json.beginArray();

    //size_t nline = 0;
    //for (const auto& l : result.rows()) {
    //  if (++nline > 1) {
    //    json.addComma();
    //  }

    //  json.beginObject();
    //  json.addObjectEntry("time");
    //  json.addInteger(l.time.unixMicros());
    //  json.addComma();

    //  json.addObjectEntry("data");
    //  l.obj.toJSON(&json);

    //  json.endObject();
    //}

    //json.endArray();
    json.endObject();

    sse_stream.sendEvent(buf, Some<String>("progress"));
  };

  service_->scanTable(
      session,
      table_name,
      scan_params,
      send_result_row,
      send_status_update);

  sse_stream.finish();
}

void EventsAPIServlet::scanTablePartition(
    const AnalyticsSession& session,
    const URI& uri,
    http::HTTPRequestStream* req_stream,
    http::HTTPResponseStream* res_stream) {
  req_stream->readBody();

  const auto& params = uri.queryParams();

  String table;
  if (!URI::getParam(params, "table", &table)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?table=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  String partition;
  if (!URI::getParam(params, "partition", &partition)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?partition=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  String limit_str;
  if (!URI::getParam(params, "limit", &limit_str)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?limit=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  auto limit = std::stoull(limit_str);
  auto scan_params = msg::decode<EventScanParams>(
      req_stream->request().body());

  EventScanResult result(limit);

  service_->scanLocalTablePartition(
      session,
      table,
      SHA1Hash::fromHexString(partition),
      scan_params,
      &result);

  Buffer res_body;
  {
    BufferedOutputStream res_os(BufferOutputStream::fromBuffer(&res_body));
    result.encode(&res_os);
  }

  http::HTTPResponse res;
  res.populateFromRequest(req_stream->request());
  res.setStatus(http::kStatusOK);
  res.addBody(res_body);
  res_stream->writeResponse(res);
}

}
<commit_msg>cleaning up<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "stx/wallclock.h"
#include "stx/assets.h"
#include "stx/protobuf/msg.h"
#include "stx/io/BufferedOutputStream.h"
#include "zbase/api/EventsAPIServlet.h"

using namespace stx;

namespace zbase {

EventsAPIServlet::EventsAPIServlet(
    EventsService* service,
    ConfigDirectory* cdir,
    const String& cachedir) :
    service_(service),
    cdir_(cdir),
    cachedir_(cachedir) {}

void EventsAPIServlet::handle(
    const AnalyticsSession& session,
    RefPtr<stx::http::HTTPRequestStream> req_stream,
    RefPtr<stx::http::HTTPResponseStream> res_stream) {
  const auto& req = req_stream->request();
  URI uri(req.uri());

  http::HTTPResponse res;
  res.populateFromRequest(req);

  if (uri.path() == "/api/v1/events/scan") {
    scanTable(session, uri, req_stream.get(), res_stream.get());
    return;
  }

  if (uri.path() == "/api/v1/events/scan_partition") {
    scanTablePartition(session, uri, req_stream.get(), res_stream.get());
    return;
  }

  res.setStatus(http::kStatusNotFound);
  res.addHeader("Content-Type", "text/html; charset=utf-8");
  res.addBody(Assets::getAsset("zbase/webui/404.html"));
  res_stream->writeResponse(res);
}

void EventsAPIServlet::scanTable(
    const AnalyticsSession& session,
    const URI& uri,
    http::HTTPRequestStream* req_stream,
    http::HTTPResponseStream* res_stream) {
  req_stream->readBody();

  const auto& params = uri.queryParams();

  String table_name;
  if (!URI::getParam(params, "table", &table_name)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?table=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  EventScanParams scan_params;

  scan_params.set_end_time(WallClock::unixMicros());

  String time_str;
  if (URI::getParam(params, "time", &time_str)) {
    scan_params.set_end_time(std::stoull(time_str));
  }

  size_t limit = 1000;
  String limit_str;
  if (URI::getParam(params, "limit", &limit_str)) {
    limit = std::stoull(limit_str);
  }
  scan_params.set_limit(limit);

  String columns_str;
  if (URI::getParam(params, "columns", &columns_str)) {
    for (const auto& c : StringUtil::split(columns_str, ",")) {
      *scan_params.add_columns() = c;
    }
  }

  http::HTTPSSEStream sse_stream(req_stream, res_stream);
  sse_stream.start();

  auto send_result_row = [&sse_stream] (const msg::DynamicMessage& row) {
    Buffer buf;
    json::JSONOutputStream json(BufferOutputStream::fromBuffer(&buf));
    json.beginObject();
    json.addObjectEntry("event");
    row.toJSON(&json);
    json.endObject();

    sse_stream.sendEvent(buf, Some<String>("result"));
  };

  auto send_status_update = [&sse_stream, &scan_params] (bool done) {
    Buffer buf;
    json::JSONOutputStream json(BufferOutputStream::fromBuffer(&buf));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString(done ? "finished" : "running");
    json.endObject();

    sse_stream.sendEvent(buf, Some<String>("progress"));
  };

  service_->scanTable(
      session,
      table_name,
      scan_params,
      send_result_row,
      send_status_update);

  sse_stream.finish();
}

void EventsAPIServlet::scanTablePartition(
    const AnalyticsSession& session,
    const URI& uri,
    http::HTTPRequestStream* req_stream,
    http::HTTPResponseStream* res_stream) {
  req_stream->readBody();

  const auto& params = uri.queryParams();

  String table;
  if (!URI::getParam(params, "table", &table)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?table=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  String partition;
  if (!URI::getParam(params, "partition", &partition)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?partition=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  String limit_str;
  if (!URI::getParam(params, "limit", &limit_str)) {
    http::HTTPResponse res;
    res.populateFromRequest(req_stream->request());
    res.setStatus(http::kStatusBadRequest);
    res.addBody("error: missing ?limit=... parameter");
    res_stream->writeResponse(res);
    return;
  }

  auto limit = std::stoull(limit_str);
  auto scan_params = msg::decode<EventScanParams>(
      req_stream->request().body());

  EventScanResult result(limit);

  service_->scanLocalTablePartition(
      session,
      table,
      SHA1Hash::fromHexString(partition),
      scan_params,
      &result);

  Buffer res_body;
  {
    BufferedOutputStream res_os(BufferOutputStream::fromBuffer(&res_body));
    result.encode(&res_os);
  }

  http::HTTPResponse res;
  res.populateFromRequest(req_stream->request());
  res.setStatus(http::kStatusOK);
  res.addBody(res_body);
  res_stream->writeResponse(res);
}

}
<|endoftext|>


<commit_before>/*  This file is part of the KDE project.

Copyright (C) 2007 Trolltech ASA. All rights reserved.

This library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 or 3 of the License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "backend.h"
#include "backendnode.h"

#include "audiooutput.h"
#include "effect.h"
#include "mediaobject.h"
#include "videowidget.h"
#include "volumeeffect.h"

//windows specific (DirectX Media Object)
#include <dmo.h>

#include <QtCore/QSettings>
#include <QtCore/QSet>
#include <QtCore/QVariant>

#include <QtCore/QtPlugin>

QT_BEGIN_NAMESPACE

Q_EXPORT_PLUGIN2(phonon_ds9, Phonon::DS9::Backend);

namespace Phonon
{
    namespace DS9
    {
        bool Backend::AudioMoniker::operator==(const AudioMoniker &other)
        {
            return other->IsEqual(*this) == S_OK;
        }

        Backend::Backend(QObject *parent, const QVariantList &)
            : QObject(parent)
        {
			::CoInitialize(0);

            //registering meta types
            qRegisterMetaType<HRESULT>("HRESULT");
            qRegisterMetaType<QSet<Filter> >("QSet<Filter>");
            qRegisterMetaType<Graph>("Graph");
        }

        Backend::~Backend()
        {
			::CoUninitialize();
        }

        QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
        {
            switch (c)
            {
            case MediaObjectClass:
                return new MediaObject(parent);
            case AudioOutputClass:
                return new AudioOutput(this, parent);
#ifndef QT_NO_PHONON_EFFECT
            case EffectClass:
                return new Effect(m_audioEffects[ args[0].toInt() ], parent);
#endif //QT_NO_PHONON_EFFECT
#ifndef QT_NO_PHONON_VIDEO
            case VideoWidgetClass:
                return new VideoWidget(qobject_cast<QWidget *>(parent));
#endif //QT_NO_PHONON_VIDEO
#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
            case VolumeFaderEffectClass:
                return new VolumeEffect(parent);
#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
            default:
                return 0;
            }
        }

        bool Backend::supportsVideo() const
        {
#ifndef QT_NO_PHONON_VIDEO
            return true;
#else
            return false;
#endif //QT_NO_PHONON_VIDEO
        }

        QStringList Backend::availableMimeTypes() const
        {
            QSet<QString> ret;

            const QStringList locations = QStringList() << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\mplayer2\\mime types")
                << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\wmplayer\\mime types");

            Q_FOREACH(QString s, locations) {
                QSettings settings(s, QSettings::NativeFormat);
                ret += settings.childGroups().toSet();
            }

            QStringList list = ret.toList();
            qSort(list);
            return list;
        }

		Filter Backend::getAudioOutputFilter(int index) const
		{
			Filter ret;
			if (index >= 0 && index < m_audioOutputs.count()) {
				m_audioOutputs.at(index)->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&ret));
			} else {
				//just return the default audio renderer (not directsound)
				ret = Filter(CLSID_AudioRender, IID_IBaseFilter);
			}
			return ret;
		}

        QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const
        {
            QList<int> ret;

            switch(type)
            {
            case Phonon::AudioOutputDeviceType:
                {
#ifdef Q_OS_WINCE
					ret << 0; // only one audio device with index 0
#else
					ComPointer<ICreateDevEnum> devEnum(CLSID_SystemDeviceEnum, IID_ICreateDevEnum);
					if (!devEnum) {
						return ret; //it is impossible to enumerate the devices
					}
                    ComPointer<IEnumMoniker> enumMon;
                    HRESULT hr = devEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, enumMon.pparam(), 0);
                    if (FAILED(hr)) {
                        break;
                    }
                    AudioMoniker mon;

                    //let's reorder the devices so that directshound appears first
                    int nbds = 0; //number of directsound devices

                    while (S_OK == enumMon->Next(1, mon.pparam(), 0)) {
                        LPOLESTR str = 0;
                        mon->GetDisplayName(0,0,&str);
                        const QString name = QString::fromUtf16((unsigned short*)str);
						ComPointer<IMalloc> alloc;
						::CoGetMalloc(1, alloc.pparam());
                        alloc->Free(str);

                        int insert_pos = 0;
                        if (!m_audioOutputs.contains(mon)) {
                            insert_pos = m_audioOutputs.count();
                            m_audioOutputs.append(mon);
                        } else {
                            insert_pos = m_audioOutputs.indexOf(mon);
                        }

                        if (name.contains(QLatin1String("DirectSound"))) {
                            ret.insert(nbds++, insert_pos);
                        } else {
                            ret.append(insert_pos);
                        }
                    }
#endif
					break;
                }
#ifndef QT_NO_PHONON_EFFECT
            case Phonon::EffectType:
                {
                    m_audioEffects.clear();
                    ComPointer<IEnumDMO> enumDMO;
                    HRESULT hr = ::DMOEnum(DMOCATEGORY_AUDIO_EFFECT, DMO_ENUMF_INCLUDE_KEYED, 0, 0, 0, 0, enumDMO.pparam());
                    if (SUCCEEDED(hr)) {
                        CLSID clsid;
                        while (S_OK == enumDMO->Next(1, &clsid, 0, 0)) {
                            ret += m_audioEffects.count();
                            m_audioEffects.append(clsid);
                        }
                    }
                    break;
                }
                break;
#endif //QT_NO_PHONON_EFFECT
            default:
                break;
            }
			return ret;
        }

        QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const
        {
            QHash<QByteArray, QVariant> ret;
            switch (type)
            {
            case Phonon::AudioOutputDeviceType:
                {
#ifdef Q_OS_WINCE
					ret["name"] = QLatin1String("default audio device");
#else
                    const AudioMoniker &mon = m_audioOutputs[index];
                    LPOLESTR str = 0;
                    HRESULT hr = mon->GetDisplayName(0,0, &str);
                    if (SUCCEEDED(hr)) {
                        QString name = QString::fromUtf16((unsigned short*)str);
						ComPointer<IMalloc> alloc;
						::CoGetMalloc(1, alloc.pparam());
                        alloc->Free(str);
                        ret["name"] = name.mid(name.indexOf('\\') + 1);
					}
#endif
                }
                break;
#ifndef QT_NO_PHONON_EFFECT
            case Phonon::EffectType:
                {
                    WCHAR name[80]; // 80 is clearly stated in the MSDN doc
                    HRESULT hr = ::DMOGetName(m_audioEffects[index], name);
                    if (SUCCEEDED(hr)) {
                        ret["name"] = QString::fromUtf16((unsigned short*)name);
                    }
                }
                break;
#endif //QT_NO_PHONON_EFFECT
            default:
                break;
            }
			return ret;
        }

        bool Backend::endConnectionChange(QSet<QObject *> objects)
        {
            //end of a transaction
            HRESULT hr = E_FAIL;
            if (!objects.isEmpty()) {
                Q_FOREACH(QObject *o, objects) {
                    if (BackendNode *node = qobject_cast<BackendNode*>(o)) {
                        MediaObject *mo = node->mediaObject();
                        if (mo && m_graphState.contains(mo)) {
                            switch(m_graphState[mo])
                            {
                            case Phonon::ErrorState:
                            case Phonon::StoppedState:
                            case Phonon::LoadingState:
                                //nothing to do
                                break;
                            case Phonon::PausedState:
                                mo->pause();
                                break;
                            default:
                                mo->play();
                                break;
                            }
                            if (mo->state() != Phonon::ErrorState) {
                                hr = S_OK;
                            }
                            m_graphState.remove(mo);
                        }
                    }
                }
            }
            return SUCCEEDED(hr);
        }

        bool Backend::startConnectionChange(QSet<QObject *> objects)
        {
            //start a transaction
            QSet<MediaObject*> mediaObjects;

            //let's save the state of the graph (before we stop it)
            Q_FOREACH(QObject *o, objects) {
                if (BackendNode *node = qobject_cast<BackendNode*>(o)) {
                    if (MediaObject *mo = node->mediaObject()) {
                        mediaObjects << mo;
                    }
                }
            }

            Q_FOREACH(MediaObject *mo, mediaObjects) {
                m_graphState[mo] = mo->state();
                mo->ensureStopped(); //we have to stop the graph..
            }

            return !mediaObjects.isEmpty();
        }

        bool Backend::connectNodes(QObject *_source, QObject *_sink)
        {
            BackendNode *source = qobject_cast<BackendNode*>(_source);
            if (!source) {
                return false;
            }
            BackendNode *sink = qobject_cast<BackendNode*>(_sink);
            if (!sink) {
                return false;
            }

            //setting the graph if needed
            if (source->mediaObject() == 0 && sink->mediaObject() == 0) {
                    //error: no graph selected
                    return false;
            } else if (source->mediaObject() && source->mediaObject() != sink->mediaObject()) {
                //this' graph becomes the common one
                source->mediaObject()->grabNode(sink);
            } else if (source->mediaObject() == 0) {
                //sink's graph becomes the common one
                sink->mediaObject()->grabNode(source);
            }

            return source->mediaObject()->connectNodes(source, sink);
        }

        bool Backend::disconnectNodes(QObject *_source, QObject *_sink)
        {
            BackendNode *source = qobject_cast<BackendNode*>(_source);
            if (!source) {
                return false;
            }
            BackendNode *sink = qobject_cast<BackendNode*>(_sink);
            if (!sink) {
                return false;
            }

            return source->mediaObject() == 0 ||
                source->mediaObject()->disconnectNodes(source, sink);
        }
    }
}

QT_END_NAMESPACE

#include "moc_backend.cpp"
<commit_msg>replace backslash with forward slash when fetching the supported mimetypes, since qsettings returns mime with backslashes<commit_after>/*  This file is part of the KDE project.

Copyright (C) 2007 Trolltech ASA. All rights reserved.

This library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 or 3 of the License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "backend.h"
#include "backendnode.h"

#include "audiooutput.h"
#include "effect.h"
#include "mediaobject.h"
#include "videowidget.h"
#include "volumeeffect.h"

//windows specific (DirectX Media Object)
#include <dmo.h>

#include <QtCore/QSettings>
#include <QtCore/QSet>
#include <QtCore/QVariant>

#include <QtCore/QtPlugin>

QT_BEGIN_NAMESPACE

Q_EXPORT_PLUGIN2(phonon_ds9, Phonon::DS9::Backend);

namespace Phonon
{
    namespace DS9
    {
        bool Backend::AudioMoniker::operator==(const AudioMoniker &other)
        {
            return other->IsEqual(*this) == S_OK;
        }

        Backend::Backend(QObject *parent, const QVariantList &)
            : QObject(parent)
        {
			::CoInitialize(0);

            //registering meta types
            qRegisterMetaType<HRESULT>("HRESULT");
            qRegisterMetaType<QSet<Filter> >("QSet<Filter>");
            qRegisterMetaType<Graph>("Graph");
        }

        Backend::~Backend()
        {
			::CoUninitialize();
        }

        QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
        {
            switch (c)
            {
            case MediaObjectClass:
                return new MediaObject(parent);
            case AudioOutputClass:
                return new AudioOutput(this, parent);
#ifndef QT_NO_PHONON_EFFECT
            case EffectClass:
                return new Effect(m_audioEffects[ args[0].toInt() ], parent);
#endif //QT_NO_PHONON_EFFECT
#ifndef QT_NO_PHONON_VIDEO
            case VideoWidgetClass:
                return new VideoWidget(qobject_cast<QWidget *>(parent));
#endif //QT_NO_PHONON_VIDEO
#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
            case VolumeFaderEffectClass:
                return new VolumeEffect(parent);
#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
            default:
                return 0;
            }
        }

        bool Backend::supportsVideo() const
        {
#ifndef QT_NO_PHONON_VIDEO
            return true;
#else
            return false;
#endif //QT_NO_PHONON_VIDEO
        }

        QStringList Backend::availableMimeTypes() const
        {
            QSet<QString> ret;

            const QStringList locations = QStringList() << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\mplayer2\\mime types")
                << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\wmplayer\\mime types");

            Q_FOREACH(QString s, locations) {
                QSettings settings(s, QSettings::NativeFormat);
                ret += settings.childGroups().replaceInStrings("\\", "/").toSet();
            }

            QStringList list = ret.toList();
            qSort(list);
            return list;
        }

		Filter Backend::getAudioOutputFilter(int index) const
		{
			Filter ret;
			if (index >= 0 && index < m_audioOutputs.count()) {
				m_audioOutputs.at(index)->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&ret));
			} else {
				//just return the default audio renderer (not directsound)
				ret = Filter(CLSID_AudioRender, IID_IBaseFilter);
			}
			return ret;
		}

        QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const
        {
            QList<int> ret;

            switch(type)
            {
            case Phonon::AudioOutputDeviceType:
                {
#ifdef Q_OS_WINCE
					ret << 0; // only one audio device with index 0
#else
					ComPointer<ICreateDevEnum> devEnum(CLSID_SystemDeviceEnum, IID_ICreateDevEnum);
					if (!devEnum) {
						return ret; //it is impossible to enumerate the devices
					}
                    ComPointer<IEnumMoniker> enumMon;
                    HRESULT hr = devEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, enumMon.pparam(), 0);
                    if (FAILED(hr)) {
                        break;
                    }
                    AudioMoniker mon;

                    //let's reorder the devices so that directshound appears first
                    int nbds = 0; //number of directsound devices

                    while (S_OK == enumMon->Next(1, mon.pparam(), 0)) {
                        LPOLESTR str = 0;
                        mon->GetDisplayName(0,0,&str);
                        const QString name = QString::fromUtf16((unsigned short*)str);
						ComPointer<IMalloc> alloc;
						::CoGetMalloc(1, alloc.pparam());
                        alloc->Free(str);

                        int insert_pos = 0;
                        if (!m_audioOutputs.contains(mon)) {
                            insert_pos = m_audioOutputs.count();
                            m_audioOutputs.append(mon);
                        } else {
                            insert_pos = m_audioOutputs.indexOf(mon);
                        }

                        if (name.contains(QLatin1String("DirectSound"))) {
                            ret.insert(nbds++, insert_pos);
                        } else {
                            ret.append(insert_pos);
                        }
                    }
#endif
					break;
                }
#ifndef QT_NO_PHONON_EFFECT
            case Phonon::EffectType:
                {
                    m_audioEffects.clear();
                    ComPointer<IEnumDMO> enumDMO;
                    HRESULT hr = ::DMOEnum(DMOCATEGORY_AUDIO_EFFECT, DMO_ENUMF_INCLUDE_KEYED, 0, 0, 0, 0, enumDMO.pparam());
                    if (SUCCEEDED(hr)) {
                        CLSID clsid;
                        while (S_OK == enumDMO->Next(1, &clsid, 0, 0)) {
                            ret += m_audioEffects.count();
                            m_audioEffects.append(clsid);
                        }
                    }
                    break;
                }
                break;
#endif //QT_NO_PHONON_EFFECT
            default:
                break;
            }
			return ret;
        }

        QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const
        {
            QHash<QByteArray, QVariant> ret;
            switch (type)
            {
            case Phonon::AudioOutputDeviceType:
                {
#ifdef Q_OS_WINCE
					ret["name"] = QLatin1String("default audio device");
#else
                    const AudioMoniker &mon = m_audioOutputs[index];
                    LPOLESTR str = 0;
                    HRESULT hr = mon->GetDisplayName(0,0, &str);
                    if (SUCCEEDED(hr)) {
                        QString name = QString::fromUtf16((unsigned short*)str);
						ComPointer<IMalloc> alloc;
						::CoGetMalloc(1, alloc.pparam());
                        alloc->Free(str);
                        ret["name"] = name.mid(name.indexOf('\\') + 1);
					}
#endif
                }
                break;
#ifndef QT_NO_PHONON_EFFECT
            case Phonon::EffectType:
                {
                    WCHAR name[80]; // 80 is clearly stated in the MSDN doc
                    HRESULT hr = ::DMOGetName(m_audioEffects[index], name);
                    if (SUCCEEDED(hr)) {
                        ret["name"] = QString::fromUtf16((unsigned short*)name);
                    }
                }
                break;
#endif //QT_NO_PHONON_EFFECT
            default:
                break;
            }
			return ret;
        }

        bool Backend::endConnectionChange(QSet<QObject *> objects)
        {
            //end of a transaction
            HRESULT hr = E_FAIL;
            if (!objects.isEmpty()) {
                Q_FOREACH(QObject *o, objects) {
                    if (BackendNode *node = qobject_cast<BackendNode*>(o)) {
                        MediaObject *mo = node->mediaObject();
                        if (mo && m_graphState.contains(mo)) {
                            switch(m_graphState[mo])
                            {
                            case Phonon::ErrorState:
                            case Phonon::StoppedState:
                            case Phonon::LoadingState:
                                //nothing to do
                                break;
                            case Phonon::PausedState:
                                mo->pause();
                                break;
                            default:
                                mo->play();
                                break;
                            }
                            if (mo->state() != Phonon::ErrorState) {
                                hr = S_OK;
                            }
                            m_graphState.remove(mo);
                        }
                    }
                }
            }
            return SUCCEEDED(hr);
        }

        bool Backend::startConnectionChange(QSet<QObject *> objects)
        {
            //start a transaction
            QSet<MediaObject*> mediaObjects;

            //let's save the state of the graph (before we stop it)
            Q_FOREACH(QObject *o, objects) {
                if (BackendNode *node = qobject_cast<BackendNode*>(o)) {
                    if (MediaObject *mo = node->mediaObject()) {
                        mediaObjects << mo;
                    }
                }
            }

            Q_FOREACH(MediaObject *mo, mediaObjects) {
                m_graphState[mo] = mo->state();
                mo->ensureStopped(); //we have to stop the graph..
            }

            return !mediaObjects.isEmpty();
        }

        bool Backend::connectNodes(QObject *_source, QObject *_sink)
        {
            BackendNode *source = qobject_cast<BackendNode*>(_source);
            if (!source) {
                return false;
            }
            BackendNode *sink = qobject_cast<BackendNode*>(_sink);
            if (!sink) {
                return false;
            }

            //setting the graph if needed
            if (source->mediaObject() == 0 && sink->mediaObject() == 0) {
                    //error: no graph selected
                    return false;
            } else if (source->mediaObject() && source->mediaObject() != sink->mediaObject()) {
                //this' graph becomes the common one
                source->mediaObject()->grabNode(sink);
            } else if (source->mediaObject() == 0) {
                //sink's graph becomes the common one
                sink->mediaObject()->grabNode(source);
            }

            return source->mediaObject()->connectNodes(source, sink);
        }

        bool Backend::disconnectNodes(QObject *_source, QObject *_sink)
        {
            BackendNode *source = qobject_cast<BackendNode*>(_source);
            if (!source) {
                return false;
            }
            BackendNode *sink = qobject_cast<BackendNode*>(_sink);
            if (!sink) {
                return false;
            }

            return source->mediaObject() == 0 ||
                source->mediaObject()->disconnectNodes(source, sink);
        }
    }
}

QT_END_NAMESPACE

#include "moc_backend.cpp"
<|endoftext|>


<commit_before>/* Copyright (C) 2005 MySQL AB

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation				// gcc: Class implementation
#endif

#define MYSQL_SERVER 1
#include "mysql_priv.h"
#include "probes_mysql.h"
#include "ha_blackhole.h"

/* Static declarations for handlerton */

static handler *blackhole_create_handler(handlerton *hton,
                                         TABLE_SHARE *table,
                                         MEM_ROOT *mem_root)
{
  return new (mem_root) ha_blackhole(hton, table);
}

/* Static declarations for shared structures */

static pthread_mutex_t blackhole_mutex;
static HASH blackhole_open_tables;

static st_blackhole_share *get_share(const char *table_name);
static void free_share(st_blackhole_share *share);

/*****************************************************************************
** BLACKHOLE tables
*****************************************************************************/

ha_blackhole::ha_blackhole(handlerton *hton,
                           TABLE_SHARE *table_arg)
  :handler(hton, table_arg)
{}

static const char *ha_blackhole_exts[] = {
  NullS
};

const char **ha_blackhole::bas_ext() const
{
  return ha_blackhole_exts;
}

int ha_blackhole::open(const char *name, int mode, uint test_if_locked)
{
  DBUG_ENTER("ha_blackhole::open");

  if (!(share= get_share(name)))
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);

  thr_lock_data_init(&share->lock, &lock, NULL);
  DBUG_RETURN(0);
}

int ha_blackhole::close(void)
{
  DBUG_ENTER("ha_blackhole::close");
  free_share(share);
  DBUG_RETURN(0);
}

int ha_blackhole::create(const char *name, TABLE *table_arg,
                         HA_CREATE_INFO *create_info)
{
  DBUG_ENTER("ha_blackhole::create");
  DBUG_RETURN(0);
}

const char *ha_blackhole::index_type(uint key_number)
{
  DBUG_ENTER("ha_blackhole::index_type");
  DBUG_RETURN((table_share->key_info[key_number].flags & HA_FULLTEXT) ?
              "FULLTEXT" :
              (table_share->key_info[key_number].flags & HA_SPATIAL) ?
              "SPATIAL" :
              (table_share->key_info[key_number].algorithm ==
               HA_KEY_ALG_RTREE) ? "RTREE" : "BTREE");
}

int ha_blackhole::write_row(uchar * buf)
{
  DBUG_ENTER("ha_blackhole::write_row");
  DBUG_RETURN(table->next_number_field ? update_auto_increment() : 0);
}

int ha_blackhole::update_row(const uchar *old_data, uchar *new_data)
{
  DBUG_ENTER("ha_blackhole::update_row");
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    DBUG_RETURN(0);
  DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}

int ha_blackhole::delete_row(const uchar *buf)
{
  DBUG_ENTER("ha_blackhole::delete_row");
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    DBUG_RETURN(0);
  DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}

int ha_blackhole::rnd_init(bool scan)
{
  DBUG_ENTER("ha_blackhole::rnd_init");
  DBUG_RETURN(0);
}

int ha_blackhole::rnd_next(uchar *buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::rnd_next");
  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
                       TRUE);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::rnd_pos(uchar * buf, uchar *pos)
{
  DBUG_ENTER("ha_blackhole::rnd_pos");
  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
                       FALSE);
  DBUG_ASSERT(0);
  MYSQL_READ_ROW_DONE(0);
  DBUG_RETURN(0);
}

void ha_blackhole::position(const uchar *record)
{
  DBUG_ENTER("ha_blackhole::position");
  DBUG_ASSERT(0);
  DBUG_VOID_RETURN;
}

int ha_blackhole::info(uint flag)
{
  DBUG_ENTER("ha_blackhole::info");

  bzero((char*) &stats, sizeof(stats));
  if (flag & HA_STATUS_AUTO)
    stats.auto_increment_value= 1;
  DBUG_RETURN(0);
}

int ha_blackhole::external_lock(THD *thd, int lock_type)
{
  DBUG_ENTER("ha_blackhole::external_lock");
  DBUG_RETURN(0);
}

THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd,
                                         THR_LOCK_DATA **to,
                                         enum thr_lock_type lock_type)
{
  DBUG_ENTER("ha_blackhole::store_lock");
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
  {
    /*
      Here is where we get into the guts of a row level lock.
      If TL_UNLOCK is set
      If we are not doing a LOCK TABLE or DISCARD/IMPORT
      TABLESPACE, then allow multiple writers
    */

    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
         lock_type <= TL_WRITE) && !thd_in_lock_tables(thd)
        && !thd_tablespace_op(thd))
      lock_type = TL_WRITE_ALLOW_WRITE;

    /*
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
      to t2. Convert the lock to a normal read lock to allow
      concurrent inserts to t2.
    */

    if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd))
      lock_type = TL_READ;

    lock.type= lock_type;
  }
  *to++= &lock;
  DBUG_RETURN(to);
}

int ha_blackhole::index_read_map(uchar * buf, const uchar * key,
                                 key_part_map keypart_map,
                             enum ha_rkey_function find_flag)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key,
                                 key_part_map keypart_map,
                                 enum ha_rkey_function find_flag)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read_idx");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key,
                                      key_part_map keypart_map)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read_last");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_next(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_next");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_prev(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_prev");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_first(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_first");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
  DBUG_RETURN(HA_ERR_END_OF_FILE);
}

int ha_blackhole::index_last(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_last");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

static st_blackhole_share *get_share(const char *table_name)
{
  st_blackhole_share *share;
  uint length;

  length= (uint) strlen(table_name);
  pthread_mutex_lock(&blackhole_mutex);

  if (!(share= (st_blackhole_share*) hash_search(&blackhole_open_tables,
                                                 (uchar*) table_name, length)))
  {
    if (!(share= (st_blackhole_share*) my_malloc(sizeof(st_blackhole_share) +
                                                 length,
                                                 MYF(MY_WME | MY_ZEROFILL))))
      goto error;

    share->table_name_length= length;
    strmov(share->table_name, table_name);

    if (my_hash_insert(&blackhole_open_tables, (uchar*) share))
    {
      my_free((uchar*) share, MYF(0));
      share= NULL;
      goto error;
    }

    thr_lock_init(&share->lock);
  }
  share->use_count++;

error:
  pthread_mutex_unlock(&blackhole_mutex);
  return share;
}

static void free_share(st_blackhole_share *share)
{
  pthread_mutex_lock(&blackhole_mutex);
  if (!--share->use_count)
    hash_delete(&blackhole_open_tables, (uchar*) share);
  pthread_mutex_unlock(&blackhole_mutex);
}

static void blackhole_free_key(st_blackhole_share *share)
{
  thr_lock_delete(&share->lock);
  my_free((uchar*) share, MYF(0));
}

static uchar* blackhole_get_key(st_blackhole_share *share, size_t *length,
                                my_bool not_used __attribute__((unused)))
{
  *length= share->table_name_length;
  return (uchar*) share->table_name;
}

static int blackhole_init(void *p)
{
  handlerton *blackhole_hton;
  blackhole_hton= (handlerton *)p;
  blackhole_hton->state= SHOW_OPTION_YES;
  blackhole_hton->db_type= DB_TYPE_BLACKHOLE_DB;
  blackhole_hton->create= blackhole_create_handler;
  blackhole_hton->flags= HTON_CAN_RECREATE;

  VOID(pthread_mutex_init(&blackhole_mutex, MY_MUTEX_INIT_FAST));
  (void) hash_init(&blackhole_open_tables, system_charset_info,32,0,0,
                   (hash_get_key) blackhole_get_key,
                   (hash_free_key) blackhole_free_key, 0);

  return 0;
}

static int blackhole_fini(void *p)
{
  hash_free(&blackhole_open_tables);
  pthread_mutex_destroy(&blackhole_mutex);

  return 0;
}

struct st_mysql_storage_engine blackhole_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };

mysql_declare_plugin(blackhole)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
  &blackhole_storage_engine,
  "BLACKHOLE",
  "MySQL AB",
  "/dev/null storage engine (anything you write to it disappears)",
  PLUGIN_LICENSE_GPL,
  blackhole_init, /* Plugin Init */
  blackhole_fini, /* Plugin Deinit */
  0x0100 /* 1.0 */,
  NULL,                       /* status variables                */
  NULL,                       /* system variables                */
  NULL                        /* config options                  */
}
mysql_declare_plugin_end;
<commit_msg>Fixed missing declaration<commit_after>/* Copyright (C) 2005 MySQL AB

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation				// gcc: Class implementation
#endif

#define MYSQL_SERVER 1
#include "mysql_priv.h"
#include "probes_mysql.h"
#include "ha_blackhole.h"

/* Static declarations for handlerton */

static handler *blackhole_create_handler(handlerton *hton,
                                         TABLE_SHARE *table,
                                         MEM_ROOT *mem_root)
{
  return new (mem_root) ha_blackhole(hton, table);
}

/* Static declarations for shared structures */

static pthread_mutex_t blackhole_mutex;
static HASH blackhole_open_tables;

static st_blackhole_share *get_share(const char *table_name);
static void free_share(st_blackhole_share *share);

/*****************************************************************************
** BLACKHOLE tables
*****************************************************************************/

ha_blackhole::ha_blackhole(handlerton *hton,
                           TABLE_SHARE *table_arg)
  :handler(hton, table_arg)
{}

static const char *ha_blackhole_exts[] = {
  NullS
};

const char **ha_blackhole::bas_ext() const
{
  return ha_blackhole_exts;
}

int ha_blackhole::open(const char *name, int mode, uint test_if_locked)
{
  DBUG_ENTER("ha_blackhole::open");

  if (!(share= get_share(name)))
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);

  thr_lock_data_init(&share->lock, &lock, NULL);
  DBUG_RETURN(0);
}

int ha_blackhole::close(void)
{
  DBUG_ENTER("ha_blackhole::close");
  free_share(share);
  DBUG_RETURN(0);
}

int ha_blackhole::create(const char *name, TABLE *table_arg,
                         HA_CREATE_INFO *create_info)
{
  DBUG_ENTER("ha_blackhole::create");
  DBUG_RETURN(0);
}

const char *ha_blackhole::index_type(uint key_number)
{
  DBUG_ENTER("ha_blackhole::index_type");
  DBUG_RETURN((table_share->key_info[key_number].flags & HA_FULLTEXT) ?
              "FULLTEXT" :
              (table_share->key_info[key_number].flags & HA_SPATIAL) ?
              "SPATIAL" :
              (table_share->key_info[key_number].algorithm ==
               HA_KEY_ALG_RTREE) ? "RTREE" : "BTREE");
}

int ha_blackhole::write_row(uchar * buf)
{
  DBUG_ENTER("ha_blackhole::write_row");
  DBUG_RETURN(table->next_number_field ? update_auto_increment() : 0);
}

int ha_blackhole::update_row(const uchar *old_data, uchar *new_data)
{
  DBUG_ENTER("ha_blackhole::update_row");
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    DBUG_RETURN(0);
  DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}

int ha_blackhole::delete_row(const uchar *buf)
{
  DBUG_ENTER("ha_blackhole::delete_row");
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    DBUG_RETURN(0);
  DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}

int ha_blackhole::rnd_init(bool scan)
{
  DBUG_ENTER("ha_blackhole::rnd_init");
  DBUG_RETURN(0);
}

int ha_blackhole::rnd_next(uchar *buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::rnd_next");
  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
                       TRUE);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::rnd_pos(uchar * buf, uchar *pos)
{
  DBUG_ENTER("ha_blackhole::rnd_pos");
  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
                       FALSE);
  DBUG_ASSERT(0);
  MYSQL_READ_ROW_DONE(0);
  DBUG_RETURN(0);
}

void ha_blackhole::position(const uchar *record)
{
  DBUG_ENTER("ha_blackhole::position");
  DBUG_ASSERT(0);
  DBUG_VOID_RETURN;
}

int ha_blackhole::info(uint flag)
{
  DBUG_ENTER("ha_blackhole::info");

  bzero((char*) &stats, sizeof(stats));
  if (flag & HA_STATUS_AUTO)
    stats.auto_increment_value= 1;
  DBUG_RETURN(0);
}

int ha_blackhole::external_lock(THD *thd, int lock_type)
{
  DBUG_ENTER("ha_blackhole::external_lock");
  DBUG_RETURN(0);
}

THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd,
                                         THR_LOCK_DATA **to,
                                         enum thr_lock_type lock_type)
{
  DBUG_ENTER("ha_blackhole::store_lock");
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
  {
    /*
      Here is where we get into the guts of a row level lock.
      If TL_UNLOCK is set
      If we are not doing a LOCK TABLE or DISCARD/IMPORT
      TABLESPACE, then allow multiple writers
    */

    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
         lock_type <= TL_WRITE) && !thd_in_lock_tables(thd)
        && !thd_tablespace_op(thd))
      lock_type = TL_WRITE_ALLOW_WRITE;

    /*
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
      to t2. Convert the lock to a normal read lock to allow
      concurrent inserts to t2.
    */

    if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd))
      lock_type = TL_READ;

    lock.type= lock_type;
  }
  *to++= &lock;
  DBUG_RETURN(to);
}

int ha_blackhole::index_read_map(uchar * buf, const uchar * key,
                                 key_part_map keypart_map,
                             enum ha_rkey_function find_flag)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key,
                                 key_part_map keypart_map,
                                 enum ha_rkey_function find_flag)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read_idx");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key,
                                      key_part_map keypart_map)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_read_last");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  THD *thd= ha_thd();
  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL)
    rc= 0;
  else
    rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_next(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_next");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_prev(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_prev");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

int ha_blackhole::index_first(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_first");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
  DBUG_RETURN(HA_ERR_END_OF_FILE);
}

int ha_blackhole::index_last(uchar * buf)
{
  int rc;
  DBUG_ENTER("ha_blackhole::index_last");
  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
  rc= HA_ERR_END_OF_FILE;
  MYSQL_INDEX_READ_ROW_DONE(rc);
  DBUG_RETURN(rc);
}

static st_blackhole_share *get_share(const char *table_name)
{
  st_blackhole_share *share;
  uint length;

  length= (uint) strlen(table_name);
  pthread_mutex_lock(&blackhole_mutex);

  if (!(share= (st_blackhole_share*) hash_search(&blackhole_open_tables,
                                                 (uchar*) table_name, length)))
  {
    if (!(share= (st_blackhole_share*) my_malloc(sizeof(st_blackhole_share) +
                                                 length,
                                                 MYF(MY_WME | MY_ZEROFILL))))
      goto error;

    share->table_name_length= length;
    strmov(share->table_name, table_name);

    if (my_hash_insert(&blackhole_open_tables, (uchar*) share))
    {
      my_free((uchar*) share, MYF(0));
      share= NULL;
      goto error;
    }

    thr_lock_init(&share->lock);
  }
  share->use_count++;

error:
  pthread_mutex_unlock(&blackhole_mutex);
  return share;
}

static void free_share(st_blackhole_share *share)
{
  pthread_mutex_lock(&blackhole_mutex);
  if (!--share->use_count)
    hash_delete(&blackhole_open_tables, (uchar*) share);
  pthread_mutex_unlock(&blackhole_mutex);
}

static void blackhole_free_key(st_blackhole_share *share)
{
  thr_lock_delete(&share->lock);
  my_free((uchar*) share, MYF(0));
}

static uchar* blackhole_get_key(st_blackhole_share *share, size_t *length,
                                my_bool not_used __attribute__((unused)))
{
  *length= share->table_name_length;
  return (uchar*) share->table_name;
}

static int blackhole_init(void *p)
{
  handlerton *blackhole_hton;
  blackhole_hton= (handlerton *)p;
  blackhole_hton->state= SHOW_OPTION_YES;
  blackhole_hton->db_type= DB_TYPE_BLACKHOLE_DB;
  blackhole_hton->create= blackhole_create_handler;
  blackhole_hton->flags= HTON_CAN_RECREATE;

  VOID(pthread_mutex_init(&blackhole_mutex, MY_MUTEX_INIT_FAST));
  (void) hash_init(&blackhole_open_tables, system_charset_info,32,0,0,
                   (hash_get_key) blackhole_get_key,
                   (hash_free_key) blackhole_free_key, 0);

  return 0;
}

static int blackhole_fini(void *p)
{
  hash_free(&blackhole_open_tables);
  pthread_mutex_destroy(&blackhole_mutex);

  return 0;
}

struct st_mysql_storage_engine blackhole_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };

mysql_declare_plugin(blackhole)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
  &blackhole_storage_engine,
  "BLACKHOLE",
  "MySQL AB",
  "/dev/null storage engine (anything you write to it disappears)",
  PLUGIN_LICENSE_GPL,
  blackhole_init, /* Plugin Init */
  blackhole_fini, /* Plugin Deinit */
  0x0100 /* 1.0 */,
  NULL,                       /* status variables                */
  NULL,                       /* system variables                */
  NULL                        /* config options                  */
}
mysql_declare_plugin_end;
<|endoftext|>