Cool-Y.github.io/2018/12/15/miio-control/index.html
2019-03-30 17:46:41 +08:00

1425 lines
46 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">
<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">
<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">
<link rel="apple-touch-icon" sizes="180x180" href="/images/hackerrank.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="32x32" href="/images/hackerrank.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="16x16" href="/images/hackerrank.png?v=5.1.4">
<link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">
<meta name="keywords" content="小米,miio,中间人,重放攻击,">
<meta name="description" content="控制局域网内的IOT设备中间人攻击—流量分析使用Nmap分析局域网内设备得到智能设备的IP 小米智能插座192.168.31.197 网关192.168.31.147控制它的手机ip ettercap嗅探智能设备和网关之间的流量sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//">
<meta name="keywords" content="小米,miio,中间人,重放攻击">
<meta property="og:type" content="article">
<meta property="og:title" content="利用miio控制局域网内的小米智能设备">
<meta property="og:url" content="https://cool-y.github.io/2018/12/15/miio-control/index.html">
<meta property="og:site_name" content="混元霹雳手">
<meta property="og:description" content="控制局域网内的IOT设备中间人攻击—流量分析使用Nmap分析局域网内设备得到智能设备的IP 小米智能插座192.168.31.197 网关192.168.31.147控制它的手机ip ettercap嗅探智能设备和网关之间的流量sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/1.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/2.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/3.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/4.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/5.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/6.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/7.png">
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/8.png">
<meta property="og:updated_time" content="2019-03-23T11:32:01.533Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="利用miio控制局域网内的小米智能设备">
<meta name="twitter:description" content="控制局域网内的IOT设备中间人攻击—流量分析使用Nmap分析局域网内设备得到智能设备的IP 小米智能插座192.168.31.197 网关192.168.31.147控制它的手机ip ettercap嗅探智能设备和网关之间的流量sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//">
<meta name="twitter:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/1.png">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Gemini',
version: '5.1.4',
sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="https://cool-y.github.io/2018/12/15/miio-control/">
<title>利用miio控制局域网内的小米智能设备 | 混元霹雳手</title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container sidebar-position-left page-post-detail">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">混元霹雳手</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle"></p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br>
首页
</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" rel="section">
<i class="menu-item-icon fa fa-fw fa-user"></i> <br>
关于
</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section">
<i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
标签
</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section">
<i class="menu-item-icon fa fa-fw fa-th"></i> <br>
分类
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
归档
</a>
</li>
<li class="menu-item menu-item-bookmarks">
<a href="/bookmarks/" rel="section">
<i class="menu-item-icon fa fa-fw fa-map"></i> <br>
书签
</a>
</li>
<li class="menu-item menu-item-search">
<a href="javascript:;" class="popup-trigger">
<i class="menu-item-icon fa fa-search fa-fw"></i> <br>
搜索
</a>
</li>
</ul>
<div class="site-search">
<div class="popup search-popup local-search-popup">
<div class="local-search-header clearfix">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<span class="popup-btn-close">
<i class="fa fa-times-circle"></i>
</span>
<div class="local-search-input-wrapper">
<input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
</div>
</div>
<div id="local-search-result"></div>
</div>
</div>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<div id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://cool-y.github.io/2018/12/15/miio-control/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Cool-Y">
<meta itemprop="description" content>
<meta itemprop="image" content="/images/avatar.png">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="混元霹雳手">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">利用miio控制局域网内的小米智能设备</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-12-15T14:38:15+08:00">
2018-12-15
</time>
</span>
<span class="post-category">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/IOT/" itemprop="url" rel="index">
<span itemprop="name">IOT</span>
</a>
</span>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2018/12/15/miio-control/#comments" itemprop="discussionUrl">
<span class="post-comments-count gitment-comments-count" data-xid="/2018/12/15/miio-control/" itemprop="commentsCount"></span>
</a>
</span>
<span id="/2018/12/15/miio-control/" class="leancloud_visitors" data-flag-title="利用miio控制局域网内的小米智能设备">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读次数&#58;</span>
<span class="leancloud-visitors-count"></span>
</span>
<div class="post-wordcount">
<span class="post-meta-item-icon">
<i class="fa fa-file-word-o"></i>
</span>
<span title="字数统计">
955 字
</span>
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-clock-o"></i>
</span>
<span title="阅读时长">
3 分钟
</span>
</div>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="控制局域网内的IOT设备"><a href="#控制局域网内的IOT设备" class="headerlink" title="控制局域网内的IOT设备"></a>控制局域网内的IOT设备</h1><h2 id="中间人攻击—流量分析"><a href="#中间人攻击—流量分析" class="headerlink" title="中间人攻击—流量分析"></a>中间人攻击—流量分析</h2><h3 id="使用Nmap分析局域网内设备得到智能设备的IP"><a href="#使用Nmap分析局域网内设备得到智能设备的IP" class="headerlink" title="使用Nmap分析局域网内设备得到智能设备的IP"></a>使用Nmap分析局域网内设备得到智能设备的IP</h3><p><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/1.png" alt><br> 小米智能插座192.168.31.197 网关192.168.31.147控制它的手机ip</p>
<h3 id="ettercap嗅探智能设备和网关之间的流量"><a href="#ettercap嗅探智能设备和网关之间的流量" class="headerlink" title="ettercap嗅探智能设备和网关之间的流量"></a>ettercap嗅探智能设备和网关之间的流量</h3><p>sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//</p>
<h3 id="wireshark抓包分析"><a href="#wireshark抓包分析" class="headerlink" title="wireshark抓包分析"></a>wireshark抓包分析</h3><p><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/2.png" alt><br>从图中可以看出设备的命令控制包为UDP传输既然是UDP协议传输那么是否可以通过命令包重放攻击来对设备进行控制<br>了解到在homeassistant中可实现对小米设备的集成并在其中对设备进行管理和操作。Homeassistant主要以Python语言开发既然它能操控小米设备那它底层肯定有相关的函数调用库。<br>为了可以消除对专有软件(米家app)的依赖并能控制自己的设备所以出现了MiIo。设备和米家app在同一局域网下使用的加密专有网络协议我们称之为MiIo协议。<br>Miio库支持的设备有<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/3.png" alt></p>
<h2 id="小米IOT控制流程"><a href="#小米IOT控制流程" class="headerlink" title="小米IOT控制流程"></a>小米IOT控制流程</h2><p>在同一局域网中小米设备可以使用专有的加密UDP网络协议进行通信控制。在网络可达的前提下向小米设备发送hello bytes就可以获得含有token的结构体数据。之后构造相应的结构体并且以同样的方式发送给设备即可完成控制。具体流程如下<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/4.png" alt></p>
<h2 id="设备Token的获取方式"><a href="#设备Token的获取方式" class="headerlink" title="设备Token的获取方式"></a>设备Token的获取方式</h2><p>小米设备的token获取有三种途径miio获取、从米家app获取、从数据库获取</p>
<h3 id="miio获取"><a href="#miio获取" class="headerlink" title="miio获取"></a>miio获取</h3><p>在ubuntu下先安装miio然后发现设备<br>npminstall -g miio<br>miiodiscover<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/5.png" alt><br>但是很可惜很多设备隐藏了token使用该方法可能无法获取到token或获取到的token不正确。</p>
<h3 id="米家app获取"><a href="#米家app获取" class="headerlink" title="米家app获取"></a>米家app获取</h3><p>这种方法需要的mijia app版本较老且只对部分设备有效。</p>
<h3 id="从数据库获取token"><a href="#从数据库获取token" class="headerlink" title="从数据库获取token"></a>从数据库获取token</h3><p>这种方法仅在Mi Home 5.0.19之前的版本可用。<br>该方法是读取手机中米家的app中的数据记录来获取设备的token具体步骤如下</p>
<ul>
<li>准备一部获取root权限的安卓手机</li>
<li>安装米家app并登录账号</li>
<li>进入/data/data/com.xiaomi.smarthome/databases/</li>
<li>拷贝db下载到电脑</li>
<li><a href="http://miio2.yinhh.com/" target="_blank" rel="noopener">前往网站</a>上传db点击提交即可获得token。</li>
<li>8894c73cbd5c7224fb4b8a39e360c255<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/6.png" alt></li>
</ul>
<h2 id="脚本控制IOT设备"><a href="#脚本控制IOT设备" class="headerlink" title="脚本控制IOT设备"></a>脚本控制IOT设备</h2><p>首先随意发送hellobytes获得时间和设备IDtoken我们自己设置然后构造发送的数据结构msgcmd中的method包括set_power(控制开关)、get_prop(获取状态)控制的params是[on]/ [off]获取状态的params是[power, temperature]<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/7.png" alt><br>如果获得了token就能对小米的设备进行操作如图下面是返回的信息。<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/8.png" alt></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>从目前的智能家居市场来看,用户不会只使用单个智能设备厂商的设备,所以对于厂商来说,通过开放接口给用户一些局域网的控制“自由”,实现不同厂商设备的联动是一个不错的选择。<br>从另外一个角度本文中体现的安全问题我们也不容忽视。如果在局域网中不经过认证就能获取物联网设备的访问凭证并进而进行控制无形中给入侵者留了一扇门。例如攻击者可经过扫描互联网发现家庭路由器并利用弱口令或设备漏洞获得路由器的shell权限接下来就可按照文中步骤就可以获得设备token进而控制。好在小米已经在最新的miio版本中修复了这一漏洞大大提高了攻击者获取token的难度。</p>
</div>
<div>
<div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
<div>您的支持将鼓励我继续创作!</div>
<button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
<span>打赏</span>
</button>
<div id="QR" style="display: none;">
<div id="wechat" style="display: inline-block">
<img id="wechat_qr" src="/images/Wechatpay.png" alt="Cool-Y 微信支付">
<p>微信支付</p>
</div>
<div id="alipay" style="display: inline-block">
<img id="alipay_qr" src="/images/Alipay.png" alt="Cool-Y 支付宝">
<p>支付宝</p>
</div>
</div>
</div>
</div>
<footer class="post-footer">
<div class="post-tags">
<a href="/tags/小米/" rel="tag"># 小米</a>
<a href="/tags/miio/" rel="tag"># miio</a>
<a href="/tags/中间人/" rel="tag"># 中间人</a>
<a href="/tags/重放攻击/" rel="tag"># 重放攻击</a>
</div>
<div class="post-nav">
<div class="post-nav-next post-nav-item">
<a href="/2018/11/16/BIBA访问控制模型实现(python)/" rel="next" title="利用python实现BIBA模型">
<i class="fa fa-chevron-left"></i> 利用python实现BIBA模型
</a>
</div>
<span class="post-nav-divider"></span>
<div class="post-nav-prev post-nav-item">
<a href="/2018/12/23/基于规则引擎发现IOT设备/" rel="prev" title="基于采集规则引擎的物联网设备发现方法">
基于采集规则引擎的物联网设备发现方法 <i class="fa fa-chevron-right"></i>
</a>
</div>
</div>
</footer>
</div>
</article>
<div class="post-spread">
</div>
</div>
</div>
<div class="comments" id="comments">
<div id="gitment-container"></div>
</div>
</div>
<div class="sidebar-toggle">
<div class="sidebar-toggle-line-wrap">
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
</div>
</div>
<aside id="sidebar" class="sidebar">
<div class="sidebar-inner">
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
文章目录
</li>
<li class="sidebar-nav-overview" data-target="site-overview-wrap">
站点概览
</li>
</ul>
<section class="site-overview-wrap sidebar-panel">
<div class="site-overview">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image" src="/images/avatar.png" alt="Cool-Y">
<p class="site-author-name" itemprop="name">Cool-Y</p>
<p class="site-description motion-element" itemprop="description"></p>
</div>
<nav class="site-state motion-element">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">11</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/index.html">
<span class="site-state-item-count">7</span>
<span class="site-state-item-name">分类</span>
</a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/index.html">
<span class="site-state-item-count">27</span>
<span class="site-state-item-name">标签</span>
</a>
</div>
</nav>
<div class="links-of-author motion-element">
<span class="links-of-author-item">
<a href="https://github.com/Cool-Y" target="_blank" title="GitHub">
<i class="fa fa-fw fa-github"></i>GitHub</a>
</span>
<span class="links-of-author-item">
<a href="mailto:cool.yim@whu.edu.cn" target="_blank" title="E-Mail">
<i class="fa fa-fw fa-envelope"></i>E-Mail</a>
</span>
<span class="links-of-author-item">
<a href="https://www.instagram.com/yan__han/" target="_blank" title="Instagram">
<i class="fa fa-fw fa-instagram"></i>Instagram</a>
</span>
</div>
</div>
</section>
<!--noindex-->
<section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
<div class="post-toc">
<div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#控制局域网内的IOT设备"><span class="nav-number">1.</span> <span class="nav-text">控制局域网内的IOT设备</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#中间人攻击—流量分析"><span class="nav-number">1.1.</span> <span class="nav-text">中间人攻击—流量分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#使用Nmap分析局域网内设备得到智能设备的IP"><span class="nav-number">1.1.1.</span> <span class="nav-text">使用Nmap分析局域网内设备得到智能设备的IP</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ettercap嗅探智能设备和网关之间的流量"><span class="nav-number">1.1.2.</span> <span class="nav-text">ettercap嗅探智能设备和网关之间的流量</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#wireshark抓包分析"><span class="nav-number">1.1.3.</span> <span class="nav-text">wireshark抓包分析</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#小米IOT控制流程"><span class="nav-number">1.2.</span> <span class="nav-text">小米IOT控制流程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#设备Token的获取方式"><span class="nav-number">1.3.</span> <span class="nav-text">设备Token的获取方式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#miio获取"><span class="nav-number">1.3.1.</span> <span class="nav-text">miio获取</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#米家app获取"><span class="nav-number">1.3.2.</span> <span class="nav-text">米家app获取</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#从数据库获取token"><span class="nav-number">1.3.3.</span> <span class="nav-text">从数据库获取token</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#脚本控制IOT设备"><span class="nav-number">1.4.</span> <span class="nav-text">脚本控制IOT设备</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#总结"><span class="nav-number">1.5.</span> <span class="nav-text">总结</span></a></li></ol></li></ol></div>
</div>
</section>
<!--/noindex-->
</div>
</aside>
</div>
</main>
<footer id="footer" class="footer">
<div class="footer-inner">
<div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
<span class="with-love">
<i class="fa fa-user"></i>
</span>
<span class="author" itemprop="copyrightHolder">Cool-Y</span>
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-area-chart"></i>
</span>
<span title="Site words total count">16.3k</span>
</div>
<div class="powered-by"><a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>
<div class="busuanzi-count">
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<span class="site-uv">
<i class="fa fa-user"></i>
<span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
</span>
<span class="site-pv">
<i class="fa fa-eye"></i>
<span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
</span>
</div>
</div>
</footer>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
</div>
</div>
<script type="text/javascript">
if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
window.Promise = null;
}
</script>
<script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
<script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
<script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>
<!-- LOCAL: You can save these files to your site and update links -->
<link rel="stylesheet" href="https://aimingoo.github.io/gitmint/style/default.css">
<script src="https://aimingoo.github.io/gitmint/dist/gitmint.browser.js"></script>
<!-- END LOCAL -->
<script type="text/javascript">
function renderGitment(){
var gitment = new Gitmint({
id: window.location.pathname,
owner: 'Cool-Y',
repo: 'gitment-comments',
lang: "" || navigator.language || navigator.systemLanguage || navigator.userLanguage,
oauth: {
client_secret: '1c5db4da72df5e6fc318d12afe5f4406f7c54343',
client_id: '180955a2c3ae3d966d9a'
}});
gitment.render('gitment-container');
}
renderGitment();
</script>
<script type="text/javascript">
// Popup Window;
var isfetched = false;
var isXml = true;
// Search DB path;
var search_path = "search.xml";
if (search_path.length === 0) {
search_path = "search.xml";
} else if (/json$/i.test(search_path)) {
isXml = false;
}
var path = "/" + search_path;
// monitor main search box;
var onPopupClose = function (e) {
$('.popup').hide();
$('#local-search-input').val('');
$('.search-result-list').remove();
$('#no-result').remove();
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
}
function proceedsearch() {
$("body")
.append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
.css('overflow', 'hidden');
$('.search-popup-overlay').click(onPopupClose);
$('.popup').toggle();
var $localSearchInput = $('#local-search-input');
$localSearchInput.attr("autocapitalize", "none");
$localSearchInput.attr("autocorrect", "off");
$localSearchInput.focus();
}
// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
// start loading animation
$("body")
.append('<div class="search-popup-overlay local-search-pop-overlay">' +
'<div id="search-loading-icon">' +
'<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
'</div>' +
'</div>')
.css('overflow', 'hidden');
$("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
$.ajax({
url: path,
dataType: isXml ? "xml" : "json",
async: true,
success: function(res) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = isXml ? $("entry", res).map(function() {
return {
title: $("title", this).text(),
content: $("content",this).text(),
url: $("url" , this).text()
};
}).get() : res;
var input = document.getElementById(search_id);
var resultContent = document.getElementById(content_id);
var inputEventFunction = function() {
var searchText = input.value.trim().toLowerCase();
var keywords = searchText.split(/[\s\-]+/);
if (keywords.length > 1) {
keywords.push(searchText);
}
var resultItems = [];
if (searchText.length > 0) {
// perform local searching
datas.forEach(function(data) {
var isMatch = false;
var hitCount = 0;
var searchTextCount = 0;
var title = data.title.trim();
var titleInLowerCase = title.toLowerCase();
var content = data.content.trim().replace(/<[^>]+>/g,"");
var contentInLowerCase = content.toLowerCase();
var articleUrl = decodeURIComponent(data.url);
var indexOfTitle = [];
var indexOfContent = [];
// only match articles with not empty titles
if(title != '') {
keywords.forEach(function(keyword) {
function getIndexByWord(word, text, caseSensitive) {
var wordLen = word.length;
if (wordLen === 0) {
return [];
}
var startPosition = 0, position = [], index = [];
if (!caseSensitive) {
text = text.toLowerCase();
word = word.toLowerCase();
}
while ((position = text.indexOf(word, startPosition)) > -1) {
index.push({position: position, word: word});
startPosition = position + wordLen;
}
return index;
}
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
});
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
isMatch = true;
hitCount = indexOfTitle.length + indexOfContent.length;
}
}
// show search results
if (isMatch) {
// sort index by position of keyword
[indexOfTitle, indexOfContent].forEach(function (index) {
index.sort(function (itemLeft, itemRight) {
if (itemRight.position !== itemLeft.position) {
return itemRight.position - itemLeft.position;
} else {
return itemLeft.word.length - itemRight.word.length;
}
});
});
// merge hits into slices
function mergeIntoSlice(text, start, end, index) {
var item = index[index.length - 1];
var position = item.position;
var word = item.word;
var hits = [];
var searchTextCountInSlice = 0;
while (position + word.length <= end && index.length != 0) {
if (word === searchText) {
searchTextCountInSlice++;
}
hits.push({position: position, length: word.length});
var wordEnd = position + word.length;
// move to next position of hit
index.pop();
while (index.length != 0) {
item = index[index.length - 1];
position = item.position;
word = item.word;
if (wordEnd > position) {
index.pop();
} else {
break;
}
}
}
searchTextCount += searchTextCountInSlice;
return {
hits: hits,
start: start,
end: end,
searchTextCount: searchTextCountInSlice
};
}
var slicesOfTitle = [];
if (indexOfTitle.length != 0) {
slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
}
var slicesOfContent = [];
while (indexOfContent.length != 0) {
var item = indexOfContent[indexOfContent.length - 1];
var position = item.position;
var word = item.word;
// cut out 100 characters
var start = position - 20;
var end = position + 80;
if(start < 0){
start = 0;
}
if (end < position + word.length) {
end = position + word.length;
}
if(end > content.length){
end = content.length;
}
slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
}
// sort slices in content by search text's count and hits' count
slicesOfContent.sort(function (sliceLeft, sliceRight) {
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
return sliceRight.hits.length - sliceLeft.hits.length;
} else {
return sliceLeft.start - sliceRight.start;
}
});
// select top N slices in content
var upperBound = parseInt('1');
if (upperBound >= 0) {
slicesOfContent = slicesOfContent.slice(0, upperBound);
}
// highlight title and content
function highlightKeyword(text, slice) {
var result = '';
var prevEnd = slice.start;
slice.hits.forEach(function (hit) {
result += text.substring(prevEnd, hit.position);
var end = hit.position + hit.length;
result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
prevEnd = end;
});
result += text.substring(prevEnd, slice.end);
return result;
}
var resultItem = '';
if (slicesOfTitle.length != 0) {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
} else {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
}
slicesOfContent.forEach(function (slice) {
resultItem += "<a href='" + articleUrl + "'>" +
"<p class=\"search-result\">" + highlightKeyword(content, slice) +
"...</p>" + "</a>";
});
resultItem += "</li>";
resultItems.push({
item: resultItem,
searchTextCount: searchTextCount,
hitCount: hitCount,
id: resultItems.length
});
}
})
};
if (keywords.length === 1 && keywords[0] === "") {
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
} else if (resultItems.length === 0) {
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
} else {
resultItems.sort(function (resultLeft, resultRight) {
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
return resultRight.searchTextCount - resultLeft.searchTextCount;
} else if (resultLeft.hitCount !== resultRight.hitCount) {
return resultRight.hitCount - resultLeft.hitCount;
} else {
return resultRight.id - resultLeft.id;
}
});
var searchResultList = '<ul class=\"search-result-list\">';
resultItems.forEach(function (result) {
searchResultList += result.item;
})
searchResultList += "</ul>";
resultContent.innerHTML = searchResultList;
}
}
if ('auto' === 'auto') {
input.addEventListener('input', inputEventFunction);
} else {
$('.search-icon').click(inputEventFunction);
input.addEventListener('keypress', function (event) {
if (event.keyCode === 13) {
inputEventFunction();
}
});
}
// remove loading animation
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
proceedsearch();
}
});
}
// handle and trigger popup window;
$('.popup-trigger').click(function(e) {
e.stopPropagation();
if (isfetched === false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});
$('.popup-btn-close').click(onPopupClose);
$('.popup').click(function(e){
e.stopPropagation();
});
$(document).on('keyup', function (event) {
var shouldDismissSearchPopup = event.which === 27 &&
$('.search-popup').is(':visible');
if (shouldDismissSearchPopup) {
onPopupClose();
}
});
</script>
<script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
<script>AV.initialize("EWwoJgHNdlj6iBjiFlMcabUO-gzGzoHsz", "x8FxDrYG79C8YFrTww9ljo8K");</script>
<script>
function showTime(Counter) {
var query = new AV.Query(Counter);
var entries = [];
var $visitors = $(".leancloud_visitors");
$visitors.each(function () {
entries.push( $(this).attr("id").trim() );
});
query.containedIn('url', entries);
query.find()
.done(function (results) {
var COUNT_CONTAINER_REF = '.leancloud-visitors-count';
if (results.length === 0) {
$visitors.find(COUNT_CONTAINER_REF).text(0);
return;
}
for (var i = 0; i < results.length; i++) {
var item = results[i];
var url = item.get('url');
var time = item.get('time');
var element = document.getElementById(url);
$(element).find(COUNT_CONTAINER_REF).text(time);
}
for(var i = 0; i < entries.length; i++) {
var url = entries[i];
var element = document.getElementById(url);
var countSpan = $(element).find(COUNT_CONTAINER_REF);
if( countSpan.text() == '') {
countSpan.text(0);
}
}
})
.fail(function (object, error) {
console.log("Error: " + error.code + " " + error.message);
});
}
function addCount(Counter) {
var $visitors = $(".leancloud_visitors");
var url = $visitors.attr('id').trim();
var title = $visitors.attr('data-flag-title').trim();
var query = new AV.Query(Counter);
query.equalTo("url", url);
query.find({
success: function(results) {
if (results.length > 0) {
var counter = results[0];
counter.fetchWhenSave(true);
counter.increment("time");
counter.save(null, {
success: function(counter) {
var $element = $(document.getElementById(url));
$element.find('.leancloud-visitors-count').text(counter.get('time'));
},
error: function(counter, error) {
console.log('Failed to save Visitor num, with error message: ' + error.message);
}
});
} else {
var newcounter = new Counter();
/* Set ACL */
var acl = new AV.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
newcounter.setACL(acl);
/* End Set ACL */
newcounter.set("title", title);
newcounter.set("url", url);
newcounter.set("time", 1);
newcounter.save(null, {
success: function(newcounter) {
var $element = $(document.getElementById(url));
$element.find('.leancloud-visitors-count').text(newcounter.get('time'));
},
error: function(newcounter, error) {
console.log('Failed to create');
}
});
}
},
error: function(error) {
console.log('Error:' + error.code + " " + error.message);
}
});
}
$(function() {
var Counter = AV.Object.extend("Counter");
if ($('.leancloud_visitors').length == 1) {
addCount(Counter);
} else if ($('.post-title-link').length > 1) {
showTime(Counter);
}
});
</script>
<script>
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>
</body>
</html>